From 4ea68bc6f255397b6cf36984c1e112e9ba8bc849 Mon Sep 17 00:00:00 2001 From: Norbert Dejlich Date: Fri, 18 Oct 2024 20:44:46 +0200 Subject: [PATCH] GH-444 Improve parsers caches (#451) * Parser caching. * Improve performance of Argument#hashcode * Remove ConcurrentHashMap from BiHashMap. Add unit tests for async arguments initialize. * Fix BlockingArgumentResolver * Fix javadocs. --- .../argument/KeyAnnotationResolver.java | 14 +++- .../profile/ProfileAnnotationProcessor.java | 9 ++- .../async/AsyncArgumentsInitializeTest.java | 69 +++++++++++++++++ .../litecommands/chatgpt/ChatGptArgument.java | 6 +- .../litecommands/argument/Argument.java | 21 ++++-- .../argument/MutableArgument.java | 16 ++++ .../litecommands/argument/SimpleArgument.java | 75 ++++++++++++++----- .../argument/parser/ParserRegistry.java | 2 + .../argument/parser/ParserRegistryImpl.java | 28 +++++-- .../profile/ArgumentProfileNamespace.java | 15 ++++ .../AbstractCollectorArgumentResolver.java | 6 +- .../CompletableFutureResolver.java | 10 +-- .../optional/OptionalArgumentResolver.java | 2 +- .../TemporalAccessorArgumentResolver.java | 3 +- .../litecommands/input/raw/RawCommand.java | 18 ++--- .../dev/rollczi/litecommands/meta/Meta.java | 1 + .../litecommands/reflect/type/TypeToken.java | 14 ++++ .../ScheduledRequirementResolver.java | 12 +-- .../schematic/SchematicFastGenerator.java | 3 +- .../schematic/SimpleSchematicGenerator.java | 3 +- .../litecommands/shared/BiHashMap.java | 3 +- .../CompletableFutureArgumentTest.java | 4 +- litecommands-unit/src/LiteBenchmark.java | 8 +- .../blocking/BlockingArgumentResolver.java | 2 +- 24 files changed, 256 insertions(+), 88 deletions(-) create mode 100644 litecommands-annotations/test/dev/rollczi/litecommands/annotations/async/AsyncArgumentsInitializeTest.java create mode 100644 litecommands-core/src/dev/rollczi/litecommands/argument/MutableArgument.java diff --git a/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/KeyAnnotationResolver.java b/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/KeyAnnotationResolver.java index 1684292b5..bb1ff2189 100644 --- a/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/KeyAnnotationResolver.java +++ b/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/KeyAnnotationResolver.java @@ -3,14 +3,22 @@ import dev.rollczi.litecommands.annotations.AnnotationInvoker; import dev.rollczi.litecommands.annotations.AnnotationProcessor; -import dev.rollczi.litecommands.meta.Meta; +import dev.rollczi.litecommands.argument.ArgumentKey; +import dev.rollczi.litecommands.argument.MutableArgument; public class KeyAnnotationResolver implements AnnotationProcessor { @Override public AnnotationInvoker process(AnnotationInvoker invoker) { - return invoker.on(Key.class, (annotation, metaHolder) -> { - metaHolder.meta().edit(Meta.ARGUMENT_KEY, argumentKey -> argumentKey.withKey(annotation.value())); + return invoker.onParameterRequirement(Key.class, (parameter, key, builder, requirement) -> { + if (!(requirement instanceof MutableArgument)) { + throw new IllegalArgumentException("@Key annotation can be used only on arguments: " + requirement.getClass().getName()); + } + + MutableArgument argument = (MutableArgument) requirement; + ArgumentKey argumentKey = argument.getKey(); + + argument.setKey(argumentKey.withKey(key.value())); }); } diff --git a/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/profile/ProfileAnnotationProcessor.java b/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/profile/ProfileAnnotationProcessor.java index 2f623297b..0f5a48601 100644 --- a/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/profile/ProfileAnnotationProcessor.java +++ b/litecommands-annotations/src/dev/rollczi/litecommands/annotations/argument/profile/ProfileAnnotationProcessor.java @@ -4,6 +4,7 @@ import dev.rollczi.litecommands.annotations.AnnotationInvoker; import dev.rollczi.litecommands.annotations.AnnotationProcessor; import dev.rollczi.litecommands.argument.Argument; +import dev.rollczi.litecommands.argument.MutableArgument; import dev.rollczi.litecommands.argument.profile.ArgumentProfile; import dev.rollczi.litecommands.reflect.LiteCommandsReflectInvocationException; import java.lang.annotation.Annotation; @@ -21,19 +22,19 @@ protected ProfileAnnotationProcessor(Class annotationType) { @Override public AnnotationInvoker process(AnnotationInvoker invoker) { return invoker.onParameterRequirement(annotationType, (parameter, annotation, builder, requirement) -> { - if (!(requirement instanceof Argument)) { + if (!(requirement instanceof MutableArgument)) { Executable declaringExecutable = parameter.getDeclaringExecutable(); - throw new LiteCommandsReflectInvocationException(declaringExecutable, parameter, "@" + annotationType.getSimpleName() + " can be used only on arguments"); + throw new LiteCommandsReflectInvocationException(declaringExecutable, parameter, "@" + annotationType.getSimpleName() + " can be used only on arguments: " + requirement.getClass().getName()); } - Argument argument = (Argument) requirement; + MutableArgument argument = (MutableArgument) requirement; PROFILE profile = createProfile(parameter, annotation, argument); if (profile == null) { return; } - argument.withProfile(profile); + argument.addProfile(profile); }); } diff --git a/litecommands-annotations/test/dev/rollczi/litecommands/annotations/async/AsyncArgumentsInitializeTest.java b/litecommands-annotations/test/dev/rollczi/litecommands/annotations/async/AsyncArgumentsInitializeTest.java new file mode 100644 index 000000000..2b93930db --- /dev/null +++ b/litecommands-annotations/test/dev/rollczi/litecommands/annotations/async/AsyncArgumentsInitializeTest.java @@ -0,0 +1,69 @@ +package dev.rollczi.litecommands.annotations.async; + +import dev.rollczi.litecommands.argument.Argument; +import dev.rollczi.litecommands.programmatic.LiteCommand; +import static dev.rollczi.litecommands.programmatic.LiteProgrammatic.async; +import dev.rollczi.litecommands.scheduler.SchedulerExecutorPoolImpl; +import dev.rollczi.litecommands.unit.AssertExecute; +import dev.rollczi.litecommands.unit.LiteCommandsTestFactory; +import dev.rollczi.litecommands.unit.TestPlatform; +import dev.rollczi.litecommands.unit.TestSender; +import dev.rollczi.litecommands.util.FutureUtil; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Period; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AsyncArgumentsInitializeTest { + + private final static List> ARGUMENTS_TYPES = Arrays.asList( + String.class, + Integer.class, + Long.class, + Double.class, + Float.class, + Byte.class, + Short.class, + Boolean.class, + Duration.class, + Period.class, + Instant.class, + LocalDateTime.class + ); + + @Test + @DisplayName("Test for async arguments parsers initialization, should not throw any exceptions while running") + void test() { + LiteCommand command = new LiteCommand<>("test"); + + for (Class type : ARGUMENTS_TYPES) { + command.argument(async(Argument.of(type.getSimpleName(), type))); + } + + List> futures = new ArrayList<>(); + for (int i = 0; i < 50; i++) { + TestPlatform platform = LiteCommandsTestFactory.startPlatform(config -> config + .scheduler(new SchedulerExecutorPoolImpl("lite-commands", 10)) + .commands(command) + ); + + for (int j = 0; j < 10; j++) { + futures.add(platform.executeAsync("test test 1 100 1.0 1.0f 1 1 true 1s 1d 2021-01-01 00:00:00 2021-01-01 00:00:00")); + } + } + + List executes = FutureUtil.asList(futures).join(); + + assertThat(executes) + .hasSize(500) + .allSatisfy(assertExecute -> assertExecute.assertSuccess()); + } + +} diff --git a/litecommands-chatgpt/src/dev/rollczi/litecommands/chatgpt/ChatGptArgument.java b/litecommands-chatgpt/src/dev/rollczi/litecommands/chatgpt/ChatGptArgument.java index db819a2f9..3c4789caa 100644 --- a/litecommands-chatgpt/src/dev/rollczi/litecommands/chatgpt/ChatGptArgument.java +++ b/litecommands-chatgpt/src/dev/rollczi/litecommands/chatgpt/ChatGptArgument.java @@ -7,9 +7,9 @@ public class ChatGptArgument extends SimpleArgument { public ChatGptArgument(String name, String topic) { - super(name, TypeToken.of(String.class), false); - this.withProfile(new ChatGptArgumentProfile(topic)); - this.withProfile(new JoinProfile()); + super(name, TypeToken.of(String.class)); + this.addProfile(new ChatGptArgumentProfile(topic)); + this.addProfile(new JoinProfile()); } public ChatGptArgument(String name) { diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java b/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java index a9fad6864..8b46a67bc 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java @@ -3,6 +3,7 @@ import dev.rollczi.litecommands.argument.profile.ArgumentProfile; import dev.rollczi.litecommands.argument.parser.ParseResult; import dev.rollczi.litecommands.argument.profile.ArgumentProfileNamespace; +import dev.rollczi.litecommands.priority.PrioritizedList; import dev.rollczi.litecommands.reflect.type.TypeToken; import dev.rollczi.litecommands.requirement.Requirement; @@ -25,26 +26,32 @@ default Optional> defaultValue() { boolean hasDefaultValue(); @ApiStatus.Experimental -

> Argument withProfile(P profile); +

Optional

getProfile(ArgumentProfileNamespace

key); @ApiStatus.Experimental -

Optional

getProfile(ArgumentProfileNamespace

key); + PrioritizedList> getProfiles(); + /** + * Create a child of the current argument. + * This is useful when resolver handles a parametrized type for class such as Optional, List, CompletableFuture, etc. + */ @ApiStatus.Experimental - Argument withType(TypeToken type); + Argument child(TypeToken type); static Argument of(String name, Class type) { - return new SimpleArgument<>(name, TypeToken.of(type), false); + return new SimpleArgument<>(name, TypeToken.of(type)); } + @Deprecated static Argument of(String name, Class type, boolean nullable) { return new SimpleArgument<>(name, TypeToken.of(type), nullable); } static Argument of(String name, TypeToken type) { - return new SimpleArgument<>(name, type, false); + return new SimpleArgument<>(name, type); } + @Deprecated static Argument of(String name, TypeToken type, boolean nullable) { return new SimpleArgument<>(name, type, nullable); } @@ -56,8 +63,8 @@ static > Argument profiled(String name, Class @ApiStatus.Experimental static > Argument profiled(String name, TypeToken type, P profile) { - return new SimpleArgument<>(name, type, false) - .withProfile(profile); + return new SimpleArgument<>(name, type) + .addProfile(profile); } } \ No newline at end of file diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/MutableArgument.java b/litecommands-core/src/dev/rollczi/litecommands/argument/MutableArgument.java new file mode 100644 index 000000000..5ad9b7fe3 --- /dev/null +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/MutableArgument.java @@ -0,0 +1,16 @@ +package dev.rollczi.litecommands.argument; + +import dev.rollczi.litecommands.argument.profile.ArgumentProfile; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public interface MutableArgument extends Argument { + + @ApiStatus.Experimental + @ApiStatus.Internal +

> Argument addProfile(P profile); + + @ApiStatus.Internal + void setKey(ArgumentKey key); + +} diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java b/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java index f3fa43f9c..18a73a0aa 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java @@ -1,39 +1,49 @@ package dev.rollczi.litecommands.argument; +import dev.rollczi.litecommands.argument.parser.ParseResult; import dev.rollczi.litecommands.argument.profile.ArgumentProfile; import dev.rollczi.litecommands.argument.profile.ArgumentProfileNamespace; -import dev.rollczi.litecommands.argument.parser.ParseResult; import dev.rollczi.litecommands.meta.Meta; import dev.rollczi.litecommands.meta.MetaHolder; import dev.rollczi.litecommands.priority.MutablePrioritizedList; -import dev.rollczi.litecommands.priority.Prioritized; +import dev.rollczi.litecommands.priority.PrioritizedList; import dev.rollczi.litecommands.reflect.type.TypeToken; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import java.util.Objects; import java.util.Optional; -public class SimpleArgument implements Argument { +public class SimpleArgument implements MutableArgument { private final String name; private final TypeToken type; private final Meta meta = Meta.create(); + + private ArgumentKey key; + private volatile int hashCode; + + private @Nullable Argument pattern; + + private final MutablePrioritizedList> profiles = new MutablePrioritizedList<>(); + @Deprecated private final boolean nullable; - private final MutablePrioritizedList> profiles = new MutablePrioritizedList<>(); - public SimpleArgument(String name, TypeToken type, @Deprecated boolean nullable) { - this.name = name; - this.type = type; - this.nullable = nullable; - this.meta.put(Meta.ARGUMENT_KEY, ArgumentKey.of(this.getClass().getName(), name)); + protected SimpleArgument(String name, TypeToken type, @Nullable Argument pattern) { + this(name, type); + this.pattern = pattern; } public SimpleArgument(String name, TypeToken type) { + this(name, type, false); + } + + public SimpleArgument(String name, TypeToken type, @Deprecated boolean nullable) { this.name = name; this.type = type; - this.nullable = false; - this.meta.put(Meta.ARGUMENT_KEY, ArgumentKey.of(this.getClass().getName(), name)); + this.nullable = nullable; + this.setKey(ArgumentKey.of(this.getClass().getName(), name)); } @Override @@ -43,7 +53,13 @@ public String getName() { @Override public ArgumentKey getKey() { - return meta.get(Meta.ARGUMENT_KEY); + return key; + } + + @Override + public void setKey(ArgumentKey key) { + this.key = key; + this.hashCode = createHashCode(); } @Override @@ -58,7 +74,7 @@ public Meta meta() { @Override public @Nullable MetaHolder parentMeta() { - return null; + return pattern; } @Override @@ -76,15 +92,14 @@ public boolean hasDefaultValue() { } @ApiStatus.Experimental - public

> SimpleArgument withProfile(P profile) { + public

> SimpleArgument addProfile(P profile) { ArgumentProfileNamespace

namespace = profile.getNamespace(); this.profiles.add(profile); this.meta.put(namespace.asMetaKey(), profile); if (this.profiles.first().equals(profile)) { - this.meta.edit(Meta.ARGUMENT_KEY, argumentKey -> argumentKey.withNamespace(namespace.getNamespace())); - + this.setKey(this.key.withNamespace(namespace.getNamespace())); } return this; } @@ -96,10 +111,30 @@ public

Optional

getProfile(ArgumentProfileNamespace

key) { } @Override - public Argument withType(TypeToken type) { - Argument argument = Argument.of(name, type, nullable); - argument.meta().putAll(meta); - return argument; + public PrioritizedList> getProfiles() { + return profiles; + } + + @Override + public Argument child(TypeToken type) { + return new SimpleArgument<>(name, type, this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SimpleArgument that = (SimpleArgument) o; + return Objects.equals(getKey(), that.getKey()) && Objects.equals(type, that.type); + } + + @Override + public int hashCode() { + return hashCode; + } + + private int createHashCode() { + return Objects.hash(getKey(), type); } } diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistry.java b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistry.java index bc189b875..cf11e4164 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistry.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistry.java @@ -4,6 +4,7 @@ import dev.rollczi.litecommands.argument.ArgumentKey; import dev.rollczi.litecommands.invocation.Invocation; import dev.rollczi.litecommands.reflect.type.TypeRange; +import org.jetbrains.annotations.ApiStatus; public interface ParserRegistry { @@ -16,6 +17,7 @@ default void registerParser(Class parserType, ArgumentKey key, void registerParser(TypeRange typeRange, ArgumentKey key, ParserChained parser); + @ApiStatus.Internal ParserSet getParserSet(Class parsedClass, ArgumentKey key); @Deprecated diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistryImpl.java b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistryImpl.java index 29537e078..d45f6aa58 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistryImpl.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParserRegistryImpl.java @@ -7,12 +7,15 @@ import dev.rollczi.litecommands.reflect.type.TypeRange; import dev.rollczi.litecommands.reflect.type.TypeIndex; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; + import org.jetbrains.annotations.NotNull; public class ParserRegistryImpl implements ParserRegistry, ParserChainAccessor { - // TODO Add caches for parsers + private final Map, Parser> cachedParsers = new HashMap<>(); private final TypeIndex> typeIndex = new TypeIndex<>(); @Override @@ -60,17 +63,32 @@ public ParserSet getParserSet(Class parserType, ArgumentKey ke @Override public Parser getParser(Argument argument) { Class argumentType = argument.getType().getRawType(); - ParserSet parserSet = getParserSet(argumentType, argument.getKey()); + Parser senderParser = (Parser) cachedParsers.get(argument); + + if (senderParser == null) { + ParserSet parserSet = getParserSet(argumentType, argument.getKey()); + senderParser = parserSet.getValidParserOrThrow(argument); + cachedParsers.put(argument, senderParser); + } - return parserSet.getValidParserOrThrow(argument); + return senderParser; } @Override public Parser getParserOrNull(Argument argument) { Class argumentType = argument.getType().getRawType(); - ParserSet parserSet = getParserSet(argumentType, argument.getKey()); + Parser senderParser = (Parser) cachedParsers.get(argument); + + if (senderParser == null) { + ParserSet parserSet = getParserSet(argumentType, argument.getKey()); + senderParser = parserSet.getValidParser(argument); + + if (senderParser != null) { + cachedParsers.put(argument, senderParser); + } + } - return parserSet.getValidParser(argument); + return senderParser; } @Override diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/profile/ArgumentProfileNamespace.java b/litecommands-core/src/dev/rollczi/litecommands/argument/profile/ArgumentProfileNamespace.java index 7c8f9ebdc..1ea63181d 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/profile/ArgumentProfileNamespace.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/profile/ArgumentProfileNamespace.java @@ -4,6 +4,8 @@ import dev.rollczi.litecommands.meta.MetaKey; import org.jetbrains.annotations.ApiStatus; +import java.util.Objects; + @ApiStatus.Experimental public class ArgumentProfileNamespace implements ArgumentKey { @@ -42,6 +44,19 @@ public ArgumentKey withNamespace(String namespace) { return ArgumentKey.of(namespace, getKey()); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ArgumentProfileNamespace that = (ArgumentProfileNamespace) o; + return Objects.equals(argumentNamespace, that.argumentNamespace) && Objects.equals(argumentKey, that.argumentKey); + } + + @Override + public int hashCode() { + return Objects.hash(argumentNamespace, argumentKey); + } + @ApiStatus.Experimental public static ArgumentProfileNamespace of(MetaKey metaKey) { return of(metaKey, ArgumentKey.DEFAULT_KEY); diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/collector/AbstractCollectorArgumentResolver.java b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/collector/AbstractCollectorArgumentResolver.java index d5f375cf9..ea929e1fa 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/collector/AbstractCollectorArgumentResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/collector/AbstractCollectorArgumentResolver.java @@ -78,7 +78,7 @@ private boolean match0(Invocation invocation, Argument c } TypeToken elementType = this.getElementType(collectionArgumentContainer); - Argument argument = collectionArgument.withType(elementType); + Argument argument = collectionArgument.child(elementType); Parser parser = parserRegistry.getParser(argument); Range range = parser.getRange(argument); @@ -100,7 +100,7 @@ private boolean match0(Invocation invocation, Argument c } private ParseResult> parseToList(TypeToken componentType, RawInput rawInput, Argument collectionArgument, VarargsProfile collectorArgumentHolder, Invocation invocation) { - Argument argument = collectionArgument.withType(componentType); + Argument argument = collectionArgument.child(componentType); Parser parser = parserRegistry.getParser(argument); @@ -214,7 +214,7 @@ public SuggestionResult suggest(Invocation invocation, Argument SuggestionResult suggest(TypeToken componentType, SuggestionContext context, Argument collectionArgument, VarargsProfile varargsProfile, Invocation invocation) { - Argument argument = collectionArgument.withType(componentType); + Argument argument = collectionArgument.child(componentType); Parser parser = parserRegistry.getParser(argument); Suggester suggester = suggesterRegistry.getSuggester(componentType.getRawType(), argument.getKey()); diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureResolver.java b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureResolver.java index c6a9dab9d..8c912b0c2 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureResolver.java @@ -32,9 +32,9 @@ public boolean canParse(Argument argument) { } private boolean canParse(Argument argument, TypeToken type) { - Parser parser = parserRegistry.getParser(argument.withType(type)); + Parser parser = parserRegistry.getParser(argument.child(type)); - return parser.canParse(argument.withType(type)); + return parser.canParse(argument.child(type)); } @Override @@ -43,7 +43,7 @@ protected ParseResult parse(Invocation invocation, Ar } private ParseResult parse(Invocation invocation, Argument argument, TypeToken type, String input, ParserChainAccessor chainAccessor) { - CompletableFuture> supply = scheduler.supply(SchedulerPoll.COMPLETABLE_FUTURE, () -> chainAccessor.parse(invocation, argument.withType(type), input)); + CompletableFuture> supply = scheduler.supply(SchedulerPoll.COMPLETABLE_FUTURE, () -> chainAccessor.parse(invocation, argument.child(type), input)); return ParseResult.completableFuture(supply) .map(future -> CompletableFuture.completedFuture(future)); @@ -55,9 +55,9 @@ protected boolean match(Invocation invocation, Argument boolean match(Invocation invocation, Argument context, String argument, ParserChainAccessor accessor, TypeToken type) { - Parser parser = parserRegistry.getParser(context.withType(type)); + Parser parser = parserRegistry.getParser(context.child(type)); - return parser.match(invocation, context.withType(type), RawInput.of(argument)); + return parser.match(invocation, context.child(type), RawInput.of(argument)); } @Override diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/optional/OptionalArgumentResolver.java b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/optional/OptionalArgumentResolver.java index 437b1b1b4..c2eef13dd 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/optional/OptionalArgumentResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/optional/OptionalArgumentResolver.java @@ -28,7 +28,7 @@ public ParseResult parse(Invocation invocation, Argument ParseResult parseValue(TypeToken type, Invocation invocation, Argument optionalArgument, RawInput input, ParserChainAccessor chainAccessor) { Argument argument = Argument.of(optionalArgument.getName(), type); - ParseResult parseResult = chainAccessor.parse(invocation, argument.withType(type), input); + ParseResult parseResult = chainAccessor.parse(invocation, argument.child(type), input); return parseResult .map(value -> Optional.of(value)) diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/standard/TemporalAccessorArgumentResolver.java b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/standard/TemporalAccessorArgumentResolver.java index d29e58b07..50f86a78d 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/standard/TemporalAccessorArgumentResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/standard/TemporalAccessorArgumentResolver.java @@ -15,6 +15,7 @@ import dev.rollczi.litecommands.suggestion.Suggestion; import dev.rollczi.litecommands.suggestion.SuggestionContext; import dev.rollczi.litecommands.suggestion.SuggestionResult; +import dev.rollczi.litecommands.util.StringUtil; import java.time.DateTimeException; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; @@ -107,6 +108,6 @@ public Range getRange(Argument unitArgument) { } private int getElementCount(DateTimeFormatter formatter) { - return formatter.toString().split(FORMATTER_ELEMENT_SEPARATOR).length; + return StringUtil.splitBySpace(formatter.toString()).size(); } } diff --git a/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawCommand.java b/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawCommand.java index 888ed85f3..7988e08de 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawCommand.java +++ b/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawCommand.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class RawCommand { @@ -47,19 +48,12 @@ public static RawCommand from(String rawInput) { ? rawInput.substring(COMMAND_SLASH.length()) : rawInput; - String[] rawCommandParts = rawCommand.split(COMMAND_SEPARATOR); - String commandLabel = rawCommandParts[0]; + List rawCommandParts = StringUtil.splitBySpace(rawCommand); + String commandLabel = rawCommandParts.get(0); - List commandArgs = new ArrayList<>(); - - if (rawCommandParts.length > 1) { - commandArgs.addAll(Arrays.asList(rawCommandParts).subList(1, rawCommandParts.length)); - } - - if (rawCommand.endsWith(COMMAND_SEPARATOR)) { - commandArgs = new ArrayList<>(commandArgs); - commandArgs.add(StringUtil.EMPTY); - } + List commandArgs = rawCommandParts.size() > 1 + ? rawCommandParts.subList(1, rawCommandParts.size()) + : Collections.emptyList(); return new RawCommand(commandLabel, commandArgs); } diff --git a/litecommands-core/src/dev/rollczi/litecommands/meta/Meta.java b/litecommands-core/src/dev/rollczi/litecommands/meta/Meta.java index e49157f00..cc77a5ab2 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/meta/Meta.java +++ b/litecommands-core/src/dev/rollczi/litecommands/meta/Meta.java @@ -25,6 +25,7 @@ public interface Meta { MetaKey PRIORITY = MetaKey.of("priority", PriorityLevel.class, PriorityLevel.NORMAL); MetaKey NATIVE_PERMISSIONS = MetaKey.of("native-permissions", Boolean.class, false); MetaKey POLL_TYPE = MetaKey.of("poll-type", SchedulerPoll.class, SchedulerPoll.MAIN); + @Deprecated MetaKey ARGUMENT_KEY = MetaKey.of("argument-key", ArgumentKey.class, ArgumentKey.of()); MetaKey> COMMAND_ORIGIN_TYPE = MetaKey.of("command-origin-class", MetaType.list(), Collections.emptyList()); MetaKey>>> VALIDATORS = MetaKey.of("validators", MetaType.list(), Collections.emptyList()); diff --git a/litecommands-core/src/dev/rollczi/litecommands/reflect/type/TypeToken.java b/litecommands-core/src/dev/rollczi/litecommands/reflect/type/TypeToken.java index c67098d2d..b5640d8f2 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/reflect/type/TypeToken.java +++ b/litecommands-core/src/dev/rollczi/litecommands/reflect/type/TypeToken.java @@ -18,6 +18,7 @@ import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Objects; public abstract class TypeToken { @@ -92,6 +93,19 @@ public TypeToken getComponentTypeToken() { return of(getComponentType()); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TypeToken typeToken = (TypeToken) o; + return Objects.equals(type, typeToken.type); + } + + @Override + public int hashCode() { + return type.hashCode(); + } + public static TypeToken of(Class type) { return new SimpleTypeToken<>(type); } diff --git a/litecommands-core/src/dev/rollczi/litecommands/requirement/ScheduledRequirementResolver.java b/litecommands-core/src/dev/rollczi/litecommands/requirement/ScheduledRequirementResolver.java index 017cacd38..ece33d538 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/requirement/ScheduledRequirementResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/requirement/ScheduledRequirementResolver.java @@ -64,17 +64,7 @@ private ScheduledRequirement toScheduled(Requirement requirement, Throwing @SuppressWarnings("unchecked") private > RequirementFutureResult matchArgument(Argument argument, Invocation invocation, MATCHER matcher) { - Class rawType = argument.getType().getRawType(); - ParserSet parserSet = (ParserSet) cachedParserSets.get(rawType, argument.getKey()); - - if (parserSet == null) { - parserSet = parserRegistry.getParserSet(rawType, argument.getKey()); - cachedParserSets.put(rawType, argument.getKey(), parserSet); - } - - Parser parser = parserSet.getValidParserOrThrow(argument); - - return matcher.nextArgument(invocation, argument, parser); + return matcher.nextArgument(invocation, argument, parserRegistry.getParser(argument)); } private RequirementFutureResult matchContext(ContextRequirement contextRequirement, Invocation invocation) { diff --git a/litecommands-core/src/dev/rollczi/litecommands/schematic/SchematicFastGenerator.java b/litecommands-core/src/dev/rollczi/litecommands/schematic/SchematicFastGenerator.java index df7c76b6a..c1cdf707e 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/schematic/SchematicFastGenerator.java +++ b/litecommands-core/src/dev/rollczi/litecommands/schematic/SchematicFastGenerator.java @@ -100,8 +100,7 @@ protected boolean isVisible(SchematicInput input, CommandExecutor boolean isOptional(SchematicInput input, Argument argument) { - ParserSet parserSet = parserRegistry.getParserSet(argument.getType().getRawType(), argument.getKey()); - Parser parser = parserSet.getValidParser(argument); + Parser parser = parserRegistry.getParserOrNull(argument); if (parser != null) { Range range = parser.getRange(argument); diff --git a/litecommands-core/src/dev/rollczi/litecommands/schematic/SimpleSchematicGenerator.java b/litecommands-core/src/dev/rollczi/litecommands/schematic/SimpleSchematicGenerator.java index d203554fa..2baf45a80 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/schematic/SimpleSchematicGenerator.java +++ b/litecommands-core/src/dev/rollczi/litecommands/schematic/SimpleSchematicGenerator.java @@ -89,8 +89,7 @@ protected boolean isVisible(SchematicInput input, CommandExecutor boolean isOptional(SchematicInput input, Argument argument) { - ParserSet parserSet = parserRegistry.getParserSet(argument.getType().getRawType(), argument.getKey()); - Parser parser = parserSet.getValidParser(argument); + Parser parser = parserRegistry.getParserOrNull(argument); if (parser != null) { Range range = parser.getRange(argument); diff --git a/litecommands-core/src/dev/rollczi/litecommands/shared/BiHashMap.java b/litecommands-core/src/dev/rollczi/litecommands/shared/BiHashMap.java index db145b630..fc74fd026 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/shared/BiHashMap.java +++ b/litecommands-core/src/dev/rollczi/litecommands/shared/BiHashMap.java @@ -2,7 +2,6 @@ import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; public class BiHashMap implements BiMap { @@ -23,7 +22,7 @@ public BiHashMap() { } public BiHashMap(int initialCapacity) { - this.nativeMap = new ConcurrentHashMap<>(initialCapacity); + this.nativeMap = new HashMap<>(initialCapacity); } @Override diff --git a/litecommands-core/test/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureArgumentTest.java b/litecommands-core/test/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureArgumentTest.java index c17ccc213..8f7160047 100644 --- a/litecommands-core/test/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureArgumentTest.java +++ b/litecommands-core/test/dev/rollczi/litecommands/argument/resolver/completeable/CompletableFutureArgumentTest.java @@ -40,8 +40,8 @@ void test() { .thenCompose(assertExecute -> assertExecute.assertSuccessAs(FUTURE_TYPE)); await() - .atLeast(350, TimeUnit.MILLISECONDS) - .atMost(1850, TimeUnit.MILLISECONDS) + .atLeast(400, TimeUnit.MILLISECONDS) + .atMost(900, TimeUnit.MILLISECONDS) .until(() -> completableFuture.isDone()); assertThat(completableFuture.join()) diff --git a/litecommands-unit/src/LiteBenchmark.java b/litecommands-unit/src/LiteBenchmark.java index af2c24ac2..cd02d5aa5 100644 --- a/litecommands-unit/src/LiteBenchmark.java +++ b/litecommands-unit/src/LiteBenchmark.java @@ -39,25 +39,25 @@ public static void main(String[] args) { System.out.println("Iteration: " + count); Stopper stopper = new Stopper("string"); for (int i = 0; i < ITERATIONS; i++) { - testPlatform.executeAsync("test first second"); + testPlatform.execute("test first second"); } stopper.stop(); stopper = new Stopper("number"); for (int i = 0; i < ITERATIONS; i++) { - testPlatform.executeAsync("test number 1 2"); + testPlatform.execute("test number 1 2"); } stopper.stop(); stopper = new Stopper("sub"); for (int i = 0; i < ITERATIONS; i++) { - testPlatform.executeAsync("test sub first"); + testPlatform.execute("test sub first"); } stopper.stop(); stopper = new Stopper("sub optional"); for (int i = 0; i < ITERATIONS; i++) { - testPlatform.executeAsync("test sub first second"); + testPlatform.execute("test sub first second"); } stopper.stop(); diff --git a/litecommands-unit/src/dev/rollczi/litecommands/unit/blocking/BlockingArgumentResolver.java b/litecommands-unit/src/dev/rollczi/litecommands/unit/blocking/BlockingArgumentResolver.java index 826cbd78f..b2fbbfc7b 100644 --- a/litecommands-unit/src/dev/rollczi/litecommands/unit/blocking/BlockingArgumentResolver.java +++ b/litecommands-unit/src/dev/rollczi/litecommands/unit/blocking/BlockingArgumentResolver.java @@ -15,7 +15,7 @@ public class BlockingArgumentResolver extends ArgumentResolver parse(Invocation invocation, Argument context, String argument) { return ParseResult.async(() -> { try { - Parameter parameter = context.meta().get(MetaAnnotationKeys.SOURCE_PARAMETER); + Parameter parameter = context.metaCollector().findFirst(MetaAnnotationKeys.SOURCE_PARAMETER); int blocking = 100; Blocking annotation = parameter.getAnnotation(Blocking.class);