From c36cf6b9372a58563a3e6726c844f0fc56ed0ede Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Sun, 6 Nov 2022 19:30:12 +0100 Subject: [PATCH] Make injection order predictable (#402) --- .../injection/ParameterInjectorRegistry.java | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/cloud-core/src/main/java/cloud/commandframework/annotations/injection/ParameterInjectorRegistry.java b/cloud-core/src/main/java/cloud/commandframework/annotations/injection/ParameterInjectorRegistry.java index eb1dab60..303e1afc 100644 --- a/cloud-core/src/main/java/cloud/commandframework/annotations/injection/ParameterInjectorRegistry.java +++ b/cloud-core/src/main/java/cloud/commandframework/annotations/injection/ParameterInjectorRegistry.java @@ -26,16 +26,16 @@ package cloud.commandframework.annotations.injection; import cloud.commandframework.annotations.AnnotationAccessor; import cloud.commandframework.context.CommandContext; import cloud.commandframework.services.ServicePipeline; +import cloud.commandframework.types.tuples.Pair; import cloud.commandframework.types.tuples.Triplet; import io.leangen.geantyref.TypeToken; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; import org.apiguardian.api.API; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -43,6 +43,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** * Registry containing mappings between {@link Class classes} and {@link ParameterInjector injectors} * + * The order injectors are tested is the same order they were registered in. + * * @param Command sender type * @since 1.2.0 */ @@ -50,8 +52,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; @API(status = API.Status.STABLE, since = "1.2.0") public final class ParameterInjectorRegistry implements InjectionService { - private volatile int injectorCount = 0; - private final Map, List>> injectors = new HashMap<>(); + private final List>, ParameterInjector>> injectors = new ArrayList<>(); private final ServicePipeline servicePipeline = ServicePipeline.builder().build(); /** @@ -63,7 +64,7 @@ public final class ParameterInjectorRegistry implements InjectionService { } /** - * Register an injector for a particular type + * Register an injector for a particular type or any of it's assignable supertypes. * * @param clazz Type that the injector should inject for. This type will matched using * {@link Class#isAssignableFrom(Class)} @@ -74,8 +75,23 @@ public final class ParameterInjectorRegistry implements InjectionService { final @NonNull Class clazz, final @NonNull ParameterInjector injector ) { - this.injectors.computeIfAbsent(clazz, missingClass -> new LinkedList<>()).add(injector); - this.injectorCount++; + this.registerInjector(cl -> clazz.isAssignableFrom(cl), injector); + } + + /** + * Register an injector for a particular type predicate. + * + * @param predicate A predicate that matches if the injector should be used for a type + * @param injector The injector that should inject the value into the command method + * @param Injected type + * @since 1.8.0 + */ + @API(status = API.Status.STABLE, since = "1.8.0") + public synchronized void registerInjector( + final @NonNull Predicate> predicate, + final @NonNull ParameterInjector injector + ) { + this.injectors.add(Pair.of(predicate, injector)); } /** @@ -92,13 +108,10 @@ public final class ParameterInjectorRegistry implements InjectionService { public synchronized @NonNull Collection<@NonNull ParameterInjector> injectors( final @NonNull Class clazz ) { - final List<@NonNull ParameterInjector> injectors = new ArrayList<>(this.injectorCount); - for (final Map.Entry, List>> entry : this.injectors.entrySet()) { - if (clazz.isAssignableFrom(entry.getKey())) { - injectors.addAll(entry.getValue()); - } - } - return Collections.unmodifiableCollection(injectors); + return Collections.unmodifiableCollection(this.injectors.stream() + .filter(pair -> pair.getFirst().test(clazz)) + .map(Pair::getSecond) + .collect(Collectors.toList())); } @Override