Make injection order predictable (#402)
This commit is contained in:
parent
2f46b0c71d
commit
c36cf6b937
1 changed files with 28 additions and 15 deletions
|
|
@ -26,16 +26,16 @@ package cloud.commandframework.annotations.injection;
|
||||||
import cloud.commandframework.annotations.AnnotationAccessor;
|
import cloud.commandframework.annotations.AnnotationAccessor;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.services.ServicePipeline;
|
import cloud.commandframework.services.ServicePipeline;
|
||||||
|
import cloud.commandframework.types.tuples.Pair;
|
||||||
import cloud.commandframework.types.tuples.Triplet;
|
import cloud.commandframework.types.tuples.Triplet;
|
||||||
import io.leangen.geantyref.TypeToken;
|
import io.leangen.geantyref.TypeToken;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.apiguardian.api.API;
|
import org.apiguardian.api.API;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
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}
|
* 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 <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @since 1.2.0
|
* @since 1.2.0
|
||||||
*/
|
*/
|
||||||
|
|
@ -50,8 +52,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
@API(status = API.Status.STABLE, since = "1.2.0")
|
@API(status = API.Status.STABLE, since = "1.2.0")
|
||||||
public final class ParameterInjectorRegistry<C> implements InjectionService<C> {
|
public final class ParameterInjectorRegistry<C> implements InjectionService<C> {
|
||||||
|
|
||||||
private volatile int injectorCount = 0;
|
private final List<Pair<Predicate<Class<?>>, ParameterInjector<C, ?>>> injectors = new ArrayList<>();
|
||||||
private final Map<Class<?>, List<ParameterInjector<C, ?>>> injectors = new HashMap<>();
|
|
||||||
private final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
private final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,7 +64,7 @@ public final class ParameterInjectorRegistry<C> implements InjectionService<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @param clazz Type that the injector should inject for. This type will matched using
|
||||||
* {@link Class#isAssignableFrom(Class)}
|
* {@link Class#isAssignableFrom(Class)}
|
||||||
|
|
@ -74,8 +75,23 @@ public final class ParameterInjectorRegistry<C> implements InjectionService<C> {
|
||||||
final @NonNull Class<T> clazz,
|
final @NonNull Class<T> clazz,
|
||||||
final @NonNull ParameterInjector<C, T> injector
|
final @NonNull ParameterInjector<C, T> injector
|
||||||
) {
|
) {
|
||||||
this.injectors.computeIfAbsent(clazz, missingClass -> new LinkedList<>()).add(injector);
|
this.registerInjector(cl -> clazz.isAssignableFrom(cl), injector);
|
||||||
this.injectorCount++;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 <T> Injected type
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public synchronized <T> void registerInjector(
|
||||||
|
final @NonNull Predicate<Class<?>> predicate,
|
||||||
|
final @NonNull ParameterInjector<C, T> injector
|
||||||
|
) {
|
||||||
|
this.injectors.add(Pair.of(predicate, injector));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -92,13 +108,10 @@ public final class ParameterInjectorRegistry<C> implements InjectionService<C> {
|
||||||
public synchronized <T> @NonNull Collection<@NonNull ParameterInjector<C, ?>> injectors(
|
public synchronized <T> @NonNull Collection<@NonNull ParameterInjector<C, ?>> injectors(
|
||||||
final @NonNull Class<T> clazz
|
final @NonNull Class<T> clazz
|
||||||
) {
|
) {
|
||||||
final List<@NonNull ParameterInjector<C, ?>> injectors = new ArrayList<>(this.injectorCount);
|
return Collections.unmodifiableCollection(this.injectors.stream()
|
||||||
for (final Map.Entry<Class<?>, List<ParameterInjector<C, ?>>> entry : this.injectors.entrySet()) {
|
.filter(pair -> pair.getFirst().test(clazz))
|
||||||
if (clazz.isAssignableFrom(entry.getKey())) {
|
.map(Pair::getSecond)
|
||||||
injectors.addAll(entry.getValue());
|
.collect(Collectors.toList()));
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableCollection(injectors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue