diff --git a/litecommands-annotations/src/dev/rollczi/litecommands/annotations/AnnotationProcessorService.java b/litecommands-annotations/src/dev/rollczi/litecommands/annotations/AnnotationProcessorService.java index e5d04e620..1ac258db3 100644 --- a/litecommands-annotations/src/dev/rollczi/litecommands/annotations/AnnotationProcessorService.java +++ b/litecommands-annotations/src/dev/rollczi/litecommands/annotations/AnnotationProcessorService.java @@ -62,8 +62,6 @@ public static AnnotationProcessorService defaultService() { .register(new PriorityAnnotationResolver<>()) .register(new ValidateAnnotationResolver<>()) .register(new CooldownAnnotationResolver<>()) - // argument meta processors - .register(new KeyAnnotationResolver<>()) // universal processor for requirements such as @Arg, @Varargs, @Flag, @Context, @Bind and more .register(new RequirementDefinitionProcessor<>()) // profile processors (they apply profiles to arguments) @@ -76,6 +74,8 @@ public static AnnotationProcessorService defaultService() { .register(new QuotedAnnotationProcessor<>()) .register(new NullableArgumentProcessor<>()) .register(new OptionalArgArgumentProcessor<>()) + // argument meta processors + .register(new KeyAnnotationResolver<>()) ; } diff --git a/litecommands-annotations/test/dev/rollczi/litecommands/annotations/argument/resolver/nullable/NullableTest.java b/litecommands-annotations/test/dev/rollczi/litecommands/annotations/argument/resolver/nullable/NullableTest.java index 947b77a67..628114fcb 100644 --- a/litecommands-annotations/test/dev/rollczi/litecommands/annotations/argument/resolver/nullable/NullableTest.java +++ b/litecommands-annotations/test/dev/rollczi/litecommands/annotations/argument/resolver/nullable/NullableTest.java @@ -8,9 +8,6 @@ class NullableTest extends LiteTestSpec { - static LiteTestConfig config = builder -> builder - .advanced(); - @Command(name = "nullable") static class NullableTestCommand { @Execute diff --git a/litecommands-annotations/test/dev/rollczi/litecommands/annotations/argument/resolver/nullable/OptionalTest.java b/litecommands-annotations/test/dev/rollczi/litecommands/annotations/argument/resolver/nullable/OptionalTest.java new file mode 100644 index 000000000..874e83774 --- /dev/null +++ b/litecommands-annotations/test/dev/rollczi/litecommands/annotations/argument/resolver/nullable/OptionalTest.java @@ -0,0 +1,54 @@ +package dev.rollczi.litecommands.annotations.argument.resolver.nullable; + +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.argument.Key; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.execute.Execute; +import dev.rollczi.litecommands.annotations.optional.OptionalArg; +import dev.rollczi.litecommands.argument.ArgumentKey; +import dev.rollczi.litecommands.argument.parser.ParseResult; +import dev.rollczi.litecommands.argument.parser.Parser; +import dev.rollczi.litecommands.unit.annotations.LiteTestSpec; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class OptionalTest extends LiteTestSpec { + + interface IFaction {} + + static class Faction implements IFaction {} + + static LiteTestConfig config = builder -> builder + .argumentParser(Faction.class, ArgumentKey.of("faction"), Parser.of((invocation, text) -> ParseResult.success(new Faction()))) + .argumentParser(IFaction.class, ArgumentKey.of("ifaction"), Parser.of((invocation, text) -> ParseResult.success(new Faction()))) + .advanced(); + + @Command(name = "optional") + static class OptionalTestCommand { + @Execute + String executeOptional(@Arg("display-faction") @Key("faction") Optional faction) { + return faction.isPresent() ? "present" : "not present"; + } + + @Execute(name = "interface") + String executeInterfaceOptional(@OptionalArg("display-faction") @Key("ifaction") IFaction faction) { + return faction != null ? "interface present" : "interface not present"; + } + } + + @Test + void testOptionalExecute() { + platform.execute("optional") + .assertSuccess("not present"); + + platform.execute("optional faction") + .assertSuccess("present"); + + platform.execute("optional interface") + .assertSuccess("interface not present"); + + platform.execute("optional interface faction") + .assertSuccess("interface present"); + } + +} diff --git a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/BukkitPlatform.java b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/BukkitPlatform.java index d9fccd88b..92e98e957 100644 --- a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/BukkitPlatform.java +++ b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/BukkitPlatform.java @@ -17,10 +17,8 @@ class BukkitPlatform extends AbstractPlatform protected void hook(CommandRoute commandRoute, PlatformInvocationListener invocationHook, PlatformSuggestionListener suggestionHook) { BukkitCommand bukkitSimpleCommand = new BukkitCommand(settings, commandRoute, invocationHook, suggestionHook); - for (String name : commandRoute.names()) { - this.settings.commandsRegistry().register(name, settings.fallbackPrefix(), bukkitSimpleCommand); - } this.settings.tabCompleter().register(settings.fallbackPrefix(), bukkitSimpleCommand); + this.settings.commandsRegistry().register(commandRoute.getName(), settings.fallbackPrefix(), bukkitSimpleCommand); } @Override diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java b/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java index 8b46a67bc..b53dd1e11 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/Argument.java @@ -38,6 +38,9 @@ default Optional> defaultValue() { @ApiStatus.Experimental Argument child(TypeToken type); + @ApiStatus.Experimental + Argument withoutProfile(ArgumentProfileNamespace profile); + static Argument of(String name, Class type) { return new SimpleArgument<>(name, TypeToken.of(type)); } diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java b/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java index 18a73a0aa..33d9618b6 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/SimpleArgument.java @@ -117,7 +117,29 @@ public PrioritizedList> getProfiles() { @Override public Argument child(TypeToken type) { - return new SimpleArgument<>(name, type, this); + SimpleArgument newSimpleArgument = new SimpleArgument<>(name, type, this); + newSimpleArgument.setKey(key.withDefaultNamespace()); + newSimpleArgument.meta.putAll(this.meta); + for (ArgumentProfile argumentProfile : profiles) { + newSimpleArgument.addProfile(argumentProfile); + } + return newSimpleArgument; + } + + @Override + public Argument withoutProfile(ArgumentProfileNamespace profile) { + SimpleArgument argument = new SimpleArgument<>(name, type, nullable); + argument.setKey(key.withDefaultNamespace()); + argument.meta.putAll(this.meta); + for (ArgumentProfile argumentProfile : profiles) { + if (!argumentProfile.getNamespace().equals(profile)) { + argument.addProfile(argumentProfile); + } + else { + argument.meta.remove(profile.asMetaKey()); + } + } + return argument; } @Override diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/Parser.java b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/Parser.java index 56ce8e56a..5c771072b 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/Parser.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/Parser.java @@ -3,10 +3,12 @@ import dev.rollczi.litecommands.LiteCommandsException; import dev.rollczi.litecommands.argument.Argument; import dev.rollczi.litecommands.argument.matcher.Matcher; +import dev.rollczi.litecommands.argument.resolver.ArgumentResolver; import dev.rollczi.litecommands.input.raw.RawInput; import dev.rollczi.litecommands.invocation.Invocation; import dev.rollczi.litecommands.range.Rangeable; import dev.rollczi.litecommands.requirement.RequirementResult; +import java.util.function.BiFunction; import org.jetbrains.annotations.ApiStatus; public interface Parser extends Matcher, Rangeable> { @@ -42,4 +44,14 @@ default boolean match(Invocation invocation, Argument argument, throw new LiteCommandsException("Async parsers should override Parser#match method! (" + this.getClass().getName() + ")"); } + @ApiStatus.Experimental + static Parser of(BiFunction, String, ParseResult> parser) { + return new ArgumentResolver() { + @Override + protected ParseResult parse(Invocation invocation, Argument context, String argument) { + return parser.apply(invocation, argument); + } + }; + } + } 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 ea929e1fa..92411bafe 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,8 @@ private boolean match0(Invocation invocation, Argument c } TypeToken elementType = this.getElementType(collectionArgumentContainer); - Argument argument = collectionArgument.child(elementType); + Argument argument = collectionArgument.child(elementType) + .withoutProfile(VarargsProfile.NAMESPACE); Parser parser = parserRegistry.getParser(argument); Range range = parser.getRange(argument); @@ -100,7 +101,8 @@ private boolean match0(Invocation invocation, Argument c } private ParseResult> parseToList(TypeToken componentType, RawInput rawInput, Argument collectionArgument, VarargsProfile collectorArgumentHolder, Invocation invocation) { - Argument argument = collectionArgument.child(componentType); + Argument argument = collectionArgument.child(componentType) + .withoutProfile(VarargsProfile.NAMESPACE); Parser parser = parserRegistry.getParser(argument); @@ -214,7 +216,8 @@ public SuggestionResult suggest(Invocation invocation, Argument SuggestionResult suggest(TypeToken componentType, SuggestionContext context, Argument collectionArgument, VarargsProfile varargsProfile, Invocation invocation) { - Argument argument = collectionArgument.child(componentType); + Argument argument = collectionArgument.child(componentType) + .withoutProfile(VarargsProfile.NAMESPACE); Parser parser = parserRegistry.getParser(argument); Suggester suggester = suggesterRegistry.getSuggester(componentType.getRawType(), argument.getKey()); diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/nullable/NullableArgumentResolver.java b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/nullable/NullableArgumentResolver.java index 132e104e0..7a2e82bf1 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/nullable/NullableArgumentResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/resolver/nullable/NullableArgumentResolver.java @@ -4,6 +4,7 @@ import dev.rollczi.litecommands.argument.parser.ParseResult; import dev.rollczi.litecommands.argument.parser.Parser; import dev.rollczi.litecommands.argument.parser.ParserRegistry; +import dev.rollczi.litecommands.argument.profile.ProfileNamespaces; import dev.rollczi.litecommands.argument.profile.ProfiledMultipleArgumentResolver; import dev.rollczi.litecommands.argument.suggester.Suggester; import dev.rollczi.litecommands.argument.suggester.SuggesterRegistry; @@ -28,13 +29,11 @@ public NullableArgumentResolver(ParserRegistry parserRegistry, Suggester @Override protected ParseResult parse(Invocation invocation, Argument argument, RawInput rawInput, NullableProfile unused) { - TypeToken optionalType = argument.getType(); - - return parseValue(optionalType, invocation, argument, rawInput); + return parseValue(invocation, argument, rawInput); } - private ParseResult parseValue(TypeToken type, Invocation invocation, Argument optionalArgument, RawInput input) { - Argument argument = Argument.of(optionalArgument.getName(), type); + private ParseResult parseValue(Invocation invocation, Argument optionalArgument, RawInput input) { + Argument argument = optionalArgument.withoutProfile(ProfileNamespaces.NULLABLE); Parser parser = parserRegistry.getParser(argument); ParseResult parseResult = parser.parse(invocation, argument, input); @@ -55,7 +54,15 @@ protected SuggestionResult suggest(Invocation invocation, Argument argument, NullableProfile unused) { - return Range.range(0, 1); + return getRangeTyped(argument); + } + + private Range getRangeTyped(Argument optionalArgument) { + Argument argument = optionalArgument.withoutProfile(ProfileNamespaces.NULLABLE); + Parser parser = parserRegistry.getParser(argument); + Range range = parser.getRange(argument); + + return Range.range(0, range.getMax()); } } 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 d58c5f693..f246e6dfd 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 @@ -35,7 +35,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); + Argument argument = optionalArgument.child(type); ParseResult parseResult = chainAccessor.parse(invocation, argument.child(type), input); return parseResult @@ -65,7 +65,7 @@ public SuggestionResult suggest(Invocation invocation, Argument optionalType = argument.getType(); TypeToken parameterized = optionalType.getParameterized(); - return chainAccessor.suggest(invocation, Argument.of(argument.getName(), parameterized), context); + return chainAccessor.suggest(invocation, argument.child(parameterized), context); } } diff --git a/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawInputAnalyzer.java b/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawInputAnalyzer.java index d390b6de5..4b27703cb 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawInputAnalyzer.java +++ b/litecommands-core/src/dev/rollczi/litecommands/input/raw/RawInputAnalyzer.java @@ -40,9 +40,9 @@ public boolean hasNextRoute() { public Context toContext( Argument argument, - Parser parserSet + Parser parser ) { - return new Context<>(argument, parserSet); + return new Context<>(argument, parser); } public String showNextRoute() { diff --git a/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java b/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java index 53d402fd4..0b00f3b58 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java @@ -25,7 +25,7 @@ private class TypeIterator implements Iterator> { private Class next; private final Queue> interfaces = new LinkedList<>(); - private final Set> visitedInterfaces= new HashSet<>(); + private final Set> visitedInterfaces = new HashSet<>(); private TypeIterator() { this.next = baseType; @@ -39,10 +39,10 @@ public boolean hasNext() { @Override public Class next() { - Class next = this.next; + Class nextToReturn = this.next; this.next = null; - if (next == null) { + if (nextToReturn == null) { return null; } @@ -57,18 +57,22 @@ public Class next() { interfaces.addAll(ReflectIndex.getInterfaces(nextInterface)); this.next = nextInterface; - return next; + return nextToReturn; } - Class superclass = next.getSuperclass(); + Class superclass = nextToReturn.getSuperclass(); if (superclass != null) { this.next = superclass; interfaces.addAll(ReflectIndex.getInterfaces(superclass)); - return next; + return nextToReturn; } - return next; + if (this.next == null && nextToReturn != Object.class) { + this.next = Object.class; + } + + return nextToReturn; } } diff --git a/litecommands-core/test/dev/rollczi/litecommands/reflect/type/IterableSuperClassResolverTest.java b/litecommands-core/test/dev/rollczi/litecommands/reflect/type/IterableSuperClassResolverTest.java index 47887d8b0..9055850d0 100644 --- a/litecommands-core/test/dev/rollczi/litecommands/reflect/type/IterableSuperClassResolverTest.java +++ b/litecommands-core/test/dev/rollczi/litecommands/reflect/type/IterableSuperClassResolverTest.java @@ -35,7 +35,8 @@ void test() { Parser.class, Matcher.class, Suggester.class, - Rangeable.class + Rangeable.class, + Object.class ); }