diff --git a/.github/workflows/publish.yml b/.github/workflows/build-and-publish.yml similarity index 59% rename from .github/workflows/publish.yml rename to .github/workflows/build-and-publish.yml index 5412ecfe..088ac21c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -1,12 +1,15 @@ -name: Publish to GitHub Packages +name: Build and upload on: + push: + pull_request: + types: + - opened + - reopened + workflow_dispatch: release: types: - - published - push: - branches: - - 1.20.6\+ + - created env: USERNAME: ${{ github.actor }} @@ -17,13 +20,12 @@ jobs: build: runs-on: ubuntu-latest permissions: - contents: write - packages: write + contents: read steps: - name: Get current date run: echo "WLIB_BUILD_DATE=$(date +'%Y-%m-%d_%H-%M-%S')" >> "$GITHUB_ENV" - name: Get snapshot number - run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}))" >> $GITHUB_ENV + run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}+68))" >> $GITHUB_ENV - uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v4 @@ -48,5 +50,27 @@ jobs: for file in */build/libs/*.jar; do gh release upload ${{ github.event.release.tag_name }} $file done - - name: Publish + publish: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + if: ${{ github.event_name != 'pull_request' }} + needs: + - build + steps: + - name: Get current date + run: echo "WLIB_BUILD_DATE=$(date +'%Y-%m-%d_%H-%M-%S')" >> "$GITHUB_ENV" + - name: Get snapshot number + run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}+68))" >> $GITHUB_ENV + - uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: temurin + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Clean, Build and Publish run: ./gradlew publish + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index aae6a066..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Build and upload - -on: - push: - pull_request: - types: - - opened - - reopened - workflow_dispatch: - -env: - USERNAME: ${{ github.actor }} - TOKEN: ${{ secrets.GITHUB_TOKEN }} - WLIB_SNAPSHOT: ${{ github.event_name != 'release' }} - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Get current date - run: echo "WLIB_BUILD_DATE=$(date +'%Y-%m-%d_%H-%M-%S')" >> "$GITHUB_ENV" - - name: Get snapshot number - run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}))" >> $GITHUB_ENV - - uses: actions/checkout@v4 - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: 21 - distribution: temurin - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Build - run: ./gradlew build - - name: Upload results - uses: actions/upload-artifact@v4 - with: - path: "**/build/libs/*.jar" - name: WLib-${{ env.WLIB_BUILD_DATE }} - if-no-files-found: error \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0e100001..9137fbef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **/build/ .idea/ .gradle/ +/build.properties diff --git a/build.gradle b/build.gradle index 178f87b1..14cae67c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { id("com.gradleup.shadow") version "9.2.2" id 'maven-publish' + id("systems.manifold.manifold-gradle-plugin") version "0.0.2-alpha" apply false } subprojects { @@ -9,7 +10,7 @@ subprojects { apply plugin: 'maven-publish' group = 'com.wizardlybump17.wlib' - version = "1.6.8+1.20.6" + ("true".equalsIgnoreCase(System.getenv("WLIB_SNAPSHOT")) ? "-SNAPSHOT." + System.getenv("WLIB_SNAPSHOT_NUMBER") : "") + version = "1.6.8-ncs-2-1.20.6+" + ("true".equalsIgnoreCase(System.getenv("WLIB_SNAPSHOT")) ? "-SNAPSHOT." + System.getenv("WLIB_SNAPSHOT_NUMBER") : "") repositories { mavenLocal() @@ -51,13 +52,6 @@ subprojects { useJUnitPlatform() } - if (project.name == 'versions' || project.name.matches('v\\d_\\d+_R\\d+')) { - tasks.withType(PublishToMavenRepository).configureEach { - it.enabled = false - } - return - } - publishing { repositories { maven { @@ -72,13 +66,14 @@ subprojects { publications { gpr(MavenPublication) { from(components.java) - pom.withXml { - asNode().dependencies.dependency.each { dep -> - if (dep.artifactId.last().value().last().matches('v\\d_\\d+_R\\d+')) - dep.parent().remove(dep) - } - } } } } + + test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + } + } } \ No newline at end of file diff --git a/bukkit-utils/build.gradle b/bukkit-utils/build.gradle index 8e914b0b..4624945e 100644 --- a/bukkit-utils/build.gradle +++ b/bukkit-utils/build.gradle @@ -4,6 +4,9 @@ dependencies { 'org.jetbrains:annotations:23.0.0', "io.papermc.paper:paper-api:1.20.6-R0.1-20240702.153951-124", ) + compileOnly("net.kyori:adventure-api:4.18.0") + compileOnly("net.kyori:adventure-text-minimessage:4.18.0") + compileOnly("net.kyori:adventure-platform-bukkit:4.3.4") implementation( project(':utils') ) diff --git a/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/MiniMessageUtil.java b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/MiniMessageUtil.java new file mode 100644 index 00000000..c0157604 --- /dev/null +++ b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/MiniMessageUtil.java @@ -0,0 +1,88 @@ +package com.wizardlybump17.wlib.util.bukkit; + +import com.google.gson.JsonNull; +import com.google.gson.JsonPrimitive; +import com.wizardlybump17.wlib.util.StringUtil; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public final class MiniMessageUtil { + + private static final @NotNull TextComponent NULL = Component.text("null"); + + private MiniMessageUtil() { + } + + public static @NotNull Component getMessage(@NotNull String message, @NotNull Map placeholders) { + MiniMessage miniMessage = MiniMessage.miniMessage(); + if (placeholders.isEmpty()) + return miniMessage.deserialize(message); + + TagResolver[] resolvers = new TagResolver[placeholders.size()]; + int resolverIndex = 0; + for (Map.Entry entry : placeholders.entrySet()) { + String key = StringUtil.pascalToCamel(entry.getKey()); + Object value = entry.getValue(); + resolvers[resolverIndex++] = TagResolver.builder() + .tag( + key, + Tag.inserting(getInsertion(value)) + ) + .build(); + } + return miniMessage.deserialize(message, resolvers); + } + + public static @NotNull Component getMessage(@NotNull String message, @NotNull String placeholderKey, @NotNull Object placeholderValue, @NotNull String prefix, @NotNull Map additionalPlaceholders) { + MiniMessage miniMessage = MiniMessage.miniMessage(); + if (additionalPlaceholders.isEmpty()) + return getMessage(message, Map.of(placeholderKey, placeholderValue)); + + TagResolver[] resolvers = new TagResolver[additionalPlaceholders.size() + 1]; + int resolverIndex = 0; + for (Map.Entry entry : additionalPlaceholders.entrySet()) { + String key = StringUtil.pascalToCamel(entry.getKey()); + Object value = entry.getValue(); + resolvers[resolverIndex++] = TagResolver.builder() + .tag( + prefix + key, + Tag.inserting(getInsertion(value)) + ) + .build(); + } + resolvers[resolverIndex] = TagResolver.builder() + .tag( + prefix + placeholderKey, + Tag.inserting(getInsertion(placeholderValue)) + ) + .build(); + return miniMessage.deserialize(message, resolvers); + } + + private static @NotNull Component getInsertion(@Nullable Object object) { + if (object == null) + return NULL; + + return switch (object) { + case Component component -> component; + case JsonPrimitive jsonPrimitive -> Component.text(jsonPrimitive.getAsString()); + case JsonNull ignored -> NULL; + default -> Component.text(String.valueOf(object)); + }; + } + + public static @NotNull Component getMessage(@NotNull String message, @NotNull String placeholderKey, @NotNull Object placeholderValue, @NotNull Map additionalPlaceholders) { + return getMessage(message, placeholderKey, placeholderValue, "", additionalPlaceholders); + } + + public static @NotNull Component getMessage(@NotNull String message) { + return getMessage(message, Map.of()); + } +} diff --git a/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/collector/ComponentCollector.java b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/collector/ComponentCollector.java new file mode 100644 index 00000000..efd535a2 --- /dev/null +++ b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/collector/ComponentCollector.java @@ -0,0 +1,65 @@ +package com.wizardlybump17.wlib.util.bukkit.collector; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +public class ComponentCollector implements Collector { + + public static final @NotNull ComponentCollector COMMA = new ComponentCollector(Component.text(", ")); + public static final @NotNull ComponentCollector NEW_LINE = new ComponentCollector(Component.text("\n")); + public static final @NotNull ComponentCollector EMPTY = new ComponentCollector(Component.empty()); + + private final @NotNull Component separator; + + public ComponentCollector(@NotNull Component separator) { + this.separator = separator; + } + + @Override + public @NotNull Supplier supplier() { + return () -> new Component[1]; + } + + @Override + public @NotNull BiConsumer accumulator() { + return (array, component) -> { + if (array[0] == null) + array[0] = component; + else + array[0] = array[0].append(separator).append(component); + }; + } + + @Override + public @NotNull BinaryOperator combiner() { + return (left, right) -> { + if (left[0] == null) + left[0] = right[0]; + else + left[0] = left[0].append(separator).append(right[0]); + return left; + }; + } + + @Override + public @NotNull Function finisher() { + return array -> Objects.requireNonNullElseGet(array[0], Component::empty); + } + + @Override + public @NotNull Set characteristics() { + return Set.of(); + } + + public @NotNull Component getSeparator() { + return separator; + } +} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommand.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommand.java deleted file mode 100644 index 494572cd..00000000 --- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.wizardlybump17.wlib.bungee.command; - -import com.wizardlybump17.wlib.command.CommandManager; -import com.wizardlybump17.wlib.command.holder.Command; -import com.wizardlybump17.wlib.command.holder.CommandExecutor; - -public class BungeeCommand implements Command { - - private CommandExecutor executor; - - @Override - public void setExecutor(CommandExecutor executor) { - this.executor = executor; - } - - @Override - public CommandExecutor getExecutor() { - return executor; - } - - @Override - public CommandExecutor getDefaultExecutor(CommandManager manager, String name) { - return new BungeeCommandExecutor(manager, name); - } -} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java deleted file mode 100644 index 57ed1875..00000000 --- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.wizardlybump17.wlib.bungee.command; - -import com.wizardlybump17.wlib.bungee.command.sender.GenericSender; -import com.wizardlybump17.wlib.bungee.command.sender.ProxiedPlayerSender; -import com.wizardlybump17.wlib.command.CommandManager; -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.holder.CommandExecutor; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.plugin.Command; - -public class BungeeCommandExecutor extends Command implements CommandExecutor { - - private final CommandManager manager; - - public BungeeCommandExecutor(CommandManager manager, String name) { - super(name); - this.manager = manager; - } - - @Override - public void execute(CommandSender sender, String commandName, String[] args) { - manager.execute(sender, commandName + " " + String.join(" ", args)); - } - - @Override - public void execute(net.md_5.bungee.api.CommandSender sender, String[] args) { - CommandSender realSender; - if (sender instanceof ProxiedPlayer) - realSender = new ProxiedPlayerSender((ProxiedPlayer) sender); - else - realSender = new GenericSender(sender); - execute(realSender, getName(), args); - } -} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandHolder.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandHolder.java deleted file mode 100644 index 878d093c..00000000 --- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandHolder.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.wizardlybump17.wlib.bungee.command; - -import com.wizardlybump17.wlib.command.holder.Command; -import com.wizardlybump17.wlib.command.holder.CommandHolder; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import net.md_5.bungee.api.plugin.Plugin; - -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Logger; - -@RequiredArgsConstructor -public class BungeeCommandHolder implements CommandHolder { - - private final Plugin plugin; - private final Map commands = new HashMap<>(); - - @Override - public Command getCommand(String name) { - if (commands.containsKey(name)) - return commands.get(name); - BungeeCommand command = new BungeeCommand(); - commands.put(name, command); - return command; - } - - @Override - public Plugin getHandle() { - return plugin; - } - - @Override - public @NonNull Logger getLogger() { - return plugin.getLogger(); - } -} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java deleted file mode 100644 index 48c0b047..00000000 --- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.wizardlybump17.wlib.bungee.command; - -import com.wizardlybump17.wlib.command.CommandManager; -import com.wizardlybump17.wlib.command.RegisteredCommand; -import net.md_5.bungee.api.plugin.Command; -import net.md_5.bungee.api.plugin.Plugin; - -public class BungeeCommandManager extends CommandManager { - - public BungeeCommandManager(BungeeCommandHolder holder) { - super(holder); - } - - @Override - public void registerCommands(Object... objects) { - super.registerCommands(objects); - for (Object object : objects) { - for (RegisteredCommand command : getCommands(object)) { - Plugin plugin = (Plugin) holder.getHandle(); - plugin.getProxy().getPluginManager().registerCommand(plugin, (Command) holder.getCommand(command.getName()).getExecutor()); - } - } - } -} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/BungeeCommandSender.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/BungeeCommandSender.java new file mode 100644 index 00000000..1f86a969 --- /dev/null +++ b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/BungeeCommandSender.java @@ -0,0 +1,94 @@ +package com.wizardlybump17.wlib.bungee.command.sender; + +import com.wizardlybump17.wlib.bungee.util.collector.ComponentCollector; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ConnectedPlayer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@RequiredArgsConstructor +public class BungeeCommandSender implements com.wizardlybump17.wlib.command.sender.CommandSender { + + private final net.md_5.bungee.api.CommandSender handle; + + @Override + public net.md_5.bungee.api.CommandSender getHandle() { + return handle; + } + + @Override + public void sendMessage(String message) { + handle.sendMessage(TextComponent.fromLegacyText(message)); + } + + @Override + public void sendMessage(String... messages) { + handle.sendMessage(TextComponent.fromLegacyText(String.join("\n", messages))); + } + + @Override + public void sendMessage(@Nullable Object message) { + switch (message) { + case null -> handle.sendMessage(new TextComponent("null")); + case BaseComponent component -> handle.sendMessage(component); + default -> handle.sendMessage(new TextComponent(String.valueOf(message))); + } + } + + @Override + public void sendMessage(@Nullable Object @Nullable ... messages) { + if (messages == null) { + sendMessage((Object) null); + return; + } + + List components = new ArrayList<>(messages.length); + for (Object message : messages) { + switch (message) { + case null -> components.add(new TextComponent("null")); + case BaseComponent component -> components.add(component); + case String string -> components.add(new TextComponent(string)); + default -> components.add(new TextComponent(String.valueOf(message))); + } + } + + handle.sendMessage(components.stream().collect(ComponentCollector.NEW_LINE)); + } + + @Override + public String getName() { + return handle.getName(); + } + + @Override + public boolean hasPermission(String permission) { + return handle.hasPermission(permission); + } + + public @NotNull ProxiedPlayer asProxiedPlayer() { + return (ProxiedPlayer) handle; + } + + public @NotNull ConnectedPlayer asConnectedPlayer() { + return (ConnectedPlayer) handle; + } + + @Override + public boolean hasId(@NotNull UUID id) { + return handle instanceof ProxiedPlayer player && player.getUniqueId().equals(id); + } + + @Override + public @NotNull UUID getId() throws IllegalStateException { + if (handle instanceof ProxiedPlayer player) + return player.getUniqueId(); + throw new IllegalStateException(handle + " does not have an ID"); + } +} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/GenericSender.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/GenericSender.java deleted file mode 100644 index 86698b06..00000000 --- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/GenericSender.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.wizardlybump17.wlib.bungee.command.sender; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.RequiredArgsConstructor; -import net.md_5.bungee.api.chat.TextComponent; - -@RequiredArgsConstructor -public class GenericSender implements CommandSender { - - private final net.md_5.bungee.api.CommandSender handle; - - @Override - public net.md_5.bungee.api.CommandSender getHandle() { - return handle; - } - - @Override - public void sendMessage(String message) { - handle.sendMessage(TextComponent.fromLegacyText(message)); - } - - @Override - public void sendMessage(String... messages) { - handle.sendMessage(TextComponent.fromLegacyText(String.join("\n", messages))); - } - - @Override - public String getName() { - return handle.getName(); - } - - @Override - public boolean hasPermission(String permission) { - return handle.hasPermission(permission); - } - - @Override - public GenericSender toGeneric() { - return this; - } - - public static boolean isGeneric() { - return true; - } -} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/ProxiedPlayerSender.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/ProxiedPlayerSender.java deleted file mode 100644 index d2fca3f7..00000000 --- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/ProxiedPlayerSender.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.wizardlybump17.wlib.bungee.command.sender; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.RequiredArgsConstructor; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -@RequiredArgsConstructor -public class ProxiedPlayerSender implements CommandSender { - - private final ProxiedPlayer handle; - - @Override - public ProxiedPlayer getHandle() { - return handle; - } - - @Override - public void sendMessage(String message) { - handle.sendMessage(TextComponent.fromLegacyText(message)); - } - - @Override - public void sendMessage(String... messages) { - handle.sendMessage(TextComponent.fromLegacyText(String.join("\n", messages))); - } - - @Override - public String getName() { - return handle.getName(); - } - - @Override - public boolean hasPermission(String permission) { - return handle.hasPermission(permission); - } - - @Override - public GenericSender toGeneric() { - return new GenericSender(handle); - } -} diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/util/collector/ComponentCollector.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/util/collector/ComponentCollector.java new file mode 100644 index 00000000..55c83b05 --- /dev/null +++ b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/util/collector/ComponentCollector.java @@ -0,0 +1,72 @@ +package com.wizardlybump17.wlib.bungee.util.collector; + +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +public class ComponentCollector implements Collector { + + public static final @NotNull ComponentCollector COMMA = new ComponentCollector(new TextComponent(", ")); + public static final @NotNull ComponentCollector NEW_LINE = new ComponentCollector(new TextComponent("\n")); + public static final @NotNull ComponentCollector EMPTY = new ComponentCollector(new TextComponent()); + + private final @NotNull BaseComponent separator; + + public ComponentCollector(@NotNull BaseComponent separator) { + this.separator = separator; + } + + @Override + public @NotNull Supplier supplier() { + return () -> new BaseComponent[1]; + } + + @Override + public @NotNull BiConsumer accumulator() { + return (array, component) -> { + if (array[0] == null) + array[0] = component.duplicate(); + else { + array[0] = array[0].duplicate(); + array[0].addExtra(separator); + array[0].addExtra(component); + } + }; + } + + @Override + public @NotNull BinaryOperator combiner() { + return (left, right) -> { + if (left[0] == null) + left[0] = right[0].duplicate(); + else { + left[0] = left[0].duplicate(); + left[0].addExtra(separator); + left[0].addExtra(right[0].duplicate()); + } + return left; + }; + } + + @Override + public @NotNull Function finisher() { + return array -> Objects.requireNonNullElseGet(array[0], TextComponent::new); + } + + @Override + public @NotNull Set characteristics() { + return Set.of(); + } + + public @NotNull BaseComponent getSeparator() { + return separator.duplicate(); + } +} diff --git a/commands/build.gradle b/commands/build.gradle deleted file mode 100644 index ba457952..00000000 --- a/commands/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -dependencies { - compileOnly( - 'org.projectlombok:lombok:1.18.32', - 'org.jetbrains:annotations:23.0.0', - ) - annotationProcessor('org.projectlombok:lombok:1.18.32') - implementation( - project(':objects'), - project(':utils') - ) - implementation("com.github.sisyphsu:dateparser:1.0.11") { - exclude(group: "org.projectlombok", module: "lombok") - } -} - -shadowJar { - relocate("com.github.sisyphsu", "com.wizardlybump17.wlib.libs.com.github.sisyphsu") -} diff --git a/commands/build.gradle.kts b/commands/build.gradle.kts new file mode 100644 index 00000000..10b877a5 --- /dev/null +++ b/commands/build.gradle.kts @@ -0,0 +1,12 @@ +val annotations = "26.0.1" +val gson = "2.13.2" + +dependencies { + implementation("org.jetbrains:annotations:${annotations}") + implementation(project(":objects")) + implementation(project(":utils")) + + implementation("com.google.code.gson:gson:${gson}") + + testImplementation("org.jetbrains:annotations:${annotations}") +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/Argument.java b/commands/src/main/java/com/wizardlybump17/wlib/command/Argument.java deleted file mode 100644 index 71416e57..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/Argument.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.wizardlybump17.wlib.command; - -import lombok.AccessLevel; -import lombok.Data; -import lombok.Getter; - -/** - * Class used in the arguments to represent a command argument. - *

It stores useful information like the argument name and the user input

- * @param the argument type - */ -@Data -public class Argument { - - private final String name; - @Getter(AccessLevel.NONE) - private final T value; - private final String input; - - public T value() { - return value; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/Command.java b/commands/src/main/java/com/wizardlybump17/wlib/command/Command.java index ca53cf22..3419d4f2 100644 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/Command.java +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/Command.java @@ -1,84 +1,78 @@ package com.wizardlybump17.wlib.command; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - *

An annotation that describes a command.

- *

For a command to be valid the first parameter of the method must implements {@link CommandSender}

- */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Command { - - /** - *

How the sender must type the command in order for it to be triggered. - * Example:

command hello world
- * In the example above, the sender must type /command hello world, so it can be triggered. - * To add parameters that depends on the sender input, just put the parameter between <>. - *
- * Example:
command <hello> world

- *

The type of each parameter is defined in the method that have this annotation. - * An example for the command above: - *

-     *  @Command(execution = "command <hello> world")
-     *  public void commandHelloWorld(GenericSender sender, String hello) {
-     *      System.out.println(sender.getName() + " executed the hello world command with the argument " + hello);
-     *  }
-     * 

- * - * @return how the command must be sent to be triggered - * @see com.wizardlybump17.wlib.command.args.reader.ArgsReader - */ - String execution(); - - /** - * @return which permission the sender must have to trigger this command - */ - String permission() default ""; - - /** - *

Used when the {@link CommandSender} does not have the required {@link #permission()}.

- * @return the message to be sent when the {@link CommandSender} does not have the required {@link #permission()} - */ - String permissionMessage() default ""; - - /** - * @return if the {@link #permissionMessage()} is a field in the class that have this annotation - */ - boolean permissionMessageIsField() default false; - - /** - * Sets the priority of this command. If the priority is -1, then the priority check is the same as - *
{@code this.execution().split(" ").length}
- * - * @return the priority of this command - */ - int priority() default -1; - - /** - * Sets the options of this command. - * The Bukkit implementation does nothing with this - * - * @return the options of this command - */ - String[] options() default {}; - - /** - * @return the description of this command - */ - String description() default ""; - - /** - *

Used when the {@link CommandSender} is not valid for this command.

- * @return the message to be sent when the {@link CommandSender} is not valid for this command - */ - String invalidSenderMessage() default ""; - - /** - * @return if the {@link #invalidSenderMessage()} is a field in the class that have this annotation - */ - boolean invalidSenderMessageIsField() default false; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Comparator; +import java.util.Objects; + +public class Command implements Comparable { + + public static final @NotNull Comparator COMPARATOR = Comparator.comparing(Command::getFullCommand); + + private final @NotNull LiteralCommandNode root; + + public Command(@NotNull LiteralCommandNode root) { + this.root = root; + } + + public @NotNull LiteralCommandNode getRoot() { + return root; + } + + public @NotNull Command merge(@NotNull Command other) { + if (other.getClass() != getClass()) + return other.merge(this); + return new Command(root.merge(other.getRoot())); + } + + @Override + public String toString() { + return "Command{" + + "root=" + root + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + Command command = (Command) o; + return Objects.equals(root, command.root); + } + + public boolean equalsIgnoreExecutor(@Nullable Object other) { + if (other == null || getClass() != other.getClass()) + return false; + Command command = (Command) other; + return root.equalsIgnoreExecutor(command.root); + } + + @Override + public int hashCode() { + return Objects.hashCode(root); + } + + @Override + public int compareTo(@NotNull Command other) { + return COMPARATOR.compare(this, other); + } + + public @NotNull String getFullCommand() { + return root.getFullCommand(); + } + + public @Nullable CommandNode findNode(@NotNull String name) { + return root.findChild(name); + } + + public @NotNull String getName() { + return root.getName(); + } + + public int getTotalNodes() { + return root.getTotalNodes(); + } } diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java deleted file mode 100644 index 371354f6..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.wizardlybump17.wlib.command; - -import com.wizardlybump17.wlib.command.holder.CommandHolder; -import com.wizardlybump17.wlib.util.ReflectionUtil; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.Nullable; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; - -@Getter -@RequiredArgsConstructor -public class CommandManager { - - private final List commands = new ArrayList<>(); - protected final CommandHolder holder; - private final @NonNull Map, Map> fieldCache = new HashMap<>(); - - public void registerCommands(Object... objects) { - for (Object object : objects) { - for (Method method : object.getClass().getDeclaredMethods()) { - if (!method.isAnnotationPresent(Command.class) || method.getParameterCount() == 0 || !CommandSender.class.isAssignableFrom(method.getParameterTypes()[0])) - continue; - - MethodHandle handle; - try { - handle = MethodHandles.publicLookup().findVirtual(object.getClass(), method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes())); - } catch (NoSuchMethodException | IllegalAccessException e) { - holder.getLogger().log(Level.SEVERE, "Error while trying to get the MethodHandle for " + method.getName() + " at " + object.getClass().getName(), e); - continue; - } - - RegisteredCommand command = new RegisteredCommand( - method.getAnnotation(Command.class), - object, - method, - handle - ); - - commands.add(command); - com.wizardlybump17.wlib.command.holder.Command holderCommand = holder.getCommand(command.getName()); - if (holderCommand != null) - holderCommand.setExecutor(holderCommand.getDefaultExecutor(this, command.getName())); - } - } - - commands.sort(null); - } - - public void unregisterCommands() { - commands.clear(); - } - - public void execute(CommandSender sender, String string) { - if (commands.isEmpty()) - return; - - for (RegisteredCommand registeredCommand : commands) { - Command command = registeredCommand.getCommand(); - CommandResult result = registeredCommand.execute(sender, string); - - switch (result) { - case PERMISSION_FAIL -> { - handleMessage(registeredCommand, sender, command.permissionMessage(), command.permissionMessageIsField()); - return; - } - - case INVALID_SENDER -> { - handleMessage(registeredCommand, sender, command.invalidSenderMessage(), command.invalidSenderMessageIsField()); - return; - } - - case SUCCESS, EXCEPTION -> { - return; - } - } - } - } - - protected void handleMessage(@NonNull RegisteredCommand registeredCommand, @NonNull CommandSender sender, @NonNull String message, boolean isField) { - if (!isField) { - if (!message.isEmpty()) - sender.sendMessage(message); - return; - } - - String fieldMessage = getFieldMessage(registeredCommand, message); - if (fieldMessage != null) - sender.sendMessage(fieldMessage); - } - - protected @Nullable String getFieldMessage(@NonNull RegisteredCommand registeredCommand, @NonNull String fieldName) { - Map fields = fieldCache.computeIfAbsent(registeredCommand.getObject().getClass(), clazz -> { - Map map = new HashMap<>(); - for (Field field : clazz.getDeclaredFields()) - map.put(field.getName(), field); - return map; - }); - - Field field = fields.get(fieldName); - if (field == null) - return null; - - Object fieldValue = ReflectionUtil.getFieldValue(field, registeredCommand.getObject()); - return fieldValue == null ? null : fieldValue.toString(); - } - - @NonNull - public List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String string) { - List result = new ArrayList<>(); - for (RegisteredCommand command : commands) - result.addAll(command.autoComplete(sender, string)); - return result; - } - - public List getCommand(String name) { - List commands = new ArrayList<>(this.commands.size()); - for (RegisteredCommand command : this.commands) - if (command.getName().equalsIgnoreCase(name)) - commands.add(command); - return commands; - } - - public List getCommands(Object object) { - List commands = new ArrayList<>(this.commands.size()); - for (RegisteredCommand command : this.commands) - if (command.getObject() == object) - commands.add(command); - return commands; - } -} \ No newline at end of file diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandResult.java b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandResult.java deleted file mode 100644 index 875696af..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandResult.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wizardlybump17.wlib.command; - -public enum CommandResult { - - SUCCESS, - ARGS_FAIL, - EXCEPTION, - PERMISSION_FAIL, - METHOD_FAIL, - INVALID_SENDER -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/RegisteredCommand.java b/commands/src/main/java/com/wizardlybump17/wlib/command/RegisteredCommand.java deleted file mode 100644 index 5ae2885e..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/RegisteredCommand.java +++ /dev/null @@ -1,275 +0,0 @@ -package com.wizardlybump17.wlib.command; - -import com.wizardlybump17.wlib.command.args.ArgsNode; -import com.wizardlybump17.wlib.command.args.ArgsReaderRegistry; -import com.wizardlybump17.wlib.command.args.ArgsReaderType; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException; -import com.wizardlybump17.wlib.object.Pair; -import lombok.Getter; -import lombok.NonNull; -import org.jetbrains.annotations.NotNull; - -import java.lang.invoke.MethodHandle; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.*; - -@Getter -public class RegisteredCommand implements Comparable { - - private final Command command; - private final Object object; - private final Method method; - private final List nodes = new ArrayList<>(); - private final @NonNull MethodHandle methodHandle; - - public RegisteredCommand(Command command, Object object, Method method, @NonNull MethodHandle methodHandle) { - this.command = command; - this.object = object; - this.method = method; - this.methodHandle = methodHandle; - prepareNodes(); - } - - private void prepareNodes() { - String[] commandArgs = command.execution().split(" "); - - Class[] types = method.getParameterTypes(); - Parameter[] parameters = method.getParameters(); - int index = 1; //skipping the first type because of the CommandSender - for (String commandArg : commandArgs) { - if (!requiredArgs(commandArg)) { - nodes.add(new ArgsNode( - commandArg, - false, - null, - null, - false - )); - continue; - } - - Description description = parameters[index].getAnnotation(Description.class); - - ArgsReaderType argsReaderType = parameters[index].getAnnotation(ArgsReaderType.class); - if (argsReaderType == null && Argument.class.isAssignableFrom(types[index])) - throw new IllegalArgumentException("the \"" + commandArg + "\" argument requires the " + ArgsReaderType.class.getName() + " annotation"); - - ArgsReader reader; - if (argsReaderType == null) - reader = ArgsReaderRegistry.INSTANCE.getReader(types[index]); - else - reader = ArgsReaderRegistry.INSTANCE.get(argsReaderType.value()); - if (reader == null) - throw new IllegalArgumentException("no reader found for " + types[index].getName()); - - nodes.add(new ArgsNode( - trim(commandArg), - true, - reader, - description == null ? null : description.value(), - Argument.class.isAssignableFrom(types[index]) - )); - - index++; - } - } - - public String getName() { - return command.execution().split(" ")[0]; - } - - @Override - public int compareTo(@NotNull RegisteredCommand o) { - if (o.command.priority() == -1 && command.priority() == -1) { - int args = compareArgs(o); - - if (args == 0) - return compareSize(o); - - return args; - } - - return Integer.compare(o.command.priority(), command.priority()); - } - - private int compareArgs(RegisteredCommand other) { - return Integer.compare(other.command.execution().split(" ").length, command.execution().split(" ").length); - } - - private int compareSize(RegisteredCommand other) { - return Integer.compare(other.command.execution().length(), command.execution().length()); - } - - public Optional>> parse(String input, boolean autoComplete) throws ArgsReaderException { - List toParse = new ArrayList<>(); - checkArrays(input, toParse, autoComplete); - - List> result = new ArrayList<>(nodes.size()); - - if (parseRequiredOnly(result, toParse, autoComplete)) - return Optional.of(result); - - return Optional.empty(); - } - - private boolean parseRequiredOnly(List> target, List strings, boolean autoComplete) throws ArgsReaderException { - if (autoComplete && nodes.size() > strings.size()) - return false; - - for (int i = 0; i < nodes.size() && i < strings.size(); i++) { - ArgsNode node = nodes.get(i); - if (!node.isUserInput()) { - if (!node.getName().equalsIgnoreCase(strings.get(i))) - return false; - - continue; - } - - Object parse = node.parse(strings.get(i)); - if (parse == ArgsNode.EMPTY) - continue; - - target.add(new Pair<>(node, parse)); - } - - return true; - } - - private void checkArrays(String input, List target, boolean autoComplete) throws ArgsReaderException { - StringBuilder builder = new StringBuilder(); - boolean inArray = false; - for (String s : input.split(" ")) { - if (s.startsWith("\"") && s.endsWith("\"") && !s.endsWith("\\\"") && s.length() != 1) { //"string" | single word - target.add(s.substring(1, s.length() - 1)); - continue; - } - - if (s.startsWith("\"")) { //"string | it is starting the array - builder.append(s.substring(1).replace("\\\"", "\"")).append(" "); - inArray = true; - continue; - } - - if (s.endsWith("\"") && !s.endsWith("\\\"")) { //string" | it is ending the array - builder.append(s.replace("\\\"", "\""), 0, s.length() - 1); - target.add(builder.toString()); - builder = new StringBuilder(); - inArray = false; - continue; - } - - if (!builder.isEmpty()) { //string | it is in the array - builder.append(s.replace("\\\"", "\"")).append(" "); - continue; - } - - target.add(s.replace("\\\"", "\"")); //string | it is not in the array - } - - if (inArray && !autoComplete) - throw new ArgsReaderException("Invalid array"); - } - - public CommandResult execute(CommandSender sender, String string) { - try { - Optional>> parse = parse(string, true); - if (parse.isEmpty()) - return CommandResult.ARGS_FAIL; - - if (!getSenderType().isInstance(sender) && !isSenderGeneric()) - return CommandResult.INVALID_SENDER; - - Object[] objects = parse.get().stream().map(Pair::getSecond).toArray(); - return executeInternal(sender, objects); - } catch (ArgsReaderException e) { - return CommandResult.EXCEPTION; - } - } - - private CommandResult executeInternal(CommandSender sender, Object[] objects) { - try { - if (!command.permission().isEmpty() && !sender.hasPermission(command.permission())) - return CommandResult.PERMISSION_FAIL; - - List list = new ArrayList<>(Arrays.asList(objects)); - list.add(0, isSenderGeneric() ? sender.toGeneric() : sender); - - if (list.size() != method.getParameterCount()) - return CommandResult.ARGS_FAIL; - - list.add(0, object); - methodHandle.invokeWithArguments(list); - return CommandResult.SUCCESS; - } catch (Throwable e) { - e.printStackTrace(); - return CommandResult.METHOD_FAIL; - } - } - - @NonNull - public List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String string) { - if (nodes.size() == 1) - return Collections.emptyList(); - - if (!getSenderType().isInstance(sender) && !isSenderGeneric()) - return Collections.emptyList(); - - try { - Optional>> parse = parse(string, false); - if (parse.isEmpty()) - return Collections.emptyList(); - - String[] split = string.split(" "); - String lastString = split[split.length - 1]; - - List> pairs = parse.get(); - - if (pairs.isEmpty()) { - ArgsNode secondNode = nodes.get(1); - ArgsReader secondNodeReader = secondNode.getReader(); - return secondNodeReader == null ? List.of(secondNode.getName()) : secondNodeReader.autoComplete(sender, lastString); - } - - Pair lastPair = pairs.get(pairs.size() - 1); - - ArgsNode node = lastPair.getFirst(); - ArgsReader reader = node.getReader(); - - return reader == null ? List.of(node.getName()) : reader.autoComplete(sender, lastString); - } catch (ArgsReaderException e) { - e.printStackTrace(); - return Collections.emptyList(); - } - } - - @Override - public String toString() { - return "RegisteredCommand{" + command.execution() + "}"; - } - - @SuppressWarnings("unchecked") - public Class> getSenderType() { - return (Class>) method.getParameterTypes()[0]; - } - - public boolean isSenderGeneric() { - boolean result = false; - try { - result = (Boolean) getSenderType().getDeclaredMethod("isGeneric").invoke(null); - } catch (NoSuchMethodException ignored) { - } catch (Exception e) { - e.printStackTrace(); - } - return result; - } - - private static String trim(String string) { - return string.substring(1, string.length() - 1); - } - - private static boolean requiredArgs(String string) { - return string.startsWith("<") && string.endsWith(">"); - } -} \ No newline at end of file diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/Command.java b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/Command.java new file mode 100644 index 00000000..bdd69c96 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/Command.java @@ -0,0 +1,17 @@ +package com.wizardlybump17.wlib.command.annotation; + +import org.jetbrains.annotations.NotNull; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Command { + + @NotNull String value(); + + @NotNull String permission() default ""; +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/Description.java b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/NonNullInput.java similarity index 71% rename from commands/src/main/java/com/wizardlybump17/wlib/command/Description.java rename to commands/src/main/java/com/wizardlybump17/wlib/command/annotation/NonNullInput.java index f66546fc..1060aab8 100644 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/Description.java +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/NonNullInput.java @@ -1,4 +1,4 @@ -package com.wizardlybump17.wlib.command; +package com.wizardlybump17.wlib.command.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -7,7 +7,5 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) -public @interface Description { - - String value(); +public @interface NonNullInput { } diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsNode.java deleted file mode 100644 index afb04352..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsNode.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.wizardlybump17.wlib.command.args; - -import com.wizardlybump17.wlib.command.Argument; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException; -import lombok.AllArgsConstructor; -import lombok.Data; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Represents a node of a command argument - */ -@Data -@AllArgsConstructor -public class ArgsNode { - - public static final Object EMPTY = new Object(); - - @NotNull - private final String name; - private final boolean userInput; - @Nullable - private final ArgsReader reader; - @Nullable - private final String description; - private final boolean isArgument; - - /** - * Parses the given input - * @param input the input - * @return the parsed object - * @throws ArgsReaderException if the input is invalid - */ - public Object parse(String input) throws ArgsReaderException { - if (reader == null) - return EMPTY; - - Object object = reader.read(input); - if (isArgument) - return new Argument<>(name, object, input); - - return object; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderRegistry.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderRegistry.java deleted file mode 100644 index 09c8f99d..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderRegistry.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.wizardlybump17.wlib.command.args; - -import com.wizardlybump17.wlib.command.args.reader.*; -import com.wizardlybump17.wlib.object.Registry; - -/** - * A registry for the {@link ArgsReader}. - *

The key is the {@link ArgsReader#getClass()} and the value is the {@link ArgsReader}

- */ -public class ArgsReaderRegistry extends Registry>, ArgsReader> { - - public static final ArgsReaderRegistry INSTANCE = new ArgsReaderRegistry(); - - static { - INSTANCE.add(new StringReader()); - INSTANCE.add(new ByteReader()); - INSTANCE.add(new ByteArrayReader()); - INSTANCE.add(new ShortReader()); - INSTANCE.add(new ShortArrayReader()); - INSTANCE.add(new IntegerReader()); - INSTANCE.add(new IntegerArrayReader()); - INSTANCE.add(new FloatReader()); - INSTANCE.add(new FloatArrayReader()); - INSTANCE.add(new DoubleReader()); - INSTANCE.add(new DoubleArrayReader()); - INSTANCE.add(new BigIntegerArgsReader()); - INSTANCE.add(new BigDecimalArgsReader()); - INSTANCE.add(new UUIDReader()); - INSTANCE.add(new BooleanReader()); - INSTANCE.add(new DateReader()); - } - - private ArgsReaderRegistry() { - } - - @SuppressWarnings("unchecked") - public void add(ArgsReader reader) { - put((Class>) reader.getClass(), reader); - } - - /** - * Gets the first reader that can read the specified type - * @param type the type - * @return the reader - */ - public ArgsReader getReader(Class type) { - for (ArgsReader reader : getMap().values()) - if (reader.getTypes().contains(type)) - return reader; - return null; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderType.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderType.java deleted file mode 100644 index 2e8fc4ca..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderType.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.wizardlybump17.wlib.command.args; - -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Specified the {@link ArgsReader} of the parameter - */ -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface ArgsReaderType { - - /** - * @return the class of the {@link ArgsReader} to use - */ - Class> value(); -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReader.java deleted file mode 100644 index 3ca7ecee..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReader.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; - -/** - * Used to read and convert the args from string to the specified type - * @param which type the string will be converted to - */ -public abstract class ArgsReader { - - private List> typesCache; - - /** - *

The type that the string will be converted to.

- *

If it is {@code null} then you should use the {@link com.wizardlybump17.wlib.command.args.ArgsReaderType} annotation on your parameter

- * @return the type that the string will be converted to - * @deprecated Use the {@link #getTypes()} instead - */ - @Nullable - @Deprecated - public abstract Class getType(); - - /** - * @return the types that {@code this} {@link ArgsReader} can accept in the method parameter - */ - public @NonNull List> getTypes() { - if (typesCache == null) - typesCache = Collections.singletonList(getType()); - return typesCache; - } - - public abstract T read(String string) throws ArgsReaderException; - - /** - *

Gives the suggestions for the specified sender and current argument (optional).

- * @param sender who is executing the command - * @param current the current argument - * @return a {@link List} of suggestions - */ - @NonNull - public List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return Collections.emptyList(); - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReaderException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReaderException.java deleted file mode 100644 index e24704dd..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReaderException.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -public class ArgsReaderException extends Exception { - - public ArgsReaderException(String message) { - super(message); - } - - public ArgsReaderException(Throwable cause) { - super(cause); - } - - public ArgsReaderException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigDecimalArgsReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigDecimalArgsReader.java deleted file mode 100644 index 638016ba..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigDecimalArgsReader.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import java.math.BigDecimal; - -public class BigDecimalArgsReader extends ArgsReader { - - @Override - public Class getType() { - return BigDecimal.class; - } - - @Override - public BigDecimal read(String string) { - try { - return new BigDecimal(string); - } catch (NumberFormatException e) { - return null; - } - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigIntegerArgsReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigIntegerArgsReader.java deleted file mode 100644 index bf39e180..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigIntegerArgsReader.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import java.math.BigInteger; - -public class BigIntegerArgsReader extends ArgsReader { - - @Override - public Class getType() { - return BigInteger.class; - } - - @Override - public BigInteger read(String string) { - try { - return new BigInteger(string); - } catch (NumberFormatException e) { - return null; - } - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BooleanReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BooleanReader.java deleted file mode 100644 index ef854c6a..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BooleanReader.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public class BooleanReader extends ArgsReader { - - public static final @NonNull List> TYPES = List.of(boolean.class, Boolean.class); - public static final List SUGGESTIONS = List.of("true", "false"); - - @Override - public Class getType() { - return boolean.class; - } - - @Override - public @NonNull List> getTypes() { - return TYPES; - } - - @Override - public Boolean read(String string) throws ArgsReaderException { - if (string.equalsIgnoreCase("true")) - return true; - if (string.equalsIgnoreCase("false")) - return false; - - return null; - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteArrayReader.java deleted file mode 100644 index ed29be28..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteArrayReader.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -public class ByteArrayReader extends ArgsReader { - - @Override - public Class getType() { - return byte[].class; - } - - @Override - public byte[] read(String string) throws ArgsReaderException { - try { - final String[] strings = string.split(" "); - - byte[] result = new byte[strings.length]; - for (int i = 0; i < result.length; i++) - result[i] = Byte.parseByte(strings[i]); - return result; - } catch (NumberFormatException e) { - throw new ArgsReaderException("expected a byte array in string form but got " + string); - } - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteReader.java deleted file mode 100644 index 6b7e920d..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteReader.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public class ByteReader extends ArgsReader { - - public static final List SUGGESTIONS = List.of("-128", "0", "127"); - public static final @NonNull List> TYPES = List.of(byte.class, Byte.class); - - @Override - public Class getType() { - return byte.class; - } - - @Override - public @NonNull List> getTypes() { - return TYPES; - } - - @Override - public Byte read(String string) throws ArgsReaderException { - try { - return Byte.parseByte(string); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DateReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DateReader.java deleted file mode 100644 index 70f576eb..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DateReader.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.github.sisyphsu.dateparser.DateParserUtils; -import lombok.NonNull; -import org.jetbrains.annotations.Nullable; - -import java.time.format.DateTimeParseException; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -public class DateReader extends ArgsReader { - - //i could do a dedicated system for this, but this one is simpler and works so idc - public static final @NonNull Map> PROVIDERS = new HashMap<>(); - - static { - PROVIDERS.put("now", Date::new); - PROVIDERS.put("today", () -> getToday().getTime()); - PROVIDERS.put("yesterday", () -> { - Calendar today = getToday(); - today.add(Calendar.DAY_OF_YEAR, -1); - return today.getTime(); - }); - PROVIDERS.put("tomorrow", () -> { - Calendar today = getToday(); - today.add(Calendar.DAY_OF_YEAR, 1); - return today.getTime(); - }); - } - - @Override - public @NonNull Class getType() { - return Date.class; - } - - @Override - public @Nullable Date read(@NonNull String string) { - Supplier provider = PROVIDERS.get(string.toLowerCase()); - if (provider != null) - return provider.get(); - - try { - return DateParserUtils.parseDate(string); - } catch (DateTimeParseException e) { - return null; //i need to rework the command system :C - } - } - - public static @NonNull Calendar getToday() { - Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - return calendar; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleArrayReader.java deleted file mode 100644 index 7ebd303f..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleArrayReader.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -public class DoubleArrayReader extends ArgsReader { - - @Override - public Class getType() { - return double[].class; - } - - @Override - public double[] read(String string) throws ArgsReaderException { - try { - final String[] strings = string.split(" "); - - double[] result = new double[strings.length]; - for (int i = 0; i < result.length; i++) - result[i] = Double.parseDouble(strings[i]); - return result; - } catch (NumberFormatException e) { - throw new ArgsReaderException("expected a double array in string form but got " + string); - } - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleReader.java deleted file mode 100644 index 9dd12592..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleReader.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public class DoubleReader extends ArgsReader { - - public static final List SUGGESTIONS = List.of("1", "1.5", "10", "10.5"); - public static final @NonNull List> TYPES = List.of(double.class, Double.class); - - @Override - public Class getType() { - return double.class; - } - - @Override - public @NonNull List> getTypes() { - return TYPES; - } - - @Override - public Double read(String string) throws ArgsReaderException { - try { - return Double.parseDouble(string); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatArrayReader.java deleted file mode 100644 index bdb28e86..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatArrayReader.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -public class FloatArrayReader extends ArgsReader { - - @Override - public Class getType() { - return float[].class; - } - - @Override - public float[] read(String string) throws ArgsReaderException { - try { - final String[] strings = string.split(" "); - - float[] result = new float[strings.length]; - for (int i = 0; i < result.length; i++) - result[i] = Float.parseFloat(strings[i]); - return result; - } catch (NumberFormatException e) { - throw new ArgsReaderException("expected a float array in string form but got " + string); - } - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatReader.java deleted file mode 100644 index 1541cf68..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatReader.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public class FloatReader extends ArgsReader { - - public static final List SUGGESTIONS = List.of("1", "1.5", "10", "10.5"); - public static final @NonNull List> TYPES = List.of(float.class, Float.class); - - @Override - public Class getType() { - return float.class; - } - - @Override - public @NonNull List> getTypes() { - return TYPES; - } - - @Override - public Float read(String string) throws ArgsReaderException { - try { - return Float.parseFloat(string); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerArrayReader.java deleted file mode 100644 index 43f4e59e..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerArrayReader.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -public class IntegerArrayReader extends ArgsReader { - - @Override - public Class getType() { - return int[].class; - } - - @Override - public int[] read(String string) throws ArgsReaderException { - try { - final String[] strings = string.split(" "); - - int[] result = new int[strings.length]; - for (int i = 0; i < result.length; i++) - result[i] = Integer.parseInt(strings[i]); - return result; - } catch (NumberFormatException e) { - throw new ArgsReaderException("expected a int array in string form but got " + string); - } - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerReader.java deleted file mode 100644 index 111c3c09..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerReader.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public class IntegerReader extends ArgsReader { - - public static final List SUGGESTIONS = List.of("1", "10", "100"); - public static final @NonNull List> TYPES = List.of(int.class, Integer.class); - - @Override - public Class getType() { - return int.class; - } - - @Override - public @NonNull List> getTypes() { - return TYPES; - } - - @Override - public Integer read(String string) throws ArgsReaderException { - try { - return Integer.parseInt(string); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortArrayReader.java deleted file mode 100644 index 62315102..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortArrayReader.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -public class ShortArrayReader extends ArgsReader { - - @Override - public Class getType() { - return short[].class; - } - - @Override - public short[] read(String string) throws ArgsReaderException { - try { - final String[] strings = string.split(" "); - - short[] result = new short[strings.length]; - for (int i = 0; i < result.length; i++) - result[i] = Short.parseShort(strings[i]); - return result; - } catch (NumberFormatException e) { - throw new ArgsReaderException("expected a short array in string form but got " + string); - } - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortReader.java deleted file mode 100644 index af95f3ff..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortReader.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public class ShortReader extends ArgsReader { - - public static final List SUGGESTIONS = List.of("-32768", "0", "32767"); - public static final @NonNull List> TYPES = List.of(short.class, Short.class); - - @Override - public Class getType() { - return short.class; - } - - @Override - public @NonNull List> getTypes() { - return TYPES; - } - - @Override - public Short read(String string) throws ArgsReaderException { - try { - return Short.parseShort(string); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/StringReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/StringReader.java deleted file mode 100644 index e6719a67..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/StringReader.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public class StringReader extends ArgsReader { - - public static final List SUGGESTIONS = List.of("dummy", "text"); - - @Override - public Class getType() { - return String.class; - } - - @Override - public String read(String string) { - return string; - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/UUIDReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/UUIDReader.java deleted file mode 100644 index d061710b..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/UUIDReader.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.wizardlybump17.wlib.command.args.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; -import java.util.UUID; - -public class UUIDReader extends ArgsReader { - - public static final List SUGGESTIONS = List.of("00000000-0000-0000-0000-000000000000"); - - @Override - public Class getType() { - return UUID.class; - } - - @Override - public UUID read(String string) throws ArgsReaderException { - try { - return UUID.fromString(string); - } catch (IllegalArgumentException e) { - return null; - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleter.java b/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleter.java deleted file mode 100644 index 187191c0..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleter.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.wizardlybump17.wlib.command.completer; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; - -import java.util.List; - -public interface ArgumentCompleter { - - @NonNull - List complete(@NonNull CommandSender sender, @NonNull String @NonNull [] args); -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleterRegistry.java b/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleterRegistry.java deleted file mode 100644 index 3758074a..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleterRegistry.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wizardlybump17.wlib.command.completer; - -import com.wizardlybump17.wlib.object.Registry; - -public class ArgumentCompleterRegistry extends Registry, ArgumentCompleter> { - - public static final ArgumentCompleterRegistry INSTANCE = new ArgumentCompleterRegistry(); - - private ArgumentCompleterRegistry() { - } -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/context/CommandContext.java b/commands/src/main/java/com/wizardlybump17/wlib/command/context/CommandContext.java new file mode 100644 index 00000000..82a63e15 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/context/CommandContext.java @@ -0,0 +1,66 @@ +package com.wizardlybump17.wlib.command.context; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.*; + +public record CommandContext(@NotNull Command command, @NotNull CommandSender sender, @NotNull CommandNodeArguments arguments, int lastInputIndex, @NotNull CommandNode lastNode) { + + public record CommandNodeArgument(@NotNull CommandNode node, @Nullable String input, @Nullable T data) { + } + + public static final class CommandNodeArguments { + + private final @NotNull @Unmodifiable Map> arguments; + + public CommandNodeArguments(@NotNull List> arguments) { + Map> argumentsMap = new LinkedHashMap<>(); + for (CommandNodeArgument argument : arguments) + argumentsMap.put(argument.node().getName(), argument); + this.arguments = Collections.unmodifiableMap(argumentsMap); + } + + public boolean hasArgument(@NotNull String key) { + return arguments.containsKey(key); + } + + @SuppressWarnings("unchecked") + public @NotNull Optional> getArgument(@NotNull String key) { + return Optional.ofNullable((CommandNodeArgument) arguments.get(key)); + } + + @SuppressWarnings("unchecked") + public @NotNull Optional getArgumentData(@NotNull String key) { + return (Optional) getArgument(key).map(CommandNodeArgument::data); + } + + public @NotNull @Unmodifiable Map> getArguments() { + return arguments; + } + + @Override + public String toString() { + return "CommandNodeArguments{" + + "arguments=" + arguments + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + CommandNodeArguments arguments1 = (CommandNodeArguments) o; + return Objects.equals(arguments, arguments1.arguments); + } + + @Override + public int hashCode() { + return Objects.hash(arguments); + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandExecutionException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandExecutionException.java new file mode 100644 index 00000000..9438ec26 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandExecutionException.java @@ -0,0 +1,23 @@ +package com.wizardlybump17.wlib.command.exception; + +import org.jetbrains.annotations.NotNull; + +public class CommandExecutionException extends RuntimeException { + + public static final @NotNull String MESSAGE = "Error while executing the command %s, index %s, node %s"; + + public CommandExecutionException() { + } + + public CommandExecutionException(@NotNull String message) { + super(message); + } + + public CommandExecutionException(@NotNull String message, @NotNull Throwable cause) { + super(message, cause); + } + + public CommandExecutionException(@NotNull Throwable cause) { + super(cause); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InputParsingException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InputParsingException.java new file mode 100644 index 00000000..9f4c4315 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InputParsingException.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.command.exception; + +import org.jetbrains.annotations.NotNull; + +public class InputParsingException extends Exception { + + public InputParsingException(@NotNull String message) { + super(message); + } + + public InputParsingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InvalidInputException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InvalidInputException.java new file mode 100644 index 00000000..410fa29e --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InvalidInputException.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.command.exception; + +import org.jetbrains.annotations.NotNull; + +public class InvalidInputException extends Exception { + + public InvalidInputException(@NotNull String message) { + super(message); + } + + public InvalidInputException(@NotNull String message, @NotNull Throwable cause) { + super(message, cause); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/SuggesterException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/SuggesterException.java new file mode 100644 index 00000000..92c90364 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/SuggesterException.java @@ -0,0 +1,21 @@ +package com.wizardlybump17.wlib.command.exception; + +import org.jetbrains.annotations.NotNull; + +public class SuggesterException extends Exception { + + public SuggesterException() { + } + + public SuggesterException(@NotNull String message) { + super(message); + } + + public SuggesterException(@NotNull String message, @NotNull Throwable cause) { + super(message, cause); + } + + public SuggesterException(@NotNull Throwable cause) { + super(cause); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/CommandExtractorException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/CommandExtractorException.java new file mode 100644 index 00000000..fb6767aa --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/CommandExtractorException.java @@ -0,0 +1,22 @@ +package com.wizardlybump17.wlib.command.exception.extractor; + +import org.jetbrains.annotations.NotNull; + +public abstract class CommandExtractorException extends Exception { + + public CommandExtractorException() { + super(); + } + + public CommandExtractorException(@NotNull String message) { + super(message); + } + + public CommandExtractorException(@NotNull String message, @NotNull Throwable cause) { + super(message, cause); + } + + public CommandExtractorException(@NotNull Throwable cause) { + super(cause); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/method/MethodCommandNodeFactoryNotFoundException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/method/MethodCommandNodeFactoryNotFoundException.java new file mode 100644 index 00000000..2a3f5195 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/method/MethodCommandNodeFactoryNotFoundException.java @@ -0,0 +1,15 @@ +package com.wizardlybump17.wlib.command.exception.extractor.method; + +import com.wizardlybump17.wlib.command.exception.extractor.CommandExtractorException; +import org.jetbrains.annotations.NotNull; + +public class MethodCommandNodeFactoryNotFoundException extends CommandExtractorException { + + public MethodCommandNodeFactoryNotFoundException() { + super(); + } + + public MethodCommandNodeFactoryNotFoundException(@NotNull String message) { + super(message); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandNodeExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandNodeExecutor.java new file mode 100644 index 00000000..e4bb8599 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandNodeExecutor.java @@ -0,0 +1,11 @@ +package com.wizardlybump17.wlib.command.executor; + +import com.wizardlybump17.wlib.command.context.CommandContext; +import com.wizardlybump17.wlib.command.result.CommandResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface CommandNodeExecutor { + + @Nullable CommandResult execute(@NotNull CommandContext context) throws Throwable; +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/CommandExtractor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/CommandExtractor.java new file mode 100644 index 00000000..d6dc28e4 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/CommandExtractor.java @@ -0,0 +1,20 @@ +package com.wizardlybump17.wlib.command.extractor; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.exception.extractor.CommandExtractorException; +import com.wizardlybump17.wlib.command.extractor.method.MethodCommandExtractor; +import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface CommandExtractor { + + boolean isAccepted(@NotNull Object object); + + @NotNull List extract(@NotNull Object object) throws CommandExtractorException; + + static @NotNull MethodCommandExtractor method(@NotNull MethodCommandNodeFactoryRegistry factoryRegistry) { + return new MethodCommandExtractor(factoryRegistry); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/MethodCommandExtractor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/MethodCommandExtractor.java new file mode 100644 index 00000000..0b599fae --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/MethodCommandExtractor.java @@ -0,0 +1,184 @@ +package com.wizardlybump17.wlib.command.extractor.method; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.context.CommandContext; +import com.wizardlybump17.wlib.command.exception.extractor.method.MethodCommandNodeFactoryNotFoundException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.extractor.CommandExtractor; +import com.wizardlybump17.wlib.command.extractor.method.executor.AbstractMethodCommandNodeExecutor; +import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry; +import com.wizardlybump17.wlib.command.result.CommandResult; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.List; + +public class MethodCommandExtractor implements CommandExtractor { + + private final @NotNull MethodCommandNodeFactoryRegistry factoryRegistry; + + public MethodCommandExtractor(@NotNull MethodCommandNodeFactoryRegistry factoryRegistry) { + this.factoryRegistry = factoryRegistry; + } + + @Override + public boolean isAccepted(@NotNull Object object) { + return true; + } + + public @NotNull MethodCommandNodeFactoryRegistry getFactoryRegistry() { + return factoryRegistry; + } + + @Override + public @NotNull List extract(@NotNull Object object) throws MethodCommandNodeFactoryNotFoundException { + List commands = new ArrayList<>(); + + Class clazz = object.getClass(); + + for (Method method : clazz.getMethods()) { + com.wizardlybump17.wlib.command.annotation.Command annotation = method.getAnnotation(com.wizardlybump17.wlib.command.annotation.Command.class); + if (annotation == null) + continue; + + Parameter[] parameters = method.getParameters(); + Class returnType = method.getReturnType(); + + String[] commandParts = annotation.value().split(" "); + + CommandNode root = null; + + int parameterIndex = parameters.length - 1; + for (int i = commandParts.length - 1; i >= 0; i--) { + String part = commandParts[i]; + + CommandNode oldRoot = root; + root = createNode(factoryRegistry, part, parameters, parameterIndex, root, annotation, object, method); + if (!(root instanceof LiteralCommandNode)) + parameterIndex--; + + if (oldRoot == null) + root = root.withExecutor(createExecutor(object, parameters, returnType, method)); + } + + if (root == null) + throw new IllegalArgumentException(); + + commands.add(new Command((LiteralCommandNode) root)); + } + + return commands; + } + + private static @NotNull CommandNode createNode(@NotNull MethodCommandNodeFactoryRegistry factoryRegistry, @NotNull String part, @NotNull Parameter @NotNull [] parameters, int parameterIndex, @Nullable CommandNode root, @NotNull com.wizardlybump17.wlib.command.annotation.Command annotation, @NotNull Object object, @NotNull Method method) throws MethodCommandNodeFactoryNotFoundException { + CommandNode newNode; + + boolean argument = part.charAt(0) == '<' && part.charAt(part.length() - 1) == '>'; + if (argument) + part = part.substring(1, part.length() - 1); + + if (parameterIndex < 0) { + newNode = new LiteralCommandNode(part, root == null ? List.of() : List.of(root), null, null); + } else { + if (argument) { + Parameter parameter = parameters[parameterIndex]; + Class parameterType = parameter.getType(); + + MethodCommandNodeFactory factory = factoryRegistry.getFactory(parameterType); + if (factory == null) + throw new MethodCommandNodeFactoryNotFoundException("MethodCommandNodeFactory not found for the parameter " + parameter); + + newNode = factory.create(object, method, annotation, parameter, part, root); + } else { + newNode = new LiteralCommandNode(part, root == null ? List.of() : List.of(root), null, null); + } + } + + if (!annotation.permission().isEmpty()) + newNode = newNode.withPermission(annotation.permission()); + + return newNode; + } + + public static @NotNull CommandNodeExecutor createExecutor(@NotNull Object object, @NotNull String methodName, @NotNull Class @NotNull ... parameterTypes) { + try { + Method method = object.getClass().getMethod(methodName, parameterTypes); + return createExecutor(object, method.getParameters(), method.getReturnType(), method); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException(e); + } + } + + + /** + *
    + *
  • If empty -> check the return type;
  • + *
  • If CommandSender only -> pass only the command sender and check the return type;
  • + *
  • If CommandContext only -> pass only the command context and check the return type;
  • + *
  • If CommandSender + more parameters -> pass the command sender and the parameters and check the return type;
  • + *
  • If CommandContext + more parameters -> pass the command context and the parameters and check the return type;
  • + *
  • If only parameters -> pass the parameters and check the return type.
  • + *
+ * + * @param object the object containing the method + * @param parameters the parameters of the method + * @param returnType the return type of the method + * @param method the {@link Method} + * @return the appropriate CommandNodeExecutor based on the parameters and return type of the method + */ + private static @NotNull CommandNodeExecutor createExecutor(@NotNull Object object, @NotNull Parameter @NotNull [] parameters, @NotNull Class returnType, @NotNull Method method) { + if (parameters.length == 0) { //no parameters + if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type + return new AbstractMethodCommandNodeExecutor.NoArgumentsCommandResultExecutor<>(object, method); + } else { + return new AbstractMethodCommandNodeExecutor.NoArgumentsExecutor<>(object, method); + } + } + + Parameter firstParameter = parameters[0]; + Class firstParameterType = firstParameter.getType(); + + if (parameters.length == 1) { + if (CommandSender.class.isAssignableFrom(firstParameterType)) { //CommandSender only + if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type + return new AbstractMethodCommandNodeExecutor.CommandSenderCommandResultExecutor<>(object, method); + } else { //anything else return type + return new AbstractMethodCommandNodeExecutor.CommandSenderExecutor<>(object, method); + } + } else if (CommandContext.class.isAssignableFrom(firstParameterType)) { //CommandContext only + if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type + return new AbstractMethodCommandNodeExecutor.CommandContextCommandResultExecutor<>(object, method); + } else { //anything else return type + return new AbstractMethodCommandNodeExecutor.CommandContextExecutor<>(object, method); + } + } + } + + if (CommandSender.class.isAssignableFrom(firstParameterType)) { //CommandSender + more arguments + if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type + return new AbstractMethodCommandNodeExecutor.CommandSenderAndArgumentsCommandResultExecutor<>(object, method); + } else { //anything else return type + return new AbstractMethodCommandNodeExecutor.CommandSenderAndArgumentsExecutor<>(object, method); + } + } else if (CommandContext.class.isAssignableFrom(firstParameterType)) { //CommandContext + more arguments + if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type + return new AbstractMethodCommandNodeExecutor.CommandContextAndArgumentsCommandResultExecutor<>(object, method); + } else { //anything else return type + return new AbstractMethodCommandNodeExecutor.CommandContextAndArgumentsExecutor<>(object, method); + } + } else { + if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type + return new AbstractMethodCommandNodeExecutor.ArgumentsCommandResultExecutor<>(object, method); + } else { //anything else return type + return new AbstractMethodCommandNodeExecutor.ArgumentsExecutor<>(object, method); + } + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/AbstractMethodCommandNodeExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/AbstractMethodCommandNodeExecutor.java new file mode 100644 index 00000000..221b0bbe --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/AbstractMethodCommandNodeExecutor.java @@ -0,0 +1,295 @@ +package com.wizardlybump17.wlib.command.extractor.method.executor; + +import com.wizardlybump17.wlib.command.context.CommandContext; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.result.CommandResult; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@ApiStatus.Internal +public abstract sealed class AbstractMethodCommandNodeExecutor implements MethodCommandNodeExecutor { + + private final @NotNull Object object; + private final @NotNull MethodHandle methodHandle; + private final @NotNull Method method; + + public AbstractMethodCommandNodeExecutor(@NotNull Object object, @NotNull Method method) { + this.object = object; + try { + this.methodHandle = MethodHandles.publicLookup().findVirtual(object.getClass(), method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes())); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new IllegalArgumentException(e); + } + this.method = method; + } + + @Override + public @NotNull Object object() { + return object; + } + + @Override + public @NotNull MethodHandle methodHandle() { + return methodHandle; + } + + @Override + public @NotNull Method method() { + return method; + } + + @Override + public boolean equals(Object object1) { + if (object1 == null || getClass() != object1.getClass()) + return false; + AbstractMethodCommandNodeExecutor that = (AbstractMethodCommandNodeExecutor) object1; + return Objects.equals(object, that.object) + && Objects.equals(method, that.method); + } + + @Override + public int hashCode() { + return Objects.hash(object, method); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "object=" + object + + ", methodHandle=" + methodHandle + + ", method=" + method + + '}'; + } + + @ApiStatus.Internal + public static final class CommandSenderCommandResultExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandSenderCommandResultExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context.sender()); + return (CommandResult) methodHandle().invokeWithArguments(parameters); + } + } + + @ApiStatus.Internal + public static final class CommandSenderExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandSenderExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable{ + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context.sender()); + return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters)); + } + } + + @ApiStatus.Internal + public static final class CommandContextCommandResultExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandContextCommandResultExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context); + return (CommandResult) methodHandle().invokeWithArguments(parameters); + } + } + + @ApiStatus.Internal + public static final class CommandContextExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandContextExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context); + return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters)); + } + } + + @ApiStatus.Internal + public static final class CommandSenderAndArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandSenderAndArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context.sender()); + context.arguments().getArguments().forEach((nodeName, argument) -> { + if (!(argument.node() instanceof LiteralCommandNode)) + parameters.add(argument.data()); + }); + return (CommandResult) methodHandle().invokeWithArguments(parameters); + } + } + + @ApiStatus.Internal + public static final class CommandSenderAndArgumentsExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandSenderAndArgumentsExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context.sender()); + context.arguments().getArguments().forEach((nodeName, argument) -> { + if (!(argument.node() instanceof LiteralCommandNode)) + parameters.add(argument.data()); + }); + return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters)); + } + } + + @ApiStatus.Internal + public static final class CommandContextAndArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandContextAndArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context); + context.arguments().getArguments().forEach((nodeName, argument) -> { + if (!(argument.node() instanceof LiteralCommandNode)) + parameters.add(argument.data()); + }); + return (CommandResult) methodHandle().invokeWithArguments(parameters); + } + } + + @ApiStatus.Internal + public static final class CommandContextAndArgumentsExecutor extends AbstractMethodCommandNodeExecutor { + + public CommandContextAndArgumentsExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + parameters.add(context); + context.arguments().getArguments().forEach((nodeName, argument) -> { + if (!(argument.node() instanceof LiteralCommandNode)) + parameters.add(argument.data()); + }); + return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters)); + } + } + + @ApiStatus.Internal + public static final class ArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor { + + public ArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + context.arguments().getArguments().forEach((nodeName, argument) -> { + if (!(argument.node() instanceof LiteralCommandNode)) + parameters.add(argument.data()); + }); + return (CommandResult) methodHandle().invokeWithArguments(parameters); + } + } + + @ApiStatus.Internal + public static final class ArgumentsExecutor extends AbstractMethodCommandNodeExecutor { + + public ArgumentsExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + context.arguments().getArguments().forEach((nodeName, argument) -> { + if (!(argument.node() instanceof LiteralCommandNode)) + parameters.add(argument.data()); + }); + return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters)); + } + } + + @ApiStatus.Internal + public static final class NoArgumentsExecutor extends AbstractMethodCommandNodeExecutor { + + public NoArgumentsExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters)); + } + } + + @ApiStatus.Internal + public static final class NoArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor { + + public NoArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) { + super(object, method); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable { + List parameters = new ArrayList<>(); + parameters.add(object()); + return (CommandResult) methodHandle().invokeWithArguments(parameters); + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/MethodCommandNodeExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/MethodCommandNodeExecutor.java new file mode 100644 index 00000000..9b814159 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/MethodCommandNodeExecutor.java @@ -0,0 +1,16 @@ +package com.wizardlybump17.wlib.command.extractor.method.executor; + +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import org.jetbrains.annotations.NotNull; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Method; + +public sealed interface MethodCommandNodeExecutor extends CommandNodeExecutor permits AbstractMethodCommandNodeExecutor { + + @NotNull Object object(); + + @NotNull MethodHandle methodHandle(); + + @NotNull Method method(); +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/MethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/MethodCommandNodeFactory.java new file mode 100644 index 00000000..9ed8be17 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/MethodCommandNodeFactory.java @@ -0,0 +1,34 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.node.CommandNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +public abstract class MethodCommandNodeFactory { + + public abstract @NotNull CommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root); + + public abstract @NotNull Class @NotNull [] getSupportedTypes(); + + public boolean isSupported(@NotNull Class type) { + if (isStrict()) { + for (Class supportedType : getSupportedTypes()) + if (supportedType.equals(type)) + return true; + return false; + } + + for (Class supportedType : getSupportedTypes()) + if (supportedType.isAssignableFrom(type)) + return true; + return false; + } + + public boolean isStrict() { + return true; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/InstantCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/InstantCommandNode.java new file mode 100644 index 00000000..36030ef2 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/InstantCommandNode.java @@ -0,0 +1,46 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.object; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.List; + +public class InstantCommandNode extends CommandNode { + + public InstantCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @Nullable Instant parse(@NotNull String input) throws InputParsingException { + if (input.equalsIgnoreCase("null")) + return null; + try { + return Instant.parse(input); + } catch (DateTimeParseException e) { + throw new InputParsingException("Could not parse as Instant: " + input, e); + } + } + + @Override + public @NotNull InstantCommandNode withChildren(@NotNull List> children) { + return new InstantCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull InstantCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new InstantCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull InstantCommandNode withPermission(@Nullable String permission) { + return new InstantCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/JsonElementMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/JsonElementMethodCommandNodeFactory.java new file mode 100644 index 00000000..8c9072a3 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/JsonElementMethodCommandNodeFactory.java @@ -0,0 +1,52 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.object; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.annotation.NonNullInput; +import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.object.JsonElementCommandNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; + +public class JsonElementMethodCommandNodeFactory extends MethodCommandNodeFactory { + + private final @NotNull Gson gson; + + public JsonElementMethodCommandNodeFactory(@NotNull Gson gson) { + this.gson = gson; + } + + @Override + public @NotNull JsonElementCommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + return new JsonElementCommandNode( + name, + root == null ? List.of() : List.of(root), + parameter.isAnnotationPresent(NonNullInput.class) ? AllowedInputs.anyNotNull() : AllowedInputs.anyNullable(), + null, + null, + null, + gson + ); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] {JsonElement.class}; + } + + @Override + public boolean isStrict() { + return false; + } + + public @NotNull Gson getGson() { + return gson; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/UUIDMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/UUIDMethodCommandNodeFactory.java new file mode 100644 index 00000000..cb3817c4 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/UUIDMethodCommandNodeFactory.java @@ -0,0 +1,38 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.object; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.annotation.NonNullInput; +import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.input.object.AllowedUUIDInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.object.UUIDCommandNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.UUID; + +public class UUIDMethodCommandNodeFactory extends MethodCommandNodeFactory { + + @Override + public @NotNull CommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + if (!isSupported(parameter.getType())) + throw new IllegalArgumentException("Unsupported type. We only accept UUIDs: " + parameter); + + return new UUIDCommandNode( + name, + root == null ? List.of() : List.of(root), + parameter.isAnnotationPresent(NonNullInput.class) ? AllowedUUIDInputs.anyNotNull() : AllowedUUIDInputs.anyNullable(), + null, + null, + null + ); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] {UUID.class}; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/BooleanMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/BooleanMethodCommandNodeFactory.java new file mode 100644 index 00000000..9cbe2856 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/BooleanMethodCommandNodeFactory.java @@ -0,0 +1,37 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.primitive; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.input.primitive.AllowedBooleanInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.BooleanCommandNode; +import com.wizardlybump17.wlib.command.suggestion.primitive.BooleanSuggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; + +public class BooleanMethodCommandNodeFactory extends PrimitiveMethodCommandNodeFactory { + + @Override + public @NotNull BooleanCommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + Class type = parameter.getType(); + if (isSupported(type)) + throw new IllegalArgumentException("Unsupported type. We only accept booleans: " + type); + + return new BooleanCommandNode( + name, + root == null ? List.of() : List.of(root), + AllowedBooleanInputs.anyNotNull(), + BooleanSuggester.any(), + null, + null + ); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] {boolean.class, Boolean.class}; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/CharacterMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/CharacterMethodCommandNodeFactory.java new file mode 100644 index 00000000..cd662956 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/CharacterMethodCommandNodeFactory.java @@ -0,0 +1,37 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.primitive; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.input.primitive.AllowedCharacterInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.AbstractPrimitiveCommandNode; +import com.wizardlybump17.wlib.command.node.primitive.CharacterCommandNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; + +public class CharacterMethodCommandNodeFactory extends PrimitiveMethodCommandNodeFactory { + + @Override + public @NotNull AbstractPrimitiveCommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + Class type = parameter.getType(); + if (!isSupported(type)) + throw new IllegalArgumentException("Unsupported type. We only accept chars: " + type); + + return new CharacterCommandNode( + name, + root == null ? List.of() : List.of(root), + AllowedCharacterInputs.anyNotNull(), + null, + null, + null + ); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] {char.class, Character.class}; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/NumberMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/NumberMethodCommandNodeFactory.java new file mode 100644 index 00000000..2210ae5d --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/NumberMethodCommandNodeFactory.java @@ -0,0 +1,100 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.primitive; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.input.primitive.number.*; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.number.*; +import com.wizardlybump17.wlib.command.suggestion.primitive.number.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; + +public class NumberMethodCommandNodeFactory extends PrimitiveMethodCommandNodeFactory { + + @Override + public @NotNull NumberCommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + Class type = parameter.getType(); + if (!isSupported(type)) + throw new IllegalArgumentException("Unsupported type. We accept only primitive numbers: " + type); + + List> children = root == null ? List.of() : List.of(root); + + if (type == byte.class || type == Byte.class) { + return new ByteCommandNode( + name, + children, + AllowedByteInputs.unlimited(), + ByteSuggester.unlimited(), + null, + null + ); + } + if (type == short.class || type == Short.class) { + return new ShortCommandNode( + name, + children, + AllowedShortInputs.unlimited(), + ShortSuggester.unlimited(), + null, + null + ); + } + if (type == int.class || type == Integer.class) { + return new IntegerCommandNode( + name, + children, + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ); + } + if (type == long.class || type == Long.class) { + return new LongCommandNode( + name, + children, + AllowedLongInputs.unlimited(), + LongSuggester.unlimited(), + null, + null + ); + } + if (type == float.class || type == Float.class) { + return new FloatCommandNode( + name, + children, + AllowedFloatInputs.unlimited(), + FloatSuggester.unlimited(), + null, + null + ); + } + if (type == double.class || type == Double.class) { + return new DoubleCommandNode( + name, + children, + AllowedDoubleInputs.unlimited(), + DoubleSuggester.unlimited(), + null, + null + ); + } + + throw new IllegalArgumentException("Unsupported type. We accept only primitive numbers: " + type); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] { + byte.class, Byte.class, + short.class, Short.class, + int.class, Integer.class, + long.class, Long.class, + float.class, Float.class, + double.class, Double.class + }; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/PrimitiveMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/PrimitiveMethodCommandNodeFactory.java new file mode 100644 index 00000000..1ded737b --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/PrimitiveMethodCommandNodeFactory.java @@ -0,0 +1,16 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.primitive; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.node.CommandNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +public abstract class PrimitiveMethodCommandNodeFactory extends MethodCommandNodeFactory { + + @Override + public abstract @NotNull CommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root); +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/string/StringMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/string/StringMethodCommandNodeFactory.java new file mode 100644 index 00000000..eb29fc96 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/string/StringMethodCommandNodeFactory.java @@ -0,0 +1,39 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory.string; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.annotation.NonNullInput; +import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.input.string.AllowedStringInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.string.StringCommandNode; +import com.wizardlybump17.wlib.command.suggestion.string.StringSuggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; + +public class StringMethodCommandNodeFactory extends MethodCommandNodeFactory { + + @Override + public @NotNull CommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + Class type = parameter.getType(); + if (!isSupported(type)) + throw new IllegalArgumentException("Unsupported type. We accept only Strings: " + type); + + return new StringCommandNode( + name, + root == null ? List.of() : List.of(root), + parameter.isAnnotationPresent(NonNullInput.class) ? AllowedStringInputs.anyNotNull() : AllowedStringInputs.anyNullable(), + StringSuggester.any(), + null, + null + ); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] {String.class}; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/Command.java b/commands/src/main/java/com/wizardlybump17/wlib/command/holder/Command.java deleted file mode 100644 index e766809d..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/Command.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.wizardlybump17.wlib.command.holder; - -import com.wizardlybump17.wlib.command.CommandManager; - -public interface Command { - - void setExecutor(CommandExecutor executor); - - CommandExecutor getExecutor(); - - /** - *

Used by the {@link com.wizardlybump17.wlib.command.CommandManager} when creating a new command.

- *

This is used to set the default command executor.

- * @param manager the command manager - * @param name the command name - * @return the default command executor - */ - CommandExecutor getDefaultExecutor(CommandManager manager, String name); -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandExecutor.java deleted file mode 100644 index de8e1475..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandExecutor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.wizardlybump17.wlib.command.holder; - -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException; - -/** - * Interface for intercepting commands when they are called - */ -public interface CommandExecutor { - - void execute(CommandSender sender, String commandName, String[] args) throws ArgsReaderException; -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandHolder.java b/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandHolder.java deleted file mode 100644 index c5c8e0ef..00000000 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandHolder.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.wizardlybump17.wlib.command.holder; - -import lombok.NonNull; -import org.jetbrains.annotations.Nullable; - -import java.util.logging.Logger; - -/** - * Represents something that can hold commands - * @param The holder type - */ -public interface CommandHolder { - - @Nullable Command getCommand(String name); - - @NonNull H getHandle(); - - @NonNull - Logger getLogger(); -} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedInputs.java new file mode 100644 index 00000000..afa40388 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedInputs.java @@ -0,0 +1,67 @@ +package com.wizardlybump17.wlib.command.input; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public interface AllowedInputs { + + boolean isAllowed(@Nullable T input); + + @SuppressWarnings("unchecked") + static @NotNull Any anyNullable() { + return (Any) Any.NULLABLE; + } + + @SuppressWarnings("unchecked") + static @NotNull Any anyNotNull() { + return (Any) Any.NOT_NULL; + } + + static AllowedListInputs.@NotNull Values values(@NotNull List values) { + return new AllowedListInputs.Values<>(values); + } + + final class Any implements AllowedInputs { + + private static final @NotNull Any NULLABLE = new Any<>(true); + private static final @NotNull Any NOT_NULL = new Any<>(false); + + private final boolean nullable; + + Any(boolean nullable) { + this.nullable = nullable; + } + + @Override + public boolean isAllowed(@Nullable T input) { + return nullable || input != null; + } + + public boolean nullable() { + return nullable; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + Any any = (Any) o; + return nullable == any.nullable; + } + + @Override + public int hashCode() { + return Objects.hashCode(nullable); + } + + @Override + public String toString() { + return "AllowedInputs$Any{" + + "nullable=" + nullable + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedListInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedListInputs.java new file mode 100644 index 00000000..5a6a4fbb --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedListInputs.java @@ -0,0 +1,53 @@ +package com.wizardlybump17.wlib.command.input; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public interface AllowedListInputs extends AllowedInputs { + + @NotNull List allowedValues(); + + @Override + default boolean isAllowed(@Nullable T input) { + if (input == null) + return false; + return allowedValues().contains(input); + } + + final class Values implements AllowedListInputs { + + private final @NotNull List values; + + Values(@NotNull List values) { + this.values = List.copyOf(values); + } + + @Override + public @NotNull List allowedValues() { + return values; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + Values values1 = (Values) o; + return Objects.equals(values, values1.values); + } + + @Override + public int hashCode() { + return Objects.hashCode(values); + } + + @Override + public String toString() { + return "AllowedListInputs$Values{" + + "values=" + values + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/RangedAllowedInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/RangedAllowedInputs.java new file mode 100644 index 00000000..2cf43936 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/RangedAllowedInputs.java @@ -0,0 +1,12 @@ +package com.wizardlybump17.wlib.command.input; + +import org.jetbrains.annotations.NotNull; + +public interface RangedAllowedInputs extends AllowedInputs { + + @NotNull T from(); + + @NotNull T to(); + + boolean isInRange(@NotNull T input); +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/SingleValueInput.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/SingleValueInput.java new file mode 100644 index 00000000..a78c8a29 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/SingleValueInput.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.command.input; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface SingleValueInput extends AllowedInputs { + + @NotNull T value(); + + @Override + default boolean isAllowed(@Nullable T input) { + return value().equals(input); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/object/AllowedUUIDInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/object/AllowedUUIDInputs.java new file mode 100644 index 00000000..3dc4e768 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/object/AllowedUUIDInputs.java @@ -0,0 +1,139 @@ +package com.wizardlybump17.wlib.command.input.object; + +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.input.AllowedListInputs; +import com.wizardlybump17.wlib.command.input.SingleValueInput; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +public interface AllowedUUIDInputs extends AllowedInputs { + + static @NotNull Value value(@NotNull UUID value) { + return new Value(value); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(List.copyOf(values)); + } + + static @NotNull Values values(@NotNull UUID @NotNull ... values) { + return new Values(List.of(values)); + } + + static @NotNull Any anyNullable() { + return Any.NULLABLE; + } + + static @NotNull Any anyNotNull() { + return Any.NOT_NULL; + } + + final class Value implements AllowedUUIDInputs, SingleValueInput { + + private final @NotNull UUID value; + + Value(@NotNull UUID value) { + this.value = value; + } + + @Override + public @NotNull UUID value() { + return value; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Value value1 = (Value) object; + return Objects.equals(value, value1.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public String toString() { + return "AllowedUUIDInputs$Value{" + + "value=" + value + + '}'; + } + } + + final class Values implements AllowedUUIDInputs, AllowedListInputs { + + private final @NotNull List values; + + private Values(@NotNull List values) { + this.values = values; + } + + @Override + public @NotNull List allowedValues() { + return values; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedUUIDInputs.Values values1 = (AllowedUUIDInputs.Values) object; + return Objects.equals(values, values1.values); + } + + @Override + public int hashCode() { + return Objects.hashCode(values); + } + + @Override + public String toString() { + return "AllowedUUIDInputs$Values{" + + "values=" + values + + '}'; + } + } + + final class Any implements AllowedUUIDInputs { + + private static final @NotNull AllowedUUIDInputs.Any NULLABLE = new AllowedUUIDInputs.Any(true); + private static final @NotNull AllowedUUIDInputs.Any NOT_NULL = new AllowedUUIDInputs.Any(false); + + private final boolean nullable; + + private Any(boolean nullable) { + this.nullable = nullable; + } + + @Override + public boolean isAllowed(@Nullable UUID input) { + return nullable || input != null; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedUUIDInputs.Any any = (AllowedUUIDInputs.Any) object; + return nullable == any.nullable; + } + + @Override + public int hashCode() { + return Objects.hashCode(nullable); + } + + @Override + public String toString() { + return "AllowedUUIDInputs$Any{" + + "nullable=" + nullable + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedBooleanInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedBooleanInputs.java new file mode 100644 index 00000000..154211a6 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedBooleanInputs.java @@ -0,0 +1,103 @@ +package com.wizardlybump17.wlib.command.input.primitive; + +import com.wizardlybump17.wlib.command.input.SingleValueInput; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public interface AllowedBooleanInputs extends PrimitiveAllowedInputs { + + static @NotNull True trueValue() { + return True.INSTANCE; + } + + static @NotNull False falseValue() { + return False.INSTANCE; + } + + static @NotNull Any anyNullable() { + return Any.NULLABLE; + } + + static @NotNull Any anyNotNull() { + return Any.NOT_NULL; + } + + final class True implements AllowedBooleanInputs, SingleValueInput { + + private static final @NotNull True INSTANCE = new True(); + + private True() { + } + + @Override + public @NotNull Boolean value() { + return true; + } + + @Override + public String toString() { + return "AllowedBooleanInputs$True{}"; + } + } + + final class False implements AllowedBooleanInputs, SingleValueInput { + + private static final @NotNull False INSTANCE = new False(); + + private False() { + } + + @Override + public @NotNull Boolean value() { + return false; + } + + @Override + public String toString() { + return "AllowedBooleanInputs$False{}"; + } + } + + final class Any implements AllowedBooleanInputs { + + private static final @NotNull Any NULLABLE = new Any(true); + private static final @NotNull Any NOT_NULL = new Any(false); + + private final boolean nullable; + + private Any(boolean nullable) { + this.nullable = nullable; + } + + public boolean nullable() { + return nullable; + } + + @Override + public boolean isAllowed(@Nullable Boolean input) { + return nullable || input != null; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Any any = (Any) object; + return nullable == any.nullable; + } + + @Override + public int hashCode() { + return Objects.hashCode(nullable); + } + + @Override + public String toString() { + return "AllowedBooleanInputs$Any{" + + "nullable=" + nullable + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedCharacterInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedCharacterInputs.java new file mode 100644 index 00000000..79046455 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedCharacterInputs.java @@ -0,0 +1,190 @@ +package com.wizardlybump17.wlib.command.input.primitive; + +import com.wizardlybump17.wlib.command.input.AllowedListInputs; +import com.wizardlybump17.wlib.command.input.SingleValueInput; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public interface AllowedCharacterInputs extends PrimitiveAllowedInputs { + + static @NotNull Value value(char value) { + return new Value(value, false); + } + + static @NotNull Value valueIgnoreCase(char value) { + return new Value(value, true); + } + + static @NotNull Values values(@NotNull List values) { + values = List.copyOf(values); + return new Values(values, values, false); + } + + static @NotNull Values values(@NotNull Character @NotNull ... values) { + List valuesList = List.of(values); + return new Values(valuesList, valuesList, false); + } + + static @NotNull Values valuesIgnoreCase(@NotNull List values) { + return new Values( + List.copyOf(values), + values.stream() + .map(Character::toLowerCase) + .collect(Collectors.toList()), + true + ); + } + + static @NotNull Any anyNullable() { + return Any.NULLABLE; + } + + static @NotNull Any anyNotNull() { + return Any.NOT_NULL; + } + + final class Value implements AllowedCharacterInputs, SingleValueInput { + + private final char value; + private final boolean ignoreCase; + + private Value(char value, boolean ignoreCase) { + this.value = value; + this.ignoreCase = ignoreCase; + } + + @Override + public boolean isAllowed(@Nullable Character input) { + if (input == null) + return false; + return ignoreCase ? Character.toLowerCase(input) == Character.toLowerCase(value) : input == value; + } + + @Override + public @NotNull Character value() { + return value; + } + + public boolean ignoreCase() { + return ignoreCase; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Value value1 = (Value) object; + return value == value1.value && ignoreCase == value1.ignoreCase; + } + + @Override + public int hashCode() { + return Objects.hash(value, ignoreCase); + } + + @Override + public String toString() { + return "AllowedCharacterInputs$Value{" + + "value=" + value + + ", ignoreCase=" + ignoreCase + + '}'; + } + } + + final class Values implements AllowedCharacterInputs, AllowedListInputs { + + private final @NotNull List values; + private final @NotNull List toCheck; + private final boolean ignoreCase; + + private Values(@NotNull List values, @NotNull List toCheck, boolean ignoreCase) { + this.values = values; + this.toCheck = toCheck; + this.ignoreCase = ignoreCase; + } + + @Override + public @NotNull List allowedValues() { + return values; + } + + public boolean isIgnoreCase() { + return ignoreCase; + } + + @Override + public boolean isAllowed(@Nullable Character input) { + if (input == null) + return false; + if (ignoreCase) + return toCheck.contains(Character.toLowerCase(input)); + return toCheck.contains(input); + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedCharacterInputs.Values values1 = (AllowedCharacterInputs.Values) object; + return ignoreCase == values1.ignoreCase && Objects.equals(values, values1.values) && Objects.equals(toCheck, values1.toCheck); + } + + @Override + public int hashCode() { + return Objects.hash(values, toCheck, ignoreCase); + } + + @Override + public String toString() { + return "AllowedCharacterInputs$Values{" + + "values=" + values + + ", ignoreCase=" + ignoreCase + + '}'; + } + } + + final class Any implements AllowedCharacterInputs { + + private static final @NotNull Any NULLABLE = new Any(true); + private static final @NotNull Any NOT_NULL = new Any(false); + + private final boolean nullable; + + private Any(boolean nullable) { + this.nullable = nullable; + } + + public boolean nullable() { + return nullable; + } + + @Override + public boolean isAllowed(@Nullable Character input) { + return nullable || input != null; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Any any = (Any) object; + return nullable == any.nullable; + } + + @Override + public int hashCode() { + return Objects.hashCode(nullable); + } + + @Override + public String toString() { + return "AllowedCharacterInputs$Any{" + + "nullable=" + nullable + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/PrimitiveAllowedInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/PrimitiveAllowedInputs.java new file mode 100644 index 00000000..4c815ba3 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/PrimitiveAllowedInputs.java @@ -0,0 +1,6 @@ +package com.wizardlybump17.wlib.command.input.primitive; + +import com.wizardlybump17.wlib.command.input.AllowedInputs; + +public interface PrimitiveAllowedInputs

extends AllowedInputs

{ +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedByteInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedByteInputs.java new file mode 100644 index 00000000..93d349d9 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedByteInputs.java @@ -0,0 +1,147 @@ +package com.wizardlybump17.wlib.command.input.primitive.number; + +import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface AllowedByteInputs extends PrimitiveAllowedInputs { + + static @NotNull Range range(byte from, byte to) { + return new Range(from, to); + } + + static @NotNull Unlimited unlimited() { + return Unlimited.INSTANCE; + } + + static @NotNull Positive positive() { + return Positive.INSTANCE; + } + + static @NotNull Negative negative() { + return Negative.INSTANCE; + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Value value(byte value) { + return new Value(value); + } + + final class Range extends AllowedNumberInputs.Ranged implements AllowedByteInputs { + + Range(byte from, byte to) { + super(from, to); + if (from >= to) + throw new IllegalArgumentException("from must be less than to"); + } + + @Override + public String toString() { + return "AllowedByteInputs$Range{" + + "from=" + from() + + ", to=" + to() + + "}"; + } + } + + final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedByteInputs { + + static final @NotNull AllowedByteInputs.Unlimited INSTANCE = new AllowedByteInputs.Unlimited(); + + private Unlimited() { + } + + @Override + public String toString() { + return "AllowedByteInputs$Unlimited{}"; + } + } + + final class Positive extends AllowedNumberInputs.Positive implements AllowedByteInputs { + + static final @NotNull AllowedByteInputs.Positive INSTANCE = new AllowedByteInputs.Positive(); + + private Positive() { + } + + @Override + public @NotNull Byte from() { + return 0; + } + + @Override + public @NotNull Byte to() { + return Byte.MAX_VALUE; + } + + @Override + public boolean isInRange(@NotNull Byte input) { + return input > -1; + } + + @Override + public String toString() { + return "AllowedByteInputs$Positive{}"; + } + } + + final class Negative extends AllowedNumberInputs.Negative implements AllowedByteInputs { + + static final @NotNull AllowedByteInputs.Negative INSTANCE = new AllowedByteInputs.Negative(); + + private Negative() { + } + + @Override + public @NotNull Byte from() { + return -1; + } + + @Override + public @NotNull Byte to() { + return Byte.MIN_VALUE; + } + + @Override + public boolean isInRange(@NotNull Byte input) { + return input < 0; + } + + @Override + public String toString() { + return "AllowedByteInputs$Negative{}"; + } + } + + final class Value extends AllowedNumberInputs.Value implements AllowedByteInputs { + + Value(byte value) { + super(value); + } + + @Override + public String toString() { + return "AllowedByteInputs$Value{" + + "value=" + value() + + '}'; + } + } + + final class Values extends AllowedNumberInputs.Values implements AllowedByteInputs { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "AllowedByteInputs$Values{" + + "values=" + allowedValues() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedDoubleInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedDoubleInputs.java new file mode 100644 index 00000000..e4124abc --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedDoubleInputs.java @@ -0,0 +1,147 @@ +package com.wizardlybump17.wlib.command.input.primitive.number; + +import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface AllowedDoubleInputs extends PrimitiveAllowedInputs { + + static @NotNull Range range(double from, double to) { + return new Range(from, to); + } + + static @NotNull Unlimited unlimited() { + return Unlimited.INSTANCE; + } + + static @NotNull Positive positive() { + return Positive.INSTANCE; + } + + static @NotNull Negative negative() { + return Negative.INSTANCE; + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Value value(double value) { + return new Value(value); + } + + final class Range extends AllowedNumberInputs.Ranged implements AllowedDoubleInputs { + + Range(double from, double to) { + super(from, to); + if (from >= to) + throw new IllegalArgumentException("from must be less than to"); + } + + @Override + public String toString() { + return "AllowedByteInputs$Range{" + + "from=" + from() + + ", to=" + to() + + "}"; + } + } + + final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedDoubleInputs { + + static final @NotNull AllowedDoubleInputs.Unlimited INSTANCE = new AllowedDoubleInputs.Unlimited(); + + private Unlimited() { + } + + @Override + public String toString() { + return "AllowedDoubleInputs$Unlimited{}"; + } + } + + final class Positive extends AllowedNumberInputs.Positive implements AllowedDoubleInputs { + + static final @NotNull AllowedDoubleInputs.Positive INSTANCE = new AllowedDoubleInputs.Positive(); + + private Positive() { + } + + @Override + public @NotNull Double from() { + return 0.0; + } + + @Override + public @NotNull Double to() { + return Double.MAX_VALUE; + } + + @Override + public boolean isInRange(@NotNull Double input) { + return input > -1; + } + + @Override + public String toString() { + return "AllowedDoubleInputs$Positive{}"; + } + } + + final class Negative extends AllowedNumberInputs.Negative implements AllowedDoubleInputs { + + static final @NotNull AllowedDoubleInputs.Negative INSTANCE = new AllowedDoubleInputs.Negative(); + + private Negative() { + } + + @Override + public @NotNull Double from() { + return -1.0; + } + + @Override + public @NotNull Double to() { + return Double.MIN_VALUE; + } + + @Override + public boolean isInRange(@NotNull Double input) { + return input < 0; + } + + @Override + public String toString() { + return "AllowedDoubleInputs$Negative{}"; + } + } + + final class Value extends AllowedNumberInputs.Value implements AllowedDoubleInputs { + + Value(double value) { + super(value); + } + + @Override + public String toString() { + return "AllowedDoubleInputs$Value{" + + "value=" + value() + + "}"; + } + } + + final class Values extends AllowedNumberInputs.Values implements AllowedDoubleInputs { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "AllowedDoubleInputs$Values{" + + "values=" + allowedValues() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedFloatInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedFloatInputs.java new file mode 100644 index 00000000..f1c606a3 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedFloatInputs.java @@ -0,0 +1,147 @@ +package com.wizardlybump17.wlib.command.input.primitive.number; + +import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface AllowedFloatInputs extends PrimitiveAllowedInputs { + + static @NotNull Range range(float from, float to) { + return new Range(from, to); + } + + static @NotNull Unlimited unlimited() { + return Unlimited.INSTANCE; + } + + static @NotNull Positive positive() { + return Positive.INSTANCE; + } + + static @NotNull Negative negative() { + return Negative.INSTANCE; + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Value value(float value) { + return new Value(value); + } + + final class Range extends AllowedNumberInputs.Ranged implements AllowedFloatInputs { + + Range(float from, float to) { + super(from, to); + if (from >= to) + throw new IllegalArgumentException("from must be less than to"); + } + + @Override + public String toString() { + return "AllowedFloatInputs$Range{" + + "from=" + from() + + ", to=" + to() + + "}"; + } + } + + final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedFloatInputs { + + static final @NotNull AllowedFloatInputs.Unlimited INSTANCE = new AllowedFloatInputs.Unlimited(); + + private Unlimited() { + } + + @Override + public String toString() { + return "AllowedFloatInputs$Unlimited{}"; + } + } + + final class Positive extends AllowedNumberInputs.Positive implements AllowedFloatInputs { + + static final @NotNull AllowedFloatInputs.Positive INSTANCE = new AllowedFloatInputs.Positive(); + + private Positive() { + } + + @Override + public @NotNull Float from() { + return 0.0F; + } + + @Override + public @NotNull Float to() { + return Float.MAX_VALUE; + } + + @Override + public boolean isInRange(@NotNull Float input) { + return input > -1; + } + + @Override + public String toString() { + return "AllowedFloatInputs$Positive{}"; + } + } + + final class Negative extends AllowedNumberInputs.Negative implements AllowedFloatInputs { + + static final @NotNull AllowedFloatInputs.Negative INSTANCE = new AllowedFloatInputs.Negative(); + + private Negative() { + } + + @Override + public @NotNull Float from() { + return -1.0F; + } + + @Override + public @NotNull Float to() { + return Float.MIN_VALUE; + } + + @Override + public boolean isInRange(@NotNull Float input) { + return input < 0; + } + + @Override + public String toString() { + return "AllowedFloatInputs$Negative{}"; + } + } + + final class Value extends AllowedNumberInputs.Value implements AllowedFloatInputs { + + Value(float value) { + super(value); + } + + @Override + public String toString() { + return "AllowedFloatInputs$Value{" + + "value=" + value() + + "}"; + } + } + + final class Values extends AllowedNumberInputs.Values implements AllowedFloatInputs { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "AllowedFloatInputs$Values{" + + "values=" + allowedValues() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedIntegerInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedIntegerInputs.java new file mode 100644 index 00000000..87a2648b --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedIntegerInputs.java @@ -0,0 +1,147 @@ +package com.wizardlybump17.wlib.command.input.primitive.number; + +import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface AllowedIntegerInputs extends PrimitiveAllowedInputs { + + static @NotNull Range range(int from, int to) { + return new Range(from, to); + } + + static @NotNull Unlimited unlimited() { + return Unlimited.INSTANCE; + } + + static @NotNull Positive positive() { + return Positive.INSTANCE; + } + + static @NotNull Negative negative() { + return Negative.INSTANCE; + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Value value(int value) { + return new Value(value); + } + + final class Range extends AllowedNumberInputs.Ranged implements AllowedIntegerInputs { + + Range(int from, int to) { + super(from, to); + if (from >= to) + throw new IllegalArgumentException("from must be less than to"); + } + + @Override + public String toString() { + return "AllowedIntegerInputs$Range{" + + "from=" + from() + + ", to=" + to() + + "}"; + } + } + + final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedIntegerInputs{ + + static final @NotNull AllowedIntegerInputs.Unlimited INSTANCE = new AllowedIntegerInputs.Unlimited(); + + private Unlimited() { + } + + @Override + public String toString() { + return "AllowedIntegerInputs$Unlimited{}"; + } + } + + final class Positive extends AllowedNumberInputs.Positive implements AllowedIntegerInputs { + + static final @NotNull AllowedIntegerInputs.Positive INSTANCE = new AllowedIntegerInputs.Positive(); + + private Positive() { + } + + @Override + public @NotNull Integer from() { + return 0; + } + + @Override + public @NotNull Integer to() { + return Integer.MAX_VALUE; + } + + @Override + public boolean isInRange(@NotNull Integer input) { + return input > -1; + } + + @Override + public String toString() { + return "AllowedIntegerInputs$Positive{}"; + } + } + + final class Negative extends AllowedNumberInputs.Negative implements AllowedIntegerInputs { + + static final @NotNull AllowedIntegerInputs.Negative INSTANCE = new AllowedIntegerInputs.Negative(); + + private Negative() { + } + + @Override + public @NotNull Integer from() { + return -1; + } + + @Override + public @NotNull Integer to() { + return Integer.MIN_VALUE; + } + + @Override + public boolean isInRange(@NotNull Integer input) { + return input < 0; + } + + @Override + public String toString() { + return "AllowedIntegerInputs$Negative{}"; + } + } + + final class Value extends AllowedNumberInputs.Value implements AllowedIntegerInputs { + + Value(@NotNull Integer value) { + super(value); + } + + @Override + public String toString() { + return "AllowedIntegerInputs$Value{" + + "value=" + value() + + "}"; + } + } + + final class Values extends AllowedNumberInputs.Values implements AllowedIntegerInputs { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "AllowedIntegerInputs$Values{" + + "values=" + allowedValues() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedLongInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedLongInputs.java new file mode 100644 index 00000000..080ecfc4 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedLongInputs.java @@ -0,0 +1,147 @@ +package com.wizardlybump17.wlib.command.input.primitive.number; + +import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface AllowedLongInputs extends PrimitiveAllowedInputs { + + static @NotNull Range range(long from, long to) { + return new Range(from, to); + } + + static @NotNull Unlimited unlimited() { + return Unlimited.INSTANCE; + } + + static @NotNull Positive positive() { + return Positive.INSTANCE; + } + + static @NotNull Negative negative() { + return Negative.INSTANCE; + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Value value(long value) { + return new Value(value); + } + + final class Range extends AllowedNumberInputs.Ranged implements AllowedLongInputs { + + Range(long from, long to) { + super(from, to); + if (from >= to) + throw new IllegalArgumentException("from must be less than to"); + } + + @Override + public String toString() { + return "AllowedLongInputs$Range{" + + "from=" + from() + + ", to=" + to() + + "}"; + } + } + + final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedLongInputs { + + static final @NotNull AllowedLongInputs.Unlimited INSTANCE = new AllowedLongInputs.Unlimited(); + + private Unlimited() { + } + + @Override + public String toString() { + return "AllowedLongInputs$Unlimited{}"; + } + } + + final class Positive extends AllowedNumberInputs.Positive implements AllowedLongInputs { + + static final @NotNull AllowedLongInputs.Positive INSTANCE = new AllowedLongInputs.Positive(); + + private Positive() { + } + + @Override + public @NotNull Long from() { + return 0L; + } + + @Override + public @NotNull Long to() { + return Long.MAX_VALUE; + } + + @Override + public boolean isInRange(@NotNull Long input) { + return input > -1; + } + + @Override + public String toString() { + return "AllowedLongInputs$Positive{}"; + } + } + + final class Negative extends AllowedNumberInputs.Negative implements AllowedLongInputs { + + static final @NotNull AllowedLongInputs.Negative INSTANCE = new AllowedLongInputs.Negative(); + + private Negative() { + } + + @Override + public @NotNull Long from() { + return -1L; + } + + @Override + public @NotNull Long to() { + return Long.MIN_VALUE; + } + + @Override + public boolean isInRange(@NotNull Long input) { + return input < 0; + } + + @Override + public String toString() { + return "AllowedLongInputs$Negative{}"; + } + } + + final class Value extends AllowedNumberInputs.Value implements AllowedLongInputs { + + Value(long value) { + super(value); + } + + @Override + public String toString() { + return "AllowedLongInputs$Value{" + + "value=" + value() + + "}"; + } + } + + final class Values extends AllowedNumberInputs.Values implements AllowedLongInputs { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "AllowedLongInputs$Values{" + + "values=" + allowedValues() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedNumberInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedNumberInputs.java new file mode 100644 index 00000000..916bdfc8 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedNumberInputs.java @@ -0,0 +1,182 @@ +package com.wizardlybump17.wlib.command.input.primitive.number; + +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.input.AllowedListInputs; +import com.wizardlybump17.wlib.command.input.RangedAllowedInputs; +import com.wizardlybump17.wlib.command.input.SingleValueInput; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public interface AllowedNumberInputs extends AllowedInputs { + + static @NotNull Value value(@NotNull N value) { + return new Value<>(value); + } + + static @NotNull Values values(@NotNull List values) { + return new Values<>(values); + } + + abstract class Ranged> implements AllowedNumberInputs, RangedAllowedInputs { + + private final @NotNull N from; + private final @NotNull N to; + + public Ranged(@NotNull N from, @NotNull N to) { + this.from = from; + this.to = to; + } + + @Override + public @NotNull N from() { + return from; + } + + @Override + public @NotNull N to() { + return to; + } + + @Override + public boolean isInRange(@NotNull N number) { + return number.compareTo(from) >= 0 && number.compareTo(to) <= 0; + } + + @Override + public boolean isAllowed(@Nullable N input) { + if (input == null) + return false; + return isInRange(input); + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Ranged ranged = (Ranged) object; + return Objects.equals(from, ranged.from) && Objects.equals(to, ranged.to); + } + + @Override + public int hashCode() { + return Objects.hash(from, to); + } + + @Override + public String toString() { + return "Ranged{" + + "from=" + from + + ", to=" + to + + '}'; + } + } + + abstract class Unlimited implements AllowedNumberInputs { + + @Override + public boolean isAllowed(@Nullable N input) { + return true; + } + } + + class Value implements AllowedNumberInputs, SingleValueInput { + + private final @NotNull N value; + + Value(@NotNull N value) { + this.value = value; + } + + @Override + public @NotNull N value() { + return value; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Value value1 = (Value) object; + return Objects.equals(value, value1.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public String toString() { + return "Value{" + + "value=" + value + + '}'; + } + } + + class Values implements AllowedNumberInputs, AllowedListInputs { + + private final @NotNull List values; + + Values(@NotNull List values) { + this.values = List.copyOf(values); + } + + @Override + public @NotNull List allowedValues() { + return values; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedNumberInputs.Values values1 = (AllowedNumberInputs.Values) object; + return Objects.equals(values, values1.values); + } + + @Override + public int hashCode() { + return Objects.hashCode(values); + } + + @Override + public String toString() { + return "Values{" + + "values=" + values + + '}'; + } + } + + abstract class Positive implements AllowedNumberInputs, RangedAllowedInputs { + + @Override + public boolean isAllowed(@Nullable N input) { + if (input == null) + return false; + return isInRange(input); + } + + @Override + public String toString() { + return "Positive{}"; + } + } + + abstract class Negative implements AllowedNumberInputs, RangedAllowedInputs { + + @Override + public boolean isAllowed(@Nullable N input) { + if (input == null) + return false; + return isInRange(input); + } + + @Override + public String toString() { + return "Negative{}"; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedShortInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedShortInputs.java new file mode 100644 index 00000000..45ca67e6 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedShortInputs.java @@ -0,0 +1,147 @@ +package com.wizardlybump17.wlib.command.input.primitive.number; + +import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface AllowedShortInputs extends PrimitiveAllowedInputs { + + static @NotNull Range range(short from, short to) { + return new Range(from, to); + } + + static @NotNull Unlimited unlimited() { + return Unlimited.INSTANCE; + } + + static @NotNull Positive positive() { + return Positive.INSTANCE; + } + + static @NotNull Negative negative() { + return Negative.INSTANCE; + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Value value(short value) { + return new Value(value); + } + + final class Range extends AllowedNumberInputs.Ranged implements AllowedShortInputs { + + Range(short from, short to) { + super(from, to); + if (from >= to) + throw new IllegalArgumentException("from must be less than to"); + } + + @Override + public String toString() { + return "AllowedShortInputs$Range{" + + "from=" + from() + + ", to=" + to() + + "}"; + } + } + + final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedShortInputs { + + static final @NotNull AllowedShortInputs.Unlimited INSTANCE = new AllowedShortInputs.Unlimited(); + + private Unlimited() { + } + + @Override + public String toString() { + return "AllowedShortInputs$Unlimited{}"; + } + } + + final class Positive extends AllowedNumberInputs.Positive implements AllowedShortInputs { + + static final @NotNull AllowedShortInputs.Positive INSTANCE = new AllowedShortInputs.Positive(); + + private Positive() { + } + + @Override + public @NotNull Short from() { + return 0; + } + + @Override + public @NotNull Short to() { + return Short.MAX_VALUE; + } + + @Override + public boolean isInRange(@NotNull Short input) { + return input > -1; + } + + @Override + public String toString() { + return "AllowedShortInputs$Positive{}"; + } + } + + final class Negative extends AllowedNumberInputs.Negative implements AllowedShortInputs { + + static final @NotNull AllowedShortInputs.Negative INSTANCE = new AllowedShortInputs.Negative(); + + private Negative() { + } + + @Override + public @NotNull Short from() { + return -1; + } + + @Override + public @NotNull Short to() { + return Short.MIN_VALUE; + } + + @Override + public boolean isInRange(@NotNull Short input) { + return input < 0; + } + + @Override + public String toString() { + return "AllowedShortInputs$Negative{}"; + } + } + + final class Value extends AllowedNumberInputs.Value implements AllowedShortInputs { + + Value(short value) { + super(value); + } + + @Override + public String toString() { + return "AllowedShortInputs$Value{" + + "value=" + value() + + "}"; + } + } + + final class Values extends AllowedNumberInputs.Values implements AllowedShortInputs { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "AllowedShortInputs$Values{" + + "values=" + allowedValues() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/string/AllowedStringInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/string/AllowedStringInputs.java new file mode 100644 index 00000000..1e5cf8d1 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/string/AllowedStringInputs.java @@ -0,0 +1,178 @@ +package com.wizardlybump17.wlib.command.input.string; + +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.input.AllowedListInputs; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.Objects; + +public interface AllowedStringInputs extends AllowedInputs { + + static @NotNull Values valuesIgnoreCase(@NotNull List values) { + return new Values(values, true); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values, false); + } + + static @NotNull Value valueIgnoreCase(@NotNull String value) { + return new Value(value, true); + } + + static @NotNull Value value(@NotNull String value) { + return new Value(value, false); + } + + static @NotNull Any anyNullable() { + return Any.NULLABLE; + } + + static @NotNull Any anyNotNull() { + return Any.NOT_NULL; + } + + final class Values implements AllowedStringInputs, AllowedListInputs { + + private final @NotNull List values; + private final @NotNull List toCheck; + private final boolean ignoreCase; + + private Values(@NotNull List values, boolean ignoreCase) { + this.values = List.copyOf(values); + this.ignoreCase = ignoreCase; + if (ignoreCase) + toCheck = values.stream().map(String::toLowerCase).toList(); + else + toCheck = this.values; + } + + @Override + public @NotNull @Unmodifiable List allowedValues() { + return values; + } + + @Override + public boolean isAllowed(@Nullable String input) { + if (input == null) + return false; + if (ignoreCase) + return toCheck.contains(input.toLowerCase()); + return toCheck.contains(input); + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedStringInputs.Values values1 = (AllowedStringInputs.Values) object; + return ignoreCase == values1.ignoreCase && Objects.equals(values, values1.values) && Objects.equals(toCheck, values1.toCheck); + } + + @Override + public int hashCode() { + return Objects.hash(values, toCheck, ignoreCase); + } + + @Override + public String toString() { + return "AllowedStringInputs$Values{" + + "values=" + values + + ", ignoreCase=" + ignoreCase + + '}'; + } + } + + final class Value implements AllowedStringInputs { + + private final @NotNull String value; + private final boolean ignoreCase; + + public Value(@NotNull String value, boolean ignoreCase) { + this.value = value; + this.ignoreCase = ignoreCase; + } + + public @NotNull String value() { + return value; + } + + public boolean ignoreCase() { + return ignoreCase; + } + + @Override + public boolean isAllowed(@Nullable String input) { + if (input == null) + return false; + if (ignoreCase) + return value.equalsIgnoreCase(input); + return value.equals(input); + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Value value1 = (Value) object; + return ignoreCase == value1.ignoreCase && Objects.equals(value, value1.value); + } + + @Override + public int hashCode() { + return Objects.hash(value, ignoreCase); + } + + @Override + public String toString() { + return "AllowedStringInputs$Value{" + + "value='" + value + '\'' + + ", ignoreCase=" + ignoreCase + + '}'; + } + } + + final class Any implements AllowedStringInputs { + + private static final @NotNull AllowedStringInputs.Any NULLABLE = new AllowedStringInputs.Any(true); + private static final @NotNull AllowedStringInputs.Any NOT_NULL = new AllowedStringInputs.Any(false); + + private final boolean nullable; + + private Any(boolean nullable) { + this.nullable = nullable; + } + + @Override + public boolean isAllowed(@Nullable String input) { + return nullable || input != null; + } + + public boolean nullable() { + return nullable; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedStringInputs.Any any = (AllowedStringInputs.Any) object; + return nullable == any.nullable; + } + + @Override + public int hashCode() { + return Objects.hashCode(nullable); + } + + @Override + public String toString() { + return "AllowedStringInput$Any{" + + "nullable=" + nullable + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/manager/CommandManager.java b/commands/src/main/java/com/wizardlybump17/wlib/command/manager/CommandManager.java new file mode 100644 index 00000000..df98a096 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/manager/CommandManager.java @@ -0,0 +1,409 @@ +package com.wizardlybump17.wlib.command.manager; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.context.CommandContext; +import com.wizardlybump17.wlib.command.exception.CommandExecutionException; +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.exception.InvalidInputException; +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.manager.listener.CommandManagerListener; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.result.CommandResult; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import com.wizardlybump17.wlib.util.StringUtil; +import com.wizardlybump17.wlib.util.exception.QuotedStringException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +public class CommandManager { + + public static final char SEPARATOR = ':'; + + private final @NotNull Map commandsByFullName = new ConcurrentHashMap<>(); + private final @NotNull Map commandsByName = new ConcurrentHashMap<>(); + private final @NotNull Set listeners = ConcurrentHashMap.newKeySet(); + private final @NotNull Map> commandsByHolder = new ConcurrentHashMap<>(); + private final @NotNull Map holdersByFullName = new ConcurrentHashMap<>(); + + protected void addCommand(@NotNull String identifier, @NotNull String name, @NotNull Command command, @Nullable Object holder) { + String fullName = identifier + SEPARATOR + name; + + commandsByFullName.put(fullName, command); + commandsByName.put(name, command); + + if (holder != null) { + commandsByHolder.computeIfAbsent(holder, $ -> ConcurrentHashMap.newKeySet()).add(command); + holdersByFullName.put(fullName, holder); + } + + for (CommandManagerListener listener : listeners) + listener.onRegister(identifier, command, holder, this); + } + + public @NotNull Command registerCommand(@NotNull String identifier, @NotNull Command command, @Nullable Object holder) { + String commandName = command.getRoot().getName().toLowerCase(); + String fullCommandName = identifier + SEPARATOR + commandName; + + Command existingCommand = commandsByFullName.get(fullCommandName); + + if (existingCommand != null) { + Command newCommand = mergeCommand(existingCommand, command); + addCommand(identifier, commandName, newCommand, holder); + return newCommand; + } + + addCommand(identifier, commandName, command, holder); + return command; + } + + public @NotNull Command registerCommand(@NotNull String identifier, @NotNull Command command) { + return registerCommand(identifier, command, null); + } + + public @NotNull List registerCommands(@NotNull String identifier, @NotNull List commands, @Nullable Object holder) { + List newCommands = new ArrayList<>(commands.size()); + for (Command command : commands) + newCommands.add(registerCommand(identifier, command, holder)); + return newCommands; + } + + public @NotNull List registerCommands(@NotNull String identifier, @NotNull List commands) { + return registerCommands(identifier, commands, null); + } + + protected @NotNull Command mergeCommand(@NotNull Command left, @NotNull Command right) { + return left.merge(right); + } + + @SuppressWarnings("unchecked") + public @NotNull CommandResult execute(@NotNull CommandSender sender, @NotNull List input) throws CommandExecutionException { + if (input.isEmpty()) + return CommandResult.badRequest(CommandResult.ErrorDetails.emptyInput()); + + String commandName = input.getFirst(); + + Command command = commandsByFullName.get(commandName); + + if (command == null) + command = commandsByName.get(commandName); + if (command == null) + return CommandResult.notFound(CommandResult.ErrorDetails.nodeNotFound(0, commandName)); + + List> arguments = new ArrayList<>(); + List> children = List.of(command.getRoot()); + + CommandNode lastNode = null; + int lastInputIndex = 0; + InputParsingException lastParsingError = null; + InvalidInputException lastInputError = null; + + inputLoop: for (int i = 0; i < input.size(); i++) { + String inputString = input.get(i); + lastInputIndex = i; + + for (CommandNode child : children) { + lastNode = child; + + try { + Object result; + if (inputString == null) { + if (child.isValidInput(null)) + result = null; + else + throw new InvalidInputException("Null inputs are not accepted by " + child.getName()); + } else { + result = child.parseOrInvalid(inputString); + } + + if (!(child instanceof LiteralCommandNode)) + arguments.add(new CommandContext.CommandNodeArgument<>((CommandNode) child, inputString, result)); + + children = child.getChildren(); + + lastParsingError = null; + lastInputError = null; + + continue inputLoop; + } catch (InputParsingException e) { + lastParsingError = e; + } catch (InvalidInputException e) { + lastInputError = e; + } + } + + if (lastParsingError != null) + return CommandResult.badRequest(CommandResult.ErrorDetails.parseError(command.getName(), lastNode.getName(), lastParsingError)); + if (lastInputError != null) + return CommandResult.unprocessableContent(CommandResult.ErrorDetails.inputError(command.getName(), lastNode.getName(), inputString, lastInputError)); + + return CommandResult.notFound(CommandResult.ErrorDetails.nodeNotFound(lastInputIndex, inputString)); + } + + CommandNodeExecutor executor = lastNode.getExecutor(); + if (executor == null) + return CommandResult.notImplemented(CommandResult.ErrorDetails.noCommandExecutor(command.getName(), lastNode.getName())); + + CommandContext context = new CommandContext( + command, + sender, + new CommandContext.CommandNodeArguments(arguments), + lastInputIndex, + lastNode + ); + + String nodePermission = lastNode.getPermission(); + if (!lastNode.canExecute(sender)) + return CommandResult.forbidden(CommandResult.ErrorDetails.noPermission(sender.getName(), nodePermission)); + + try { + CommandResult result = executor.execute(context); + if (result == null) + throw new CommandExecutionException(CommandExecutionException.MESSAGE.formatted(input, lastInputIndex, lastNode.getName()), new NullPointerException("The returned CommandResult can not be null")); + return result; + } catch (Throwable throwable) { + throw new CommandExecutionException(CommandExecutionException.MESSAGE.formatted(input, lastInputIndex, lastNode.getName()), throwable); + } + } + + public @NotNull CommandResult execute(@NotNull CommandSender sender, @NotNull String input) throws CommandExecutionException { + return execute(sender, StringUtil.parseQuotedStrings(input)); + } + + public @NotNull CommandResult execute(@NotNull CommandSender sender, @NotNull String @NotNull [] input) throws CommandExecutionException { + return execute(sender, String.join(" ", input)); + } + + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input) throws SuggesterException { + if (input.isEmpty() || input.size() == 1) { + return commandsByName.values().stream() + .map(Command::getRoot) + .filter(node -> node.canExecute(sender)) + .map(CommandNode::getName) + .toList(); + } + + String commandName = input.getFirst(); + + Command command = commandsByFullName.get(commandName); + + if (command == null) + command = commandsByName.get(commandName); + if (command == null) + return List.of(); + + String currentInput = input.getLast(); + + List suggestions = new ArrayList<>(); + List> children = List.of(command.getRoot()); + + CommandNode lastNode = null; + Throwable lastError = null; + + inputLoop: for (int i = 0; i < input.size(); i++) { + String inputString = input.get(i); + boolean isLastInput = i == input.size() - 1; + + if (isLastInput && inputString != null && inputString.isEmpty()) { + for (CommandNode child : children) { + String permission = child.getPermission(); + if (permission == null || sender.hasPermission(permission)) + suggestions.addAll(getSuggestions0(child, sender, input, "")); + } + if (!children.isEmpty()) + lastNode = children.getLast(); + break; + } + + boolean foundNode = !children.isEmpty(); + for (CommandNode child : children) { + lastNode = child; + + if (!isLastInput) { + try { + if (inputString == null) { + if (!child.isValidInput(null)) + throw new InvalidInputException("Null inputs not accepted by " + child); + continue; + } + + child.parseOrInvalid(inputString); + } catch (InputParsingException | InvalidInputException e) { + lastError = e; + foundNode = false; + continue; + } + } + + lastError = null; + foundNode = true; + + String permission = child.getPermission(); + if (isLastInput && (permission == null || sender.hasPermission(permission))) + suggestions.addAll(getSuggestions0(child, sender, input, currentInput)); + + if (isLastInput) { + continue; + } else { + children = child.getChildren(); + continue inputLoop; + } + } + + if (lastError != null || !foundNode) + return List.of(); + } + + if (lastNode == null) + return List.of(); + + return suggestions; + } + + @SuppressWarnings("unchecked") + private static @NotNull List getSuggestions0(@NotNull CommandNode node, @NotNull CommandSender sender, @NotNull List input, @NotNull String currentInput) throws SuggesterException { + List childSuggestions = node.getSuggestions(sender, input, currentInput); + Suggester suggester = (Suggester) node.getSuggester(); + + if (suggester == null) { + return childSuggestions + .stream() + .map(Object::toString) + .toList(); + } else { + Stream stream = childSuggestions + .stream() + .map(suggester::getStringRepresentation); + if (suggester.needsEscape()) + stream = stream.map(StringUtil::escapeString); + return stream.toList(); + } + } + + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull String input) throws SuggesterException { + try { + if (!input.isEmpty() && !StringUtil.isProperlyQuoted(input)) + input = input + "\""; + return getSuggestions(sender, StringUtil.parseQuotedStrings(input)); + } catch (QuotedStringException e) { + return List.of(); + } + } + + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull String @NotNull [] input) throws SuggesterException { + if (input.length == 0) + return getSuggestions(sender, List.of()); + + String inputString = String.join(" ", input); + try { + if (!inputString.isEmpty() && !StringUtil.isProperlyQuoted(inputString)) + inputString = inputString + "\""; + + List inputList = StringUtil.parseQuotedStrings(inputString); + if (input[input.length - 1].isEmpty()) + inputList.add(""); + + return getSuggestions(sender, inputList); + } catch (QuotedStringException e) { + return List.of(); + } + } + + public @NotNull Optional getCommand(@NotNull String name) { + return Optional + .ofNullable(commandsByFullName.get(name)) + .or(() -> Optional.ofNullable(commandsByName.get(name))); + } + + public void clear() { + for (CommandManagerListener listener : listeners) + listener.onPreClear(this); + + commandsByName.clear(); + commandsByFullName.clear(); + + commandsByHolder.forEach((holder, commands) -> commands.clear()); + commandsByHolder.clear(); + + holdersByFullName.clear(); + + for (CommandManagerListener listener : listeners) + listener.onPostClear(this); + } + + public @NotNull @UnmodifiableView Map getCommandsByFullName() { + return Collections.unmodifiableMap(commandsByFullName); + } + + public @NotNull @UnmodifiableView Map getCommandsByName() { + return Collections.unmodifiableMap(commandsByName); + } + + public @NotNull @UnmodifiableView Map> getCommandsByHolder() { + return Collections.unmodifiableMap(commandsByHolder); + } + + public @NotNull @UnmodifiableView Set getCommandsByHolder(@NotNull Object holder) { + return Collections.unmodifiableSet(commandsByHolder.getOrDefault(holder, Set.of())); + } + + public @NotNull @UnmodifiableView Map getHoldersByFullName() { + return Collections.unmodifiableMap(holdersByFullName); + } + + public void addListener(@NotNull CommandManagerListener listener) { + listeners.add(listener); + } + + public void clearListeners() { + listeners.clear(); + } + + public @NotNull @UnmodifiableView Set getListeners() { + return Collections.unmodifiableSet(listeners); + } + + public boolean isEmpty() { + return commandsByFullName.isEmpty(); + } + + public void unregister(@NotNull String identifier, @NotNull Command command) { + unregister(identifier, command.getName()); + } + + public void unregister(@NotNull String identifier, @NotNull String name) { + String fullName = identifier + SEPARATOR + name; + + Command command = commandsByFullName.remove(fullName); + if (command == null) + return; + + commandsByName.remove(name); + + Object holder = holdersByFullName.remove(fullName); + if (holder != null) { + Set commands = commandsByHolder.get(holder); + if (commands != null) + commands.remove(command); + } + + for (CommandManagerListener listener : listeners) + listener.onUnregister(identifier, command, holder, this); + } + + public void unregisterByHolder(@NotNull String identifier, @NotNull Object holder) { + Set commands = commandsByHolder.get(holder); + if (commands == null) + return; + + for (Command command : commands) + unregister(identifier, command); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/manager/listener/CommandManagerListener.java b/commands/src/main/java/com/wizardlybump17/wlib/command/manager/listener/CommandManagerListener.java new file mode 100644 index 00000000..a0d3974e --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/manager/listener/CommandManagerListener.java @@ -0,0 +1,17 @@ +package com.wizardlybump17.wlib.command.manager.listener; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface CommandManagerListener { + + void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager); + + void onPreClear(@NotNull CommandManager manager); + + void onPostClear(@NotNull CommandManager manager); + + void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager); +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/CommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/CommandNode.java new file mode 100644 index 00000000..8cf9cf3d --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/CommandNode.java @@ -0,0 +1,255 @@ +package com.wizardlybump17.wlib.command.node; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.exception.InvalidInputException; +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import com.wizardlybump17.wlib.util.CollectionUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.*; + +public abstract class CommandNode { + + private final @NotNull String name; + private final @NotNull @Unmodifiable List> children; + private final @NotNull AllowedInputs allowedInputs; + private final @Nullable Suggester suggester; + private final @Nullable CommandNodeExecutor executor; + private final @Nullable String permission; + + public CommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + this.name = name; + this.children = List.copyOf(children); + this.allowedInputs = allowedInputs; + this.suggester = suggester; + this.executor = executor; + this.permission = permission; + } + + public @NotNull String getName() { + return name; + } + + public @NotNull @Unmodifiable List> getChildren() { + return children; + } + + public @NotNull AllowedInputs getAllowedInputs() { + return allowedInputs; + } + + public @Nullable Suggester getSuggester() { + return suggester; + } + + public abstract @Nullable T parse(@NotNull String input) throws InputParsingException; + + public final boolean isValidInput(@Nullable T input) { + return allowedInputs.isAllowed(input); + } + + public final @Nullable T parseOrInvalid(@NotNull String input) throws InputParsingException, InvalidInputException { + T parse = parse(input); + if (!isValidInput(parse)) + throw new InvalidInputException("Invalid input " + input); + return parse; + } + + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List args, @NotNull String currentInput) throws SuggesterException { + return suggester == null ? List.of() : suggester.getSuggestions(sender, args, currentInput, this); + } + + public @Nullable CommandNodeExecutor getExecutor() { + return executor; + } + + public @Nullable String getPermission() { + return permission; + } + + @Override + public String toString() { + return "CommandNode{" + + "name='" + name + '\'' + + ", children=" + children + + ", allowedInputs=" + allowedInputs + + ", suggester=" + suggester + + ", executor=" + executor + + ", permission='" + permission + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + CommandNode that = (CommandNode) o; + return Objects.equals(name, that.name) + && CollectionUtil.contentEquals(children, that.children) + && Objects.equals(allowedInputs, that.allowedInputs) + && Objects.equals(executor, that.executor) + && Objects.equals(permission, that.permission); + } + + public boolean equalsIgnoreExecutor(@Nullable Object other) { + if (other == null || getClass() != other.getClass()) + return false; + CommandNode that = (CommandNode) other; + return Objects.equals(name, that.name) + && equalsIgnoreExecutor(children, that.children) + && Objects.equals(allowedInputs, that.allowedInputs) + && Objects.equals(permission, that.permission); + } + + public static boolean equalsIgnoreExecutor(@NotNull Collection> a, @NotNull Collection> b) { + if (a.size() != b.size()) + return false; + + Iterator> aIterator = a.iterator(); + Iterator> bIterator = b.iterator(); + + while (aIterator.hasNext() && bIterator.hasNext()) { + CommandNode aNode = aIterator.next(); + CommandNode bNode = bIterator.next(); + if (!aNode.equalsIgnoreExecutor(bNode)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + return Objects.hash(name, children, allowedInputs, executor, permission); + } + + public @NotNull String getFullCommand() { + StringBuilder builder = new StringBuilder(); + if (this instanceof LiteralCommandNode) + builder.append(name); + else + builder.append('<').append(name).append('>'); + + for (CommandNode child : children) { + builder.append(' '); + builder.append(child.getFullCommand()); + } + + return builder.toString(); + } + + public abstract @NotNull CommandNode withChildren(@NotNull List> children); + + public abstract @NotNull CommandNode withExecutor(@Nullable CommandNodeExecutor executor); + + public abstract @NotNull CommandNode withPermission(@Nullable String permission); + + public @NotNull Optional> getChild(@NotNull String name) { + for (CommandNode child : children) + if (child.getName().equals(name)) + return Optional.of(child); + return Optional.empty(); + } + + public @NotNull CommandNode merge(@NotNull CommandNode right) { + List> newChildrenLeft = getNewChildren(right, this); + List> newChildrenRight = getNewChildren(this, right); + + newChildrenLeft.removeIf(newChildLeft -> { + for (CommandNode newChildRight : newChildrenRight) + if (newChildRight.getName().equals(newChildLeft.getName())) + return true; + return false; + }); + newChildrenRight.removeIf(newChildRight -> { + for (CommandNode newChildLeft : newChildrenLeft) + if (newChildLeft.getName().equals(newChildRight.getName())) + return true; + return false; + }); + + newChildrenLeft.addAll(newChildrenRight); + + CommandNode newNode = withChildren(newChildrenLeft); + + if (executor == null && right.getExecutor() != null) + newNode = newNode.withExecutor(right.getExecutor()); + if (permission == null && right.getPermission() != null) + newNode = newNode.withPermission(right.getPermission()); + + return newNode; + } + + private static @Nullable CommandNodeExecutor getNewExecutor(@NotNull CommandNode left, @NotNull CommandNode right) { + CommandNodeExecutor leftExecutor = left.getExecutor(); + CommandNodeExecutor rightExecutor = right.getExecutor(); + CommandNodeExecutor newLeftExecutor; + + if (leftExecutor == null && rightExecutor == null) { + newLeftExecutor = null; + } else if (leftExecutor == null && rightExecutor != null) { + newLeftExecutor = rightExecutor; + } else if (leftExecutor != null && rightExecutor == null) { + newLeftExecutor = leftExecutor; + } else if (Objects.equals(leftExecutor, rightExecutor)) { + newLeftExecutor = leftExecutor; + } else { + throw new IllegalStateException("Could not resolve an executor for the merged node."); + } + + return newLeftExecutor; + } + + private static @NotNull List> getNewChildren(@NotNull CommandNode left, @NotNull CommandNode right) { + List> newChildren = new ArrayList<>(); + + for (CommandNode rightChild : right.children) { + Optional> leftChildOptional = left.getChild(rightChild.name); + + if (leftChildOptional.isEmpty()) { + newChildren.add(rightChild); + continue; + } + + CommandNode leftChild = leftChildOptional.get(); + + CommandNodeExecutor newLeftExecutor = getNewExecutor(leftChild, rightChild); + leftChild = leftChild.withExecutor(newLeftExecutor); + + newChildren.add(leftChild.merge(rightChild)); + } + + return newChildren; + } + + public boolean canExecute(@NotNull CommandSender sender) { + return permission == null || sender.hasPermission(permission); + } + + public @Nullable CommandNode findChild(@NotNull String name) { + for (CommandNode child : children) + if (child.getName().equals(name)) + return child; + + for (CommandNode child : children) { + CommandNode found = child.findChild(name); + if (found != null) + return found; + } + + return null; + } + + public int getTotalNodes() { + int total = 1; + for (CommandNode child : children) + total += child.getTotalNodes(); + return total; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/LiteralCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/LiteralCommandNode.java new file mode 100644 index 00000000..666ede29 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/LiteralCommandNode.java @@ -0,0 +1,57 @@ +package com.wizardlybump17.wlib.command.node; + +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.string.AllowedStringInputs; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.LiteralSuggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class LiteralCommandNode extends CommandNode { + + public LiteralCommandNode(@NotNull String name, @NotNull List> children, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, AllowedStringInputs.valueIgnoreCase(name), LiteralSuggester.of(name), executor, permission); + } + + @Override + public @NotNull AllowedStringInputs.Value getAllowedInputs() { + return (AllowedStringInputs.Value) super.getAllowedInputs(); + } + + @Override + public @NotNull LiteralSuggester getSuggester() { + return (LiteralSuggester) super.getSuggester(); + } + + @Override + public @NotNull String parse(@NotNull String input) { + return input; + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List args, @NotNull String currentInput) { + return List.of(getName()); + } + + @Override + public @NotNull LiteralCommandNode withChildren(@NotNull List> children) { + return new LiteralCommandNode(getName(), children, getExecutor(), getPermission()); + } + + @Override + public @NotNull CommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new LiteralCommandNode(getName(), getChildren(), executor, getPermission()); + } + + @Override + public @NotNull CommandNode withPermission(@Nullable String permission) { + return new LiteralCommandNode(getName(), getChildren(), getExecutor(), permission); + } + + @Override + public @NotNull LiteralCommandNode merge(@NotNull CommandNode right) { + return (LiteralCommandNode) super.merge(right); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/EnumCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/EnumCommandNode.java new file mode 100644 index 00000000..b9cdf5f0 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/EnumCommandNode.java @@ -0,0 +1,49 @@ +package com.wizardlybump17.wlib.command.node.object; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class EnumCommandNode> extends CommandNode { + + private final @NotNull Class enumType; + + public EnumCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission, @NotNull Class enumType) { + super(name, children, allowedInputs, suggester, executor, permission); + this.enumType = enumType; + } + + @Override + public @Nullable E parse(@NotNull String input) throws InputParsingException { + try { + return Enum.valueOf(enumType, input.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new InputParsingException("Could not parse as " + enumType.getSimpleName() + ": " + input, e); + } + } + + @Override + public @NotNull CommandNode withChildren(@NotNull List> children) { + return new EnumCommandNode<>(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission(), enumType); + } + + @Override + public @NotNull CommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new EnumCommandNode<>(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission(), enumType); + } + + @Override + public @NotNull CommandNode withPermission(@Nullable String permission) { + return new EnumCommandNode<>(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission, enumType); + } + + public @NotNull Class getEnumType() { + return enumType; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/JsonElementCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/JsonElementCommandNode.java new file mode 100644 index 00000000..4667f8af --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/JsonElementCommandNode.java @@ -0,0 +1,49 @@ +package com.wizardlybump17.wlib.command.node.object; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonSyntaxException; +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +//TODO: test +public class JsonElementCommandNode extends CommandNode { + + private final @NotNull Gson gson; + + public JsonElementCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission, @NotNull Gson gson) { + super(name, children, allowedInputs, suggester, executor, permission); + this.gson = gson; + } + + @Override + public @Nullable JsonElement parse(@NotNull String input) throws InputParsingException { + try { + return gson.fromJson(input, JsonElement.class); + } catch (JsonSyntaxException e) { + throw new InputParsingException("Could not parse as JsonElement: " + input, e); + } + } + + @Override + public @NotNull CommandNode withChildren(@NotNull List> children) { + return new JsonElementCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission(), gson); + } + + @Override + public @NotNull CommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new JsonElementCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission(), gson); + } + + @Override + public @NotNull CommandNode withPermission(@Nullable String permission) { + return new JsonElementCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission, gson); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/UUIDCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/UUIDCommandNode.java new file mode 100644 index 00000000..97040cbb --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/object/UUIDCommandNode.java @@ -0,0 +1,44 @@ +package com.wizardlybump17.wlib.command.node.object; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.UUID; + +public class UUIDCommandNode extends CommandNode { + + public UUIDCommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @Nullable UUID parse(@NotNull String input) throws InputParsingException { + try { + return UUID.fromString(input); + } catch (IllegalArgumentException e) { + throw new InputParsingException("Could not parse as UUID: " + input, e); + } + } + + @Override + public @NotNull UUIDCommandNode withChildren(@NotNull List> children) { + return new UUIDCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull UUIDCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new UUIDCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull UUIDCommandNode withPermission(@Nullable String permission) { + return new UUIDCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/AbstractPrimitiveCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/AbstractPrimitiveCommandNode.java new file mode 100644 index 00000000..8578fe08 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/AbstractPrimitiveCommandNode.java @@ -0,0 +1,18 @@ +package com.wizardlybump17.wlib.command.node.primitive; + +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; + +public abstract class AbstractPrimitiveCommandNode

extends CommandNode

implements PrimitiveCommandNode { + + public AbstractPrimitiveCommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs

allowedInputs, @Nullable Suggester

suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/BooleanCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/BooleanCommandNode.java new file mode 100644 index 00000000..17a84308 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/BooleanCommandNode.java @@ -0,0 +1,43 @@ +package com.wizardlybump17.wlib.command.node.primitive; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; + +public class BooleanCommandNode extends AbstractPrimitiveCommandNode { + + public BooleanCommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @Nullable Boolean parse(@NotNull String input) throws InputParsingException { + if (input.equalsIgnoreCase("true")) + return true; + if (input.equalsIgnoreCase("false")) + return false; + throw new InputParsingException("Could not parse as boolean: " + input); + } + + @Override + public @NotNull BooleanCommandNode withChildren(@NotNull List> children) { + return new BooleanCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull BooleanCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new BooleanCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull BooleanCommandNode withPermission(@Nullable String permission) { + return new BooleanCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/CharacterCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/CharacterCommandNode.java new file mode 100644 index 00000000..e3f356fd --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/CharacterCommandNode.java @@ -0,0 +1,41 @@ +package com.wizardlybump17.wlib.command.node.primitive; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; + +public class CharacterCommandNode extends AbstractPrimitiveCommandNode { + + public CharacterCommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @Nullable Character parse(@NotNull String input) throws InputParsingException { + if (input.length() != 1) + throw new InputParsingException("Invalid input length. Expected exactly one char: " + input); + return input.charAt(0); + } + + @Override + public @NotNull CharacterCommandNode withChildren(@NotNull List> children) { + return new CharacterCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull CharacterCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new CharacterCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull CharacterCommandNode withPermission(@Nullable String permission) { + return new CharacterCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/PrimitiveCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/PrimitiveCommandNode.java new file mode 100644 index 00000000..b0564f5c --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/PrimitiveCommandNode.java @@ -0,0 +1,4 @@ +package com.wizardlybump17.wlib.command.node.primitive; + +public interface PrimitiveCommandNode { +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/ByteCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/ByteCommandNode.java new file mode 100644 index 00000000..a8072033 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/ByteCommandNode.java @@ -0,0 +1,43 @@ +package com.wizardlybump17.wlib.command.node.primitive.number; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.PrimitiveCommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ByteCommandNode extends NumberCommandNode implements PrimitiveCommandNode { + + public ByteCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @NotNull Byte parse(@NotNull String input) throws InputParsingException { + try { + return Byte.parseByte(input); + } catch (NumberFormatException e) { + throw new InputParsingException("Could not parse as byte: " + input, e); + } + } + + @Override + public @NotNull ByteCommandNode withChildren(@NotNull List> children) { + return new ByteCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull ByteCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new ByteCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull ByteCommandNode withPermission(@Nullable String permission) { + return new ByteCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/DoubleCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/DoubleCommandNode.java new file mode 100644 index 00000000..d27e8c53 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/DoubleCommandNode.java @@ -0,0 +1,43 @@ +package com.wizardlybump17.wlib.command.node.primitive.number; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.PrimitiveCommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class DoubleCommandNode extends NumberCommandNode implements PrimitiveCommandNode { + + public DoubleCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @NotNull Double parse(@NotNull String input) throws InputParsingException { + try { + return Double.parseDouble(input); + } catch (NumberFormatException e) { + throw new InputParsingException("Could not parse as double: " + input, e); + } + } + + @Override + public @NotNull DoubleCommandNode withChildren(@NotNull List> children) { + return new DoubleCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull DoubleCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new DoubleCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull DoubleCommandNode withPermission(@Nullable String permission) { + return new DoubleCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/FloatCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/FloatCommandNode.java new file mode 100644 index 00000000..e0f3baa0 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/FloatCommandNode.java @@ -0,0 +1,43 @@ +package com.wizardlybump17.wlib.command.node.primitive.number; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.PrimitiveCommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class FloatCommandNode extends NumberCommandNode implements PrimitiveCommandNode { + + public FloatCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @NotNull Float parse(@NotNull String input) throws InputParsingException { + try { + return Float.parseFloat(input); + } catch (NumberFormatException e) { + throw new InputParsingException("Could not parse as float: " + input, e); + } + } + + @Override + public @NotNull FloatCommandNode withChildren(@NotNull List> children) { + return new FloatCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull FloatCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new FloatCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull FloatCommandNode withPermission(@Nullable String permission) { + return new FloatCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/IntegerCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/IntegerCommandNode.java new file mode 100644 index 00000000..89c26e53 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/IntegerCommandNode.java @@ -0,0 +1,43 @@ +package com.wizardlybump17.wlib.command.node.primitive.number; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.PrimitiveCommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class IntegerCommandNode extends NumberCommandNode implements PrimitiveCommandNode { + + public IntegerCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @NotNull Integer parse(@NotNull String input) throws InputParsingException { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new InputParsingException("Could not parse as int: " + input, e); + } + } + + @Override + public @NotNull IntegerCommandNode withChildren(@NotNull List> children) { + return new IntegerCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull IntegerCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new IntegerCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull IntegerCommandNode withPermission(@Nullable String permission) { + return new IntegerCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/LongCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/LongCommandNode.java new file mode 100644 index 00000000..8bad6eda --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/LongCommandNode.java @@ -0,0 +1,43 @@ +package com.wizardlybump17.wlib.command.node.primitive.number; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.PrimitiveCommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class LongCommandNode extends NumberCommandNode implements PrimitiveCommandNode { + + public LongCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @NotNull Long parse(@NotNull String input) throws InputParsingException { + try { + return Long.parseLong(input); + } catch (NumberFormatException e) { + throw new InputParsingException("Could not parse as short: " + input, e); + } + } + + @Override + public @NotNull LongCommandNode withChildren(@NotNull List> children) { + return new LongCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull LongCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new LongCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull LongCommandNode withPermission(@Nullable String permission) { + return new LongCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/NumberCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/NumberCommandNode.java new file mode 100644 index 00000000..4b807681 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/NumberCommandNode.java @@ -0,0 +1,26 @@ +package com.wizardlybump17.wlib.command.node.primitive.number; + +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public abstract class NumberCommandNode extends CommandNode { + + public NumberCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public abstract @NotNull NumberCommandNode withChildren(@NotNull List> children); + + @Override + public abstract @NotNull NumberCommandNode withExecutor(@Nullable CommandNodeExecutor executor); + + @Override + public abstract @NotNull NumberCommandNode withPermission(@Nullable String permission); +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/ShortCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/ShortCommandNode.java new file mode 100644 index 00000000..c45cdd7d --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/primitive/number/ShortCommandNode.java @@ -0,0 +1,43 @@ +package com.wizardlybump17.wlib.command.node.primitive.number; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.primitive.PrimitiveCommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ShortCommandNode extends NumberCommandNode implements PrimitiveCommandNode { + + public ShortCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @NotNull Short parse(@NotNull String input) throws InputParsingException { + try { + return Short.parseShort(input); + } catch (NumberFormatException e) { + throw new InputParsingException("Could not parse as short: " + input, e); + } + } + + @Override + public @NotNull ShortCommandNode withChildren(@NotNull List> children) { + return new ShortCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull ShortCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new ShortCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull ShortCommandNode withPermission(@Nullable String permission) { + return new ShortCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/node/string/StringCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/node/string/StringCommandNode.java new file mode 100644 index 00000000..eedeb82a --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/node/string/StringCommandNode.java @@ -0,0 +1,39 @@ +package com.wizardlybump17.wlib.command.node.string; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; + +public class StringCommandNode extends CommandNode { + + public StringCommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @Nullable String parse(@NotNull String input) throws InputParsingException { + return input; + } + + @Override + public @NotNull StringCommandNode withChildren(@NotNull List> children) { + return new StringCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull StringCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new StringCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull StringCommandNode withPermission(@Nullable String permission) { + return new StringCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/registry/MethodCommandNodeFactoryRegistry.java b/commands/src/main/java/com/wizardlybump17/wlib/command/registry/MethodCommandNodeFactoryRegistry.java new file mode 100644 index 00000000..f8df2b2e --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/registry/MethodCommandNodeFactoryRegistry.java @@ -0,0 +1,97 @@ +package com.wizardlybump17.wlib.command.registry; + +import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.extractor.method.factory.object.UUIDMethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.extractor.method.factory.primitive.BooleanMethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.extractor.method.factory.primitive.CharacterMethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.extractor.method.factory.primitive.NumberMethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.extractor.method.factory.string.StringMethodCommandNodeFactory; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public final class MethodCommandNodeFactoryRegistry { + + private final @NotNull Map, MethodCommandNodeFactory> factories = new HashMap<>(); + + public void addFactory(@NotNull Class clazz, @NotNull MethodCommandNodeFactory factory) { + factories.put(clazz, factory); + } + + public void addFactory(@NotNull MethodCommandNodeFactory factory, @NotNull Class ... classes) { + if (classes.length < 1) + throw new IllegalArgumentException("The classes array must contain at least one element"); + for (Class clazz : classes) + addFactory(clazz, factory); + } + + public void addFactory(@NotNull MethodCommandNodeFactory factory) { + addFactory(factory, factory.getSupportedTypes()); + } + + public @Nullable MethodCommandNodeFactory getFactory(@NotNull Class clazz) { + return factories.get(clazz); + } + + public void removeFactory(@NotNull Class clazz) { + factories.remove(clazz); + } + + public void removeFactory(@NotNull Class ... classes) { + if (classes.length < 1) + throw new IllegalArgumentException("The classes array must contain at least one element"); + for (Class clazz : classes) + removeFactory(clazz); + } + + public boolean hasFactory(@NotNull Class clazz) { + return factories.containsKey(clazz); + } + + public @NotNull @Unmodifiable Map, MethodCommandNodeFactory> getFactories() { + return Collections.unmodifiableMap(factories); + } + + public void clear() { + factories.clear(); + } + + @ApiStatus.Internal + public void registerDefaults() { + addFactory(new BooleanMethodCommandNodeFactory()); + addFactory(new CharacterMethodCommandNodeFactory()); + addFactory(new NumberMethodCommandNodeFactory()); + addFactory(new StringMethodCommandNodeFactory()); + addFactory(new UUIDMethodCommandNodeFactory()); + } + + @ApiStatus.Internal + public void unregisterDefaults() { + removeFactory( + boolean.class, + Boolean.class, + char.class, + Character.class, + byte.class, + Byte.class, + short.class, + Short.class, + int.class, + Integer.class, + long.class, + Long.class, + float.class, + Float.class, + double.class, + Double.class, + String.class, + UUID.class + ); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/result/CommandErrorCodes.java b/commands/src/main/java/com/wizardlybump17/wlib/command/result/CommandErrorCodes.java new file mode 100644 index 00000000..d31ec54c --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/result/CommandErrorCodes.java @@ -0,0 +1,31 @@ +package com.wizardlybump17.wlib.command.result; + +import org.jetbrains.annotations.NotNull; + +public final class CommandErrorCodes { + + private CommandErrorCodes() { + } + + public static final @NotNull String FORBIDDEN_NO_PERMISSION = "WLib:Forbidden/NoPermission"; + public static final @NotNull String FORBIDDEN_GENERIC = "WLib:Forbidden/Generic"; + + public static final @NotNull String NOT_FOUND_NODE_NOT_FOUND = "WLib:NotFound/NodeNotFound"; + public static final @NotNull String NOT_FOUND_GENERIC = "WLib:NotFound/Generic"; + + public static final @NotNull String NOT_IMPLEMENTED_NO_COMMAND_EXECUTOR = "WLib:NotImplemented/NoCommandExecutor"; + + public static final @NotNull String BAD_REQUEST_GENERIC = "WLib:BadRequest/Generic"; + public static final @NotNull String BAD_REQUEST_PARSE_ERROR = "WLib:BadRequest/ParseError"; + public static final @NotNull String BAD_REQUEST_EMPTY_INPUT = "WLib:BadRequest/EmptyInput"; + + public static final @NotNull String UNPROCESSABLE_CONTENT_INVALID_INPUT = "WLib:UnprocessableContent/InvalidInput"; + + public static final @NotNull String CONFLICT_GENERIC = "WLib:Conflict/Generic"; + + public static final @NotNull String GENERIC_ERROR_GENERIC = "WLib:GenericError/Generic"; + + public static final @NotNull String INVALID_SENDER_GENERIC = "WLib:InvalidSender/Generic"; + + public static final @NotNull String UNAUTHORIZED_GENERIC = "WLib:Unauthorized/Generic"; +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/result/CommandResult.java b/commands/src/main/java/com/wizardlybump17/wlib/command/result/CommandResult.java new file mode 100644 index 00000000..c2f6e649 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/result/CommandResult.java @@ -0,0 +1,212 @@ +package com.wizardlybump17.wlib.command.result; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.exception.InvalidInputException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public final class CommandResult { + + private final @Nullable T data; + private final @NotNull Type type; + private final @Nullable ErrorDetails errorDetails; + + private CommandResult(@Nullable T data, @NotNull Type type, @Nullable ErrorDetails errorDetails) { + this.data = data; + this.type = type; + this.errorDetails = errorDetails; + } + + public boolean success() { + return type.isSuccess(); + } + + public @Nullable T data() { + return data; + } + + public @NotNull Type type() { + return type; + } + + public @Nullable ErrorDetails errorDetails() { + return errorDetails; + } + + @Override + public boolean equals(@Nullable Object other) { + if (other == null || getClass() != other.getClass()) + return false; + CommandResult that = (CommandResult) other; + return Objects.equals(data, that.data) && Objects.equals(type, that.type) && Objects.equals(errorDetails, that.errorDetails); + } + + @Override + public int hashCode() { + return Objects.hash(data, type, errorDetails); + } + + @Override + public String toString() { + return "CommandResult{" + + "data=" + data + + ", type='" + type + '\'' + + ", errorDetails=" + errorDetails + + '}'; + } + + //SUCCESS + + public static @NotNull CommandResult successful() { + return new CommandResult<>(null, Type.SUCCESS, null); + } + + public static @NotNull CommandResult successful(@Nullable T data) { + return new CommandResult<>(data, Type.SUCCESS, null); + } + + public static @NotNull CommandResult noContent() { + return new CommandResult<>(null, Type.NO_CONTENT, null); + } + + public static @NotNull CommandResult noContent(@Nullable T data) { + return new CommandResult<>(data, Type.NO_CONTENT, null); + } + + //ERROR + + public static @NotNull CommandResult badRequest() { + return new CommandResult<>(null, Type.BAD_REQUEST, ErrorDetails.BAD_REQUEST); + } + + public static @NotNull CommandResult badRequest(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.BAD_REQUEST, errorDetails); + } + + public static @NotNull CommandResult conflict() { + return new CommandResult<>(null, Type.CONFLICT, ErrorDetails.CONFLICT); + } + + public static @NotNull CommandResult conflict(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.CONFLICT, errorDetails); + } + + public static @NotNull CommandResult forbidden() { + return new CommandResult<>(null, Type.FORBIDDEN, ErrorDetails.FORBIDDEN); + } + + public static @NotNull CommandResult forbidden(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.FORBIDDEN, errorDetails); + } + + public static @NotNull CommandResult genericError() { + return new CommandResult<>(null, Type.GENERIC_ERROR, ErrorDetails.GENERIC_ERROR); + } + + public static @NotNull CommandResult genericError(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.GENERIC_ERROR, errorDetails); + } + + public static @NotNull CommandResult invalidSender() { + return new CommandResult<>(null, Type.INVALID_SENDER, ErrorDetails.INVALID_SENDER); + } + + public static @NotNull CommandResult invalidSender(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.INVALID_SENDER, errorDetails); + } + + public static @NotNull CommandResult notFound() { + return new CommandResult<>(null, Type.NOT_FOUND, ErrorDetails.NOT_FOUND); + } + + public static @NotNull CommandResult notFound(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.NOT_FOUND, errorDetails); + } + + public static @NotNull CommandResult unauthorized() { + return new CommandResult<>(null, Type.UNAUTHORIZED, ErrorDetails.UNAUTHORIZED); + } + + public static @NotNull CommandResult unauthorized(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.UNAUTHORIZED, errorDetails); + } + + public static @NotNull CommandResult unprocessableContent() { + return new CommandResult<>(null, Type.UNPROCESSABLE_CONTENT, ErrorDetails.UNPROCESSABLE_CONTENT); + } + + public static @NotNull CommandResult unprocessableContent(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.UNPROCESSABLE_CONTENT, errorDetails); + } + + public static @NotNull CommandResult notImplemented() { + return new CommandResult<>(null, Type.NOT_IMPLEMENTED, ErrorDetails.NOT_IMPLEMENTED); + } + + public static @NotNull CommandResult notImplemented(@NotNull ErrorDetails errorDetails) { + return new CommandResult<>(null, Type.NOT_IMPLEMENTED, errorDetails); + } + + public enum Type { + + SUCCESS, + NO_CONTENT, + BAD_REQUEST, + CONFLICT, + FORBIDDEN, + GENERIC_ERROR, + INVALID_SENDER, + NOT_FOUND, + UNAUTHORIZED, + UNPROCESSABLE_CONTENT, + NOT_IMPLEMENTED; + + public boolean isSuccess() { + return switch (this) { + case SUCCESS, NO_CONTENT -> true; + default -> false; + }; + } + } + + public record ErrorDetails(@NotNull String code, @NotNull String message, @NotNull String detail) { + + public static final @NotNull ErrorDetails BAD_REQUEST = new ErrorDetails(CommandErrorCodes.BAD_REQUEST_GENERIC, "Bad request", "Bad request"); + public static final @NotNull ErrorDetails CONFLICT = new ErrorDetails(CommandErrorCodes.CONFLICT_GENERIC, "Conflict", "The request could not be completed due to a conflict with the current state of the target resource"); + public static final @NotNull ErrorDetails FORBIDDEN = new ErrorDetails(CommandErrorCodes.FORBIDDEN_GENERIC, "Forbidden", "Access to the requested resource is forbidden"); + public static final @NotNull ErrorDetails GENERIC_ERROR = new ErrorDetails(CommandErrorCodes.GENERIC_ERROR_GENERIC, "Internal server error", "An unexpected error occurred while processing the request"); + public static final @NotNull ErrorDetails INVALID_SENDER = new ErrorDetails(CommandErrorCodes.INVALID_SENDER_GENERIC, "Invalid sender", "The sender of the command is invalid or not permitted"); + public static final @NotNull ErrorDetails NOT_FOUND = new ErrorDetails(CommandErrorCodes.NOT_FOUND_GENERIC, "Not found", "The requested resource could not be found"); + public static final @NotNull ErrorDetails UNAUTHORIZED = new ErrorDetails(CommandErrorCodes.UNAUTHORIZED_GENERIC, "Unauthorized", "Authentication is required and has failed or has not been provided"); + public static final @NotNull ErrorDetails UNPROCESSABLE_CONTENT = new ErrorDetails(CommandErrorCodes.UNPROCESSABLE_CONTENT_INVALID_INPUT, "Unprocessable content", "The request was well-formed but contained semantic errors"); + public static final @NotNull ErrorDetails NOT_IMPLEMENTED = new ErrorDetails(CommandErrorCodes.NOT_IMPLEMENTED_NO_COMMAND_EXECUTOR, "Not implemented", "The requested functionality is not implemented"); + + private static final @NotNull ErrorDetails EMPTY_INPUT = new ErrorDetails(CommandErrorCodes.BAD_REQUEST_EMPTY_INPUT, "The input can not be empty", "The input can not be empty"); + + public static @NotNull ErrorDetails noCommandExecutor(@NotNull String command, @NotNull String node) { + return new ErrorDetails(CommandErrorCodes.NOT_IMPLEMENTED_NO_COMMAND_EXECUTOR, "No executor found for this command", "The command \"" + command + "\" does not have an executor at the node \"" + node + "\""); + } + + public static @NotNull ErrorDetails parseError(@NotNull String command, @NotNull String node, @NotNull InputParsingException exception) { + return new ErrorDetails(CommandErrorCodes.BAD_REQUEST_PARSE_ERROR, "Error while parsing the input", "Error while parsing the input at the command \"" + command + "\", node \"" + node + "\": " + exception.getMessage()); + } + + public static @NotNull ErrorDetails inputError(@NotNull String command, @NotNull String node, @Nullable String input, @NotNull InvalidInputException exception) { + return new ErrorDetails(CommandErrorCodes.UNPROCESSABLE_CONTENT_INVALID_INPUT, "Input not accepted", "The input \"" + input + "\" is not accepted by \"" + node + "\" at \"" + command + "\": " + exception.getMessage()); + } + + public static @NotNull ErrorDetails noPermission(@NotNull String sender, @NotNull String permission) { + return new ErrorDetails(CommandErrorCodes.FORBIDDEN_NO_PERMISSION, "Not enough permissions", sender + " does not have the permission \"" + permission + "\""); + } + + public static @NotNull ErrorDetails emptyInput() { + return EMPTY_INPUT; + } + + public static @NotNull ErrorDetails nodeNotFound(int index, @Nullable String node) { + return new ErrorDetails(CommandErrorCodes.NOT_FOUND_NODE_NOT_FOUND, "Node not found", "Could not find a node for \"" + node + "\" at index " + index); + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/sender/BasicCommandSender.java b/commands/src/main/java/com/wizardlybump17/wlib/command/sender/BasicCommandSender.java new file mode 100644 index 00000000..f236d3d2 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/sender/BasicCommandSender.java @@ -0,0 +1,180 @@ +package com.wizardlybump17.wlib.command.sender; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class BasicCommandSender implements CommandSender { + + private final @NotNull S handle; + private final @NotNull String name; + private final @NotNull UUID id; + private final @NotNull Consumer messageConsumer; + private final @NotNull Predicate permissionTest; + + public BasicCommandSender(@NotNull S handle, @NotNull String name, @NotNull UUID id, @NotNull Consumer messageConsumer, @NotNull Predicate permissionTest) { + this.handle = handle; + this.name = name; + this.id = id; + this.messageConsumer = messageConsumer; + this.permissionTest = permissionTest; + } + + @Override + public @NotNull S getHandle() { + return handle; + } + + @Override + public void sendMessage(String message) { + messageConsumer.accept(message); + } + + @Override + public void sendMessage(String... messages) { + sendMessage(String.join("\n", messages)); + } + + @Override + public void sendMessage(@Nullable Object message) { + sendMessage(String.valueOf(message)); + } + + @Override + public void sendMessage(@Nullable Object @Nullable ... messages) { + if (messages == null) + sendMessage((Object) null); + else + sendMessage(Arrays.stream(messages).map(String::valueOf).collect(Collectors.joining("\n"))); + } + + @Override + public @NotNull String getName() { + return name; + } + + @Override + public boolean hasPermission(String permission) { + return permissionTest.test(permission); + } + + @Override + public boolean hasId(@NotNull UUID id) { + return id.equals(this.id); + } + + @Override + public @NotNull UUID getId() { + return id; + } + + public @NotNull Consumer getMessageConsumer() { + return messageConsumer; + } + + public @NotNull Predicate getPermissionTest() { + return permissionTest; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + BasicCommandSender that = (BasicCommandSender) o; + return Objects.equals(handle, that.handle) && Objects.equals(name, that.name) && Objects.equals(id, that.id) + && Objects.equals(messageConsumer, that.messageConsumer) && Objects.equals(permissionTest, that.permissionTest); + } + + @Override + public int hashCode() { + return Objects.hash(handle, name, id, messageConsumer, permissionTest); + } + + @Override + public String toString() { + return "BasicCommandSender{" + + "handle=" + handle + + ", name='" + name + '\'' + + ", id=" + id + + ", messageConsumer=" + messageConsumer + + ", permissionTest=" + permissionTest + + '}'; + } + + public static Builder builder() { + return new Builder<>(); + } + + public static class Builder { + + private @Nullable S handle; + private @Nullable String name; + private @Nullable UUID id; + private @Nullable Consumer messageConsumer; + private @Nullable Predicate permissionTest; + + protected Builder() { + } + + public @Nullable S handle() { + return handle; + } + + public @NotNull Builder handle(@Nullable S handle) { + this.handle = handle; + return this; + } + + public @Nullable String name() { + return name; + } + + public @NotNull Builder name(@Nullable String name) { + this.name = name; + return this; + } + + public @Nullable UUID id() { + return id; + } + + public @NotNull Builder id(@Nullable UUID id) { + this.id = id; + return this; + } + + public @Nullable Consumer messageConsumer() { + return messageConsumer; + } + + public @NotNull Builder messageConsumer(@Nullable Consumer messageConsumer) { + this.messageConsumer = messageConsumer; + return this; + } + + public @Nullable Predicate permissionTest() { + return permissionTest; + } + + public @NotNull Builder permissionTest(@Nullable Predicate permissionTest) { + this.permissionTest = permissionTest; + return this; + } + + public @NotNull BasicCommandSender build() { + return new BasicCommandSender<>( + Objects.requireNonNull(handle, "the 'handler' can not be null"), + Objects.requireNonNull(name, "the 'name' can not be null"), + Objects.requireNonNull(id, "the 'id' can not be null"), + messageConsumer == null ? System.out::println : messageConsumer, + permissionTest == null ? permission -> true : permissionTest + ); + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandSender.java b/commands/src/main/java/com/wizardlybump17/wlib/command/sender/CommandSender.java similarity index 53% rename from commands/src/main/java/com/wizardlybump17/wlib/command/CommandSender.java rename to commands/src/main/java/com/wizardlybump17/wlib/command/sender/CommandSender.java index c4b89aa3..70ff8acd 100644 --- a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandSender.java +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/sender/CommandSender.java @@ -1,7 +1,10 @@ -package com.wizardlybump17.wlib.command; +package com.wizardlybump17.wlib.command.sender; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.UUID; + /** * Represents a command sender, someone that can trigger commands. * @@ -18,17 +21,18 @@ public interface CommandSender { void sendMessage(String... messages); + void sendMessage(@Nullable Object message); + + void sendMessage(@Nullable Object @Nullable ... messages); + String getName(); boolean hasPermission(String permission); + boolean hasId(@NotNull UUID id); + /** - * Used to convert this CommandSender to a generic sender, a command sender that can be anything - * - * @return the generic sender + * @throws IllegalStateException if the sender does not have an ID */ - @Nullable - default CommandSender toGeneric() { - return null; - } + @NotNull UUID getId() throws IllegalStateException; } diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/AbstractValuesSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/AbstractValuesSuggester.java new file mode 100644 index 00000000..a1c895a6 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/AbstractValuesSuggester.java @@ -0,0 +1,47 @@ +package com.wizardlybump17.wlib.command.suggestion; + +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Objects; + +public abstract class AbstractValuesSuggester implements ValuesSuggester { + + private final @NotNull List suggestions; + + public AbstractValuesSuggester(@NotNull List suggestions) { + this.suggestions = List.copyOf(suggestions); + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return suggestions; + } + + @Override + public @NotNull List values() { + return suggestions; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AbstractValuesSuggester that = (AbstractValuesSuggester) object; + return Objects.equals(suggestions, that.suggestions); + } + + @Override + public int hashCode() { + return Objects.hashCode(suggestions); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "suggestions=" + suggestions + + '}'; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/CachingSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/CachingSuggester.java new file mode 100644 index 00000000..a64681ea --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/CachingSuggester.java @@ -0,0 +1,62 @@ +package com.wizardlybump17.wlib.command.suggestion; + +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collections; +import java.util.List; + +public class CachingSuggester implements Suggester { + + public static final long DEFAULT_EXPIRATION_MILLIS = 30 * 1000; + + private final @NotNull Suggester delegate; + private final long expirationMillis; + private @NotNull @UnmodifiableView List cache = List.of(); + private long lastUpdateMillis; + + private CachingSuggester(@NotNull Suggester delegate, long expirationMillis) { + this.delegate = delegate; + this.expirationMillis = expirationMillis; + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) throws SuggesterException { + if (lastUpdateMillis + expirationMillis <= System.currentTimeMillis()) { + List suggestions = delegate.getSuggestions(sender, input, current, currentNode); + cache = Collections.unmodifiableList(suggestions); + lastUpdateMillis = System.currentTimeMillis(); + return suggestions; + } + + return cache; + } + + @Override + public @NotNull String getStringRepresentation(@NotNull T value) { + return delegate.getStringRepresentation(value); + } + + public long expirationMillis() { + return expirationMillis; + } + + public @NotNull @UnmodifiableView List cache() { + return cache; + } + + private long lastUpdateMillis() { + return lastUpdateMillis; + } + + public static @NotNull CachingSuggester of(@NotNull Suggester suggester, long expirationMillis) { + return new CachingSuggester<>(suggester, expirationMillis); + } + + public static @NotNull CachingSuggester of(@NotNull Suggester suggester) { + return new CachingSuggester<>(suggester, DEFAULT_EXPIRATION_MILLIS); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/LiteralSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/LiteralSuggester.java new file mode 100644 index 00000000..6df01e63 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/LiteralSuggester.java @@ -0,0 +1,47 @@ +package com.wizardlybump17.wlib.command.suggestion; + +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Objects; + +public final class LiteralSuggester implements Suggester { + + private final @NotNull List suggestions; + + private LiteralSuggester(@NotNull String value) { + this.suggestions = List.of(value); + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) throws SuggesterException { + return suggestions; + } + + public static @NotNull LiteralSuggester of(@NotNull String value) { + return new LiteralSuggester(value); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + LiteralSuggester that = (LiteralSuggester) o; + return Objects.equals(suggestions, that.suggestions); + } + + @Override + public int hashCode() { + return Objects.hashCode(suggestions); + } + + @Override + public String toString() { + return "LiteralSuggester{" + + "value=" + suggestions.getFirst() + + '}'; + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/Suggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/Suggester.java new file mode 100644 index 00000000..f7ca1ed3 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/Suggester.java @@ -0,0 +1,39 @@ +package com.wizardlybump17.wlib.command.suggestion; + +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; + +public interface Suggester { + + @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) throws SuggesterException; + + default @NotNull String getStringRepresentation(@NotNull T value) { + return value.toString(); + } + + default boolean needsEscape() { + return false; + } + + static ValuesSuggester.@NotNull Values values(@NotNull List values, @Nullable Function stringRepresentation, boolean needsEscape) { + return new ValuesSuggester.Values<>(values, stringRepresentation, needsEscape); + } + + static ValuesSuggester.@NotNull Values values(@NotNull List values, @Nullable Function stringRepresentation) { + return Suggester.values(values, stringRepresentation, false); + } + + static ValuesSuggester.@NotNull Values values(@NotNull List values, boolean needsEscape) { + return Suggester.values(values, null, needsEscape); + } + + static ValuesSuggester.@NotNull Values values(@NotNull List values) { + return Suggester.values(values, null, false); + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/ValuesSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/ValuesSuggester.java new file mode 100644 index 00000000..5ac20908 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/ValuesSuggester.java @@ -0,0 +1,78 @@ +package com.wizardlybump17.wlib.command.suggestion; + +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +public interface ValuesSuggester extends Suggester { + + @NotNull List values(); + + final class Values implements ValuesSuggester { + + private final @NotNull List values; + private final @Nullable Function stringRepresentationFunction; + private final boolean needsEscape; + + Values(@NotNull List values, @Nullable Function stringRepresentationFunction, boolean needsEscape) { + this.values = List.copyOf(values); + this.stringRepresentationFunction = stringRepresentationFunction; + this.needsEscape = needsEscape; + } + + @Override + public @NotNull @Unmodifiable List values() { + return values; + } + + public @Nullable Function stringRepresentation() { + return stringRepresentationFunction; + } + + @Override + public boolean needsEscape() { + return needsEscape; + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) throws SuggesterException { + return values; + } + + @Override + public @NotNull String getStringRepresentation(@NotNull T value) { + return stringRepresentationFunction == null ? ValuesSuggester.super.getStringRepresentation(value) : stringRepresentationFunction.apply(value); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + Values values1 = (Values) o; + return Objects.equals(values, values1.values) + && Objects.equals(stringRepresentationFunction, values1.stringRepresentationFunction) + && needsEscape == values1.needsEscape; + } + + @Override + public int hashCode() { + return Objects.hash(values, stringRepresentationFunction, needsEscape); + } + + @Override + public String toString() { + return "ValuesSuggester$Values{" + + "values=" + values + + ", stringRepresentationFunction=" + stringRepresentationFunction + + ", needsEscape=" + needsEscape + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/EnumSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/EnumSuggester.java new file mode 100644 index 00000000..66cd5e27 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/EnumSuggester.java @@ -0,0 +1,66 @@ +package com.wizardlybump17.wlib.command.suggestion.object; + +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Objects; + +public interface EnumSuggester> extends Suggester { + + @Override + default @NotNull String getStringRepresentation(@NotNull E value) { + return value.name(); + } + + @NotNull Class enumType(); + + static > @NotNull Any any(@NotNull Class enumType) { + return new Any<>(enumType); + } + + final class Any> implements EnumSuggester { + + private final @NotNull List cache; + private final @NotNull Class enumType; + + private Any(@NotNull Class enumType) { + this.enumType = enumType; + this.cache = List.of(enumType.getEnumConstants()); + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) throws SuggesterException { + return cache; + } + + @Override + public @NotNull Class enumType() { + return enumType; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + Any any = (Any) o; + return Objects.equals(cache, any.cache) && Objects.equals(enumType, any.enumType); + } + + @Override + public int hashCode() { + return Objects.hash(cache, enumType); + } + + @Override + public String toString() { + return "EnumSuggester$Any{" + + "cache=" + cache + + ", enumType=" + enumType + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/JsonElementSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/JsonElementSuggester.java new file mode 100644 index 00000000..6c6d514a --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/JsonElementSuggester.java @@ -0,0 +1,123 @@ +package com.wizardlybump17.wlib.command.suggestion.object; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.Objects; + +public interface JsonElementSuggester extends Suggester { + + @NotNull Gson gson(); + + @Override + default boolean needsEscape() { + return true; + } + + static @NotNull Values values(@NotNull Gson gson, @NotNull List elements) { + return new Values(gson, elements); + } + + static @NotNull Values values(@NotNull List elements) { + return new Values(Values.GSON, elements); + } + + static @NotNull Value value(@NotNull Gson gson, @NotNull JsonElement value) { + return new Value(gson, value); + } + + static @NotNull Value value(@NotNull JsonElement value) { + return new Value(Values.GSON, value); + } + + final class Values extends AbstractValuesSuggester implements JsonElementSuggester { + + private static final @NotNull Gson GSON = new Gson(); + + private final @NotNull Gson gson; + + private Values(@NotNull Gson gson, @NotNull List suggestions) { + super(suggestions); + this.gson = gson; + } + + @Override + public @NotNull Gson gson() { + return gson; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) return false; + JsonElementSuggester.Values values = (JsonElementSuggester.Values) o; + return Objects.equals(gson, values.gson); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), gson); + } + + @Override + public String toString() { + return "JsonElementSuggester$Values{" + + "gson=" + gson + + "} " + super.toString(); + } + } + + final class Value implements JsonElementSuggester { + + private final @NotNull Gson gson; + private final @NotNull @Unmodifiable List list; + + private Value(@NotNull Gson gson, @NotNull JsonElement value) { + this.gson = gson; + this.list = List.of(value); + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return list; + } + + @Override + public @NotNull Gson gson() { + return gson; + } + + public @NotNull JsonElement value() { + return list.getFirst(); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + Value value = (Value) o; + return Objects.equals(gson, value.gson) && Objects.equals(list, value.list); + } + + @Override + public int hashCode() { + return Objects.hash(gson, list); + } + + @Override + public String toString() { + return "Value{" + + "gson=" + gson + + ", value=" + value() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/UUIDSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/UUIDSuggester.java new file mode 100644 index 00000000..062a1c24 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/object/UUIDSuggester.java @@ -0,0 +1,37 @@ +package com.wizardlybump17.wlib.command.suggestion.object; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.UUID; + +public interface UUIDSuggester extends Suggester { + + static @NotNull Values value(@NotNull UUID value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(UUID... values) { + return new Values(List.of(values)); + } + + final class Values extends AbstractValuesSuggester implements UUIDSuggester { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "UUIDSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/BooleanSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/BooleanSuggester.java new file mode 100644 index 00000000..e79cd8d6 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/BooleanSuggester.java @@ -0,0 +1,79 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive; + +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface BooleanSuggester extends PrimitiveSuggester { + + static @NotNull True trueValue() { + return True.INSTANCE; + } + + static @NotNull False falseValue() { + return False.INSTANCE; + } + + static @NotNull Any any() { + return Any.INSTANCE; + } + + final class True implements BooleanSuggester { + + private static final @NotNull List LIST = List.of(true); + private static final @NotNull True INSTANCE = new True(); + + private True() { + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return LIST; + } + + @Override + public String toString() { + return "BooleanSuggester$True{}"; + } + } + + final class False implements BooleanSuggester { + + private static final @NotNull List LIST = List.of(false); + private static final @NotNull False INSTANCE = new False(); + + private False() { + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return LIST; + } + + @Override + public String toString() { + return "BooleanSuggester$False{}"; + } + } + + final class Any implements BooleanSuggester { + + private static final @NotNull List LIST = List.of(true, false); + private static final @NotNull Any INSTANCE = new Any(); + + private Any() { + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return LIST; + } + + @Override + public String toString() { + return "BooleanSuggester$Any{}"; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/CharacterSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/CharacterSuggester.java new file mode 100644 index 00000000..a4b10a12 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/CharacterSuggester.java @@ -0,0 +1,58 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public interface CharacterSuggester extends PrimitiveSuggester { + + static @NotNull Values value(char value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(char @NotNull ... values) { + List list = new ArrayList<>(values.length); + for (char value : values) + list.add(value); + return new Values(List.copyOf(list)); + } + + static @NotNull Values range(char from, char to, int amount) { + if (to - from < amount) { + Character[] values = new Character[(int) (to - from)]; + for (int i = 0; i < values.length; i++) + values[i] = (char) (to - from - i); + return new Values(List.of(values)); + } + + char part = (char) ((to - from) / amount); + List values = new ArrayList<>(amount); + for (char i = 0; i < amount; i++) + values.add((char) (from + part * amount)); + return new Values(List.copyOf(values)); + } + + static @NotNull Values range(char from, char to) { + return range(from, to, 4); + } + + final class Values extends AbstractValuesSuggester implements CharacterSuggester { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "CharacterSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/PrimitiveSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/PrimitiveSuggester.java new file mode 100644 index 00000000..568178db --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/PrimitiveSuggester.java @@ -0,0 +1,6 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive; + +import com.wizardlybump17.wlib.command.suggestion.Suggester; + +public interface PrimitiveSuggester extends Suggester { +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/ByteSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/ByteSuggester.java new file mode 100644 index 00000000..c2f2ccde --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/ByteSuggester.java @@ -0,0 +1,93 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive.number; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.PrimitiveSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public interface ByteSuggester extends PrimitiveSuggester, NumberSuggester { + + static @NotNull Values value(byte value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(byte @NotNull ... values) { + List list = new ArrayList<>(values.length); + for (byte value : values) + list.add(value); + return new Values(List.copyOf(list)); + } + + static @NotNull Values range(byte from, byte to, int amount) { + if (to - from < amount) { + Byte[] values = new Byte[to - from]; + for (int i = 0; i < values.length; i++) + values[i] = (byte) (to - from - i); + return new Values(List.of(values)); + } + + byte part = (byte) ((to - from) / amount); + List values = new ArrayList<>(amount); + for (int i = 0; i < amount; i++) + values.add((byte) (from + part * amount)); + return new Values(List.copyOf(values)); + } + + static @NotNull Values range(byte from, byte to) { + return range(from, to, 4); + } + + static @NotNull Values positive() { + return Values.POSITIVE; + } + + static @NotNull Values negative() { + return Values.NEGATIVE; + } + + static @NotNull Values unlimited() { + return Values.UNLIMITED; + } + + final class Values extends AbstractValuesSuggester implements ByteSuggester { + + private static final @NotNull ByteSuggester.Values POSITIVE = new ByteSuggester.Values(List.of( + (byte) 0, + (byte) 50, + (byte) 100, + (byte) 127 + )); + private static final @NotNull ByteSuggester.Values NEGATIVE = new ByteSuggester.Values(List.of( + (byte) -128, + (byte) -100, + (byte) -50, + (byte) -1 + )); + private static final @NotNull ByteSuggester.Values UNLIMITED = new ByteSuggester.Values(List.of( + (byte) -128, + (byte) -100, + (byte) -50, + (byte) 0, + (byte) 50, + (byte) 100, + (byte) 127 + )); + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "ByteSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/DoubleSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/DoubleSuggester.java new file mode 100644 index 00000000..58e45291 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/DoubleSuggester.java @@ -0,0 +1,90 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive.number; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.PrimitiveSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public interface DoubleSuggester extends PrimitiveSuggester, NumberSuggester { + + static @NotNull Values value(double value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(double @NotNull ... values) { + List list = new ArrayList<>(values.length); + for (double value : values) + list.add(value); + return new Values(List.copyOf(list)); + } + + static @NotNull Values range(double from, double to, int amount) { + double part = (to - from) / amount; + List values = new ArrayList<>(amount); + for (double i = 0; i < amount; i++) + values.add(from + part * amount); + return new Values(List.copyOf(values)); + } + + static @NotNull Values range(double from, double to) { + return range(from, to, 4); + } + + static @NotNull Values positive() { + return new Values(List.of( + 0.0, + 50.5, + 200.5, + 3000.5, + 50000.5, + 100000.5 + )); + } + + static @NotNull Values negative() { + return new Values(List.of( + -100000.5, + -50000.5, + -3000.5, + -200.5, + -50.5, + -1.0 + )); + } + + static @NotNull Values unlimited() { + return new Values(List.of( + -100000.5, + -50000.5, + -3000.5, + -200.5, + -50.5, + 0.0, + 50.5, + 200.5, + 3000.5, + 50000.5, + 100000.5 + )); + } + + final class Values extends AbstractValuesSuggester implements DoubleSuggester { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "DoubleSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/FloatSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/FloatSuggester.java new file mode 100644 index 00000000..d72d740d --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/FloatSuggester.java @@ -0,0 +1,90 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive.number; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.PrimitiveSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public interface FloatSuggester extends PrimitiveSuggester, NumberSuggester { + + static @NotNull Values value(float value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(float @NotNull ... values) { + List list = new ArrayList<>(values.length); + for (float value : values) + list.add(value); + return new Values(List.copyOf(list)); + } + + static @NotNull Values range(float from, float to, int amount) { + float part = (to - from) / amount; + List values = new ArrayList<>(amount); + for (float i = 0; i < amount; i++) + values.add(from + part * amount); + return new Values(List.copyOf(values)); + } + + static @NotNull Values range(float from, float to) { + return range(from, to, 4); + } + + static @NotNull Values positive() { + return new Values(List.of( + 0.0F, + 50.5F, + 200.5F, + 3000.5F, + 50000.5F, + 100000.5F + )); + } + + static @NotNull Values negative() { + return new Values(List.of( + -100000.5F, + -50000.5F, + -3000.5F, + -200.5F, + -50.5F, + -1.0F + )); + } + + static @NotNull Values unlimited() { + return new Values(List.of( + -100000.5F, + -50000.5F, + -3000.5F, + -200.5F, + -50.5F, + 0.0F, + 50.5F, + 200.5F, + 3000.5F, + 50000.5F, + 100000.5F + )); + } + + final class Values extends AbstractValuesSuggester implements FloatSuggester { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "FloatSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/IntegerSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/IntegerSuggester.java new file mode 100644 index 00000000..64beda11 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/IntegerSuggester.java @@ -0,0 +1,99 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive.number; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.PrimitiveSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + +public interface IntegerSuggester extends PrimitiveSuggester, NumberSuggester { + + static @NotNull Values value(int value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(int @NotNull ... values) { + List list = new ArrayList<>(values.length); + for (int value : values) + list.add(value); + return new Values(List.copyOf(list)); + } + + static @NotNull Values range(int from, int to, int amount) { + if (to - from < amount) + return new Values(IntStream.rangeClosed(from, to).boxed().toList()); + + int part = (to - from) / amount; + List values = new ArrayList<>(amount); + for (int i = 0; i < amount; i++) + values.add(from + part * i); + values.add(to); + return new Values(List.copyOf(values)); + } + + static @NotNull Values range(int from, int to) { + return range(from, to, 4); + } + + static @NotNull Values positive() { + return Values.POSITIVE; + } + + static @NotNull Values negative() { + return Values.NEGATIVE; + } + + static @NotNull Values unlimited() { + return Values.UNLIMITED; + } + + final class Values extends AbstractValuesSuggester implements IntegerSuggester { + + private static final @NotNull IntegerSuggester.Values POSITIVE = new IntegerSuggester.Values(List.of( + 0, + 50, + 200, + 3000, + 50000, + 100000 + )); + private static final @NotNull IntegerSuggester.Values NEGATIVE = new IntegerSuggester.Values(List.of( + -100000, + -50000, + -3000, + -200, + -50, + -1 + )); + private static final @NotNull IntegerSuggester.Values UNLIMITED = new IntegerSuggester.Values(List.of( + -100000, + -50000, + -3000, + -200, + -50, + 0, + 50, + 200, + 3000, + 50000, + 100000 + )); + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "IntegerSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/LongSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/LongSuggester.java new file mode 100644 index 00000000..5913f0f7 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/LongSuggester.java @@ -0,0 +1,101 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive.number; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.PrimitiveSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public interface LongSuggester extends PrimitiveSuggester, NumberSuggester { + + static @NotNull Values value(long value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(long @NotNull ... values) { + List list = new ArrayList<>(values.length); + for (long value : values) + list.add(value); + return new Values(List.copyOf(list)); + } + + static @NotNull Values range(long from, long to, int amount) { + if (to - from < amount) { + Long[] longs = new Long[(int) (to - from)]; + for (int i = 0; i < longs.length; i++) + longs[i] = to - from - i; + return new Values(List.of(longs)); + } + + long part = (to - from) / amount; + List values = new ArrayList<>(amount); + for (long i = 0; i < amount; i++) + values.add(from + part * amount); + return new Values(List.copyOf(values)); + } + + static @NotNull Values range(long from, long to) { + return range(from, to, 4); + } + + static @NotNull Values positive() { + return Values.POSITIVE; + } + + static @NotNull Values negative() { + return Values.NEGATIVE; + } + + static @NotNull Values unlimited() { + return Values.UNLIMITED; + } + + final class Values extends AbstractValuesSuggester implements LongSuggester { + + private static final @NotNull LongSuggester.Values POSITIVE = new LongSuggester.Values(List.of( + 0L, + 50L, + 200L, + 3000L, + 50000L, + 100000L + )); + private static final @NotNull LongSuggester.Values NEGATIVE = new LongSuggester.Values(List.of( + -100000L, + -50000L, + -3000L, + -200L, + -50L, + -1L + )); + private static final @NotNull LongSuggester.Values UNLIMITED = new LongSuggester.Values(List.of( + -100000L, + -50000L, + -3000L, + -200L, + -50L, + 0L, + 50L, + 200L, + 3000L, + 50000L, + 100000L + )); + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "LongSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/NumberSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/NumberSuggester.java new file mode 100644 index 00000000..de51aecb --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/NumberSuggester.java @@ -0,0 +1,6 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive.number; + +import com.wizardlybump17.wlib.command.suggestion.Suggester; + +public interface NumberSuggester extends Suggester { +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/ShortSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/ShortSuggester.java new file mode 100644 index 00000000..0f6fd57a --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/primitive/number/ShortSuggester.java @@ -0,0 +1,97 @@ +package com.wizardlybump17.wlib.command.suggestion.primitive.number; + +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.PrimitiveSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public interface ShortSuggester extends PrimitiveSuggester, NumberSuggester { + + static @NotNull Values value(short value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(short @NotNull ... values) { + List list = new ArrayList<>(values.length); + for (short value : values) + list.add(value); + return new Values(List.copyOf(list)); + } + + static @NotNull Values range(short from, short to, int amount) { + if (to - from < amount) { + Short[] values = new Short[to - from]; + for (int i = 0; i < values.length; i++) + values[i] = (short) (to - from - i); + return new Values(List.of(values)); + } + + short part = (short) ((to - from) / amount); + List values = new ArrayList<>(amount); + for (short i = 0; i < amount; i++) + values.add((short) (from + part * amount)); + return new Values(List.copyOf(values)); + } + + static @NotNull Values range(short from, short to) { + return range(from, to, 4); + } + + static @NotNull Values positive() { + return Values.POSITIVE; + } + + static @NotNull Values negative() { + return Values.NEGATIVE; + } + + static @NotNull Values unlimited() { + return Values.UNLIMITED; + } + + final class Values extends AbstractValuesSuggester implements ShortSuggester { + + private static final @NotNull ShortSuggester.Values POSITIVE = new ShortSuggester.Values(List.of( + (short) 0, + (short) 50, + (short) 100, + (short) 3000, + (short) 32767 + )); + private static final @NotNull ShortSuggester.Values NEGATIVE = new ShortSuggester.Values(List.of( + (short) -32768, + (short) -3000, + (short) -100, + (short) -50, + (short) -1 + )); + private static final @NotNull ShortSuggester.Values UNLIMITED = new ShortSuggester.Values(List.of( + (short) -32768, + (short) -3000, + (short) -100, + (short) -50, + (short) 0, + (short) 50, + (short) 100, + (short) 3000, + (short) 32767 + )); + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "ShortSuggester$Values{" + + "values=" + values() + + '}'; + } + } +} diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/string/StringSuggester.java b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/string/StringSuggester.java new file mode 100644 index 00000000..7f004cf5 --- /dev/null +++ b/commands/src/main/java/com/wizardlybump17/wlib/command/suggestion/string/StringSuggester.java @@ -0,0 +1,57 @@ +package com.wizardlybump17.wlib.command.suggestion.string; + +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.AbstractValuesSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.PrimitiveSuggester; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface StringSuggester extends PrimitiveSuggester { + + static @NotNull Values value(String value) { + return new Values(List.of(value)); + } + + static @NotNull Values values(@NotNull List values) { + return new Values(values); + } + + static @NotNull Values values(String @NotNull ... values) { + return new Values(List.of(values)); + } + + static @NotNull Any any() { + return Any.INSTANCE; + } + + @Override + default boolean needsEscape() { + return true; + } + + final class Values extends AbstractValuesSuggester implements StringSuggester { + + Values(@NotNull List values) { + super(values); + } + + @Override + public String toString() { + return "StringSuggester$Values{" + + "values=" + values() + + '}'; + } + } + + final class Any implements StringSuggester { + + private static final @NotNull Any INSTANCE = new Any(); + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return List.of(current); + } + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/CommandExecutionTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/CommandExecutionTests.java new file mode 100644 index 00000000..616c2a3d --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/CommandExecutionTests.java @@ -0,0 +1,768 @@ +package com.wizardlybump17.wlib.test.command; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.context.CommandContext; +import com.wizardlybump17.wlib.command.exception.CommandExecutionException; +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.primitive.number.AllowedIntegerInputs; +import com.wizardlybump17.wlib.command.input.string.AllowedStringInputs; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.node.primitive.number.IntegerCommandNode; +import com.wizardlybump17.wlib.command.node.string.StringCommandNode; +import com.wizardlybump17.wlib.command.result.CommandResult; +import com.wizardlybump17.wlib.command.sender.BasicCommandSender; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.util.CollectionUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +public class CommandExecutionTests { + + static final @NotNull Consumer SENDER_MESSAGE_CONSUMER = System.out::println; + static final @NotNull CommandSender CHAD_SENDER = new BasicCommandSender<>(new Object(), "Chad", UUID.nameUUIDFromBytes("Chad".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> true); + static final @NotNull CommandSender BETA_SENDER = new BasicCommandSender<>(new Object(), "Beta", UUID.nameUUIDFromBytes("Beta".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> false); + + @Test + void testSuccessHello() { + LiteralCommandNode helloNode = new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + null + ); + Command command = new Command(helloNode); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.successful("hello"), + commandManager.execute(CHAD_SENDER, List.of("hello")) + ); + } + + @Test + void testSuccessHelloWorld() { + LiteralCommandNode worldNode = new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ); + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(worldNode), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.successful("hello world"), + commandManager.execute(CHAD_SENDER, List.of("hello", "world")) + ); + } + + @Test + void testSuccessHelloWorldHi() { + LiteralCommandNode hiNode = new LiteralCommandNode( + "hi", + List.of(), + context -> CommandResult.successful("hello world hi"), + null + ); + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(hiNode), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.successful("hello world hi"), + commandManager.execute(CHAD_SENDER, List.of("hello", "world", "hi")) + ); + } + + @Test + @DisplayName("Multiple children 0 (hello, hi): success") + void test0() { + LiteralCommandNode hiNode = new LiteralCommandNode( + "hi", + List.of(), + context -> CommandResult.successful("hello hi"), + null + ); + Command command = new Command(new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ), + hiNode + ), + null, + null + )); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.successful("hello hi"), + commandManager.execute(CHAD_SENDER, List.of("hello", "hi")) + ); + } + + @Test + @DisplayName("Multiple children 1 (hello, hi, world): success") + void test1() { + LiteralCommandNode hiWorldNode = new LiteralCommandNode( + "world0", + List.of(), + context -> CommandResult.successful("hello hi world"), + null + ); + Command command = new Command(new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ), + new LiteralCommandNode( + "hi", + List.of(hiWorldNode), + null, + null + ) + ), + null, + null + )); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.successful("hello hi world"), + commandManager.execute(CHAD_SENDER, List.of("hello", "hi", "world0")) + ); + } + + @Test + void testExtraArguments0() { + LiteralCommandNode helloNode = new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + null + ); + Command command = new Command(helloNode); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.notFound(CommandResult.ErrorDetails.nodeNotFound(1, "world")), + commandManager.execute(CHAD_SENDER, List.of("hello", "world", "hi", "there")) + ); + } + + @Test + void testExtraArguments1() { + LiteralCommandNode worldNode = new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ); + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(worldNode), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.notFound(CommandResult.ErrorDetails.nodeNotFound(2, "hi")), + commandManager.execute(CHAD_SENDER, List.of("hello", "world", "hi", "there")) + ); + } + + @Test + void testExtraArguments2() { + LiteralCommandNode hiWorldNode = new LiteralCommandNode( + "world0", + List.of(), + context -> CommandResult.successful("hello hi world"), + null + ); + Command command = new Command(new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ), + new LiteralCommandNode( + "hi", + List.of(hiWorldNode), + null, + null + ) + ), + null, + null + )); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.notFound(CommandResult.ErrorDetails.nodeNotFound(3, "extra")), + commandManager.execute(CHAD_SENDER, List.of("hello", "hi", "world0", "extra")) + ); + } + + @Test + void testException() { + RuntimeException helloException = new RuntimeException("hello"); + LiteralCommandNode helloNode = new LiteralCommandNode( + "hello", + List.of(), + context -> { + throw helloException; + }, + null + ); + + Command command = new Command(helloNode); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + CommandExecutionException actual = Assertions.assertThrowsExactly( + CommandExecutionException.class, + () -> commandManager.execute(CHAD_SENDER, List.of("hello")) + ); + Assertions.assertEquals( + CommandExecutionException.MESSAGE.formatted("[hello]", "0", "hello"), + actual.getMessage() + ); + Assertions.assertEquals( + helloException.getMessage(), + Assertions.assertInstanceOf( + helloException.getClass(), + actual.getCause() + ).getMessage() + ); + } + + @Test + void testInsufficientArguments() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.badRequest(CommandResult.ErrorDetails.emptyInput()), + commandManager.execute(CHAD_SENDER, List.of()) + ); + } + + @Test + void testNoPermission() { + LiteralCommandNode helloNode = new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ); + Command command = new Command(helloNode); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.forbidden(CommandResult.ErrorDetails.noPermission(BETA_SENDER.getName(), "permission")), + commandManager.execute(BETA_SENDER, List.of("hello")) + ); + } + + @Test + void testOutOfRange() { + LiteralCommandNode helloNode = new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + null + ); + Command command = new Command(helloNode); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.notFound(CommandResult.ErrorDetails.nodeNotFound(0, "hello0")), + commandManager.execute(CHAD_SENDER, List.of("hello0")) + ); + } + + @Test + void testParseInputException() { + IntegerCommandNode worldNode = new IntegerCommandNode( + "world", + List.of(), + AllowedIntegerInputs.value(10), + null, + context -> CommandResult.successful(10), + null + ); + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(worldNode), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.badRequest(CommandResult.ErrorDetails.parseError("hello", "world", new InputParsingException("Could not parse as int: world", new NumberFormatException("For input string: \"world\"")))), + commandManager.execute(CHAD_SENDER, List.of("hello", "world")) + ); + } + + @Test + void testCommandNodeExecutorNotFound() { + LiteralCommandNode helloNode = new LiteralCommandNode( + "hello", + List.of(), + null, + null + ); + Command command = new Command(helloNode); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.notImplemented(CommandResult.ErrorDetails.noCommandExecutor("hello", "hello")), + commandManager.execute(CHAD_SENDER, List.of("hello")) + ); + } + + /** + *

+ * The CommandNode#merge(CommandNode) method wasnt merging the executor and permission. + * This test will ensure that we have the correct executor and permission after merging. + *

+ */ + @Test + void testMerge() { + CommandNodeExecutor test0Executor = context -> CommandResult.successful("It works 0!"); + CommandNodeExecutor test1Executor = context -> CommandResult.successful("It works 1!"); + + CommandNode expected = new LiteralCommandNode( + "test0", + List.of( + new LiteralCommandNode( + "test1", + List.of(), + test1Executor, + "permission1" + ) + ), + test0Executor, + "permission0" + ); + CommandNode actual = new LiteralCommandNode( + "test0", + List.of(), + null, + null + ).merge(new LiteralCommandNode( + "test0", + List.of( + new LiteralCommandNode( + "test1", + List.of(), + test1Executor, + "permission1" + ) + ), + test0Executor, + "permission0" + )); + + Assertions.assertEquals(expected, actual); + + Assertions.assertEquals( + new TestMerge0( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + null, + null + ) + ), + null, + null + ) + ), + new Command( + new LiteralCommandNode( + "hello", + List.of(), + null, + null + ) + ).merge( + new TestMerge0( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + null, + null + ) + ), + null, + null + ) + ) + ) + ); + + Assertions.assertEquals( + new TestMerge1( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + null, + null + ) + ), + null, + null + ) + ), + new Command( + new LiteralCommandNode( + "hi", + List.of(), + null, + null + ) + ).merge( + new TestMerge1( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + null, + null + ) + ), + null, + null + ) + ) + ) + ); + + Assertions.assertEquals( + new TestMerge0( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ), + null, + null + ) + ), + new TestMerge0( + new LiteralCommandNode( + "test", + List.of(), + null, + null + ) + ).merge( + new TestMerge1( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ), + null, + null + ) + ) + ) + ); + } + + static class TestMerge0 extends Command { + + public TestMerge0(@NotNull LiteralCommandNode root) { + super(root); + } + + @Override + public @NotNull Command merge(@NotNull Command other) { + return new TestMerge0(getRoot().merge(other.getRoot())); + } + } + + static class TestMerge1 extends Command { + + public TestMerge1(@NotNull LiteralCommandNode root) { + super(root); + } + + @Override + public @NotNull Command merge(@NotNull Command other) { + return new TestMerge1(getRoot().merge(other.getRoot())); + } + } + + @Test + void testCommandArguments() { + { + LiteralCommandNode world = new LiteralCommandNode( + "world", + List.of(), + context -> { + Assertions.assertEquals(Map.of(), context.arguments().getArguments()); + return CommandResult.successful("Hello World"); + }, + null + ); + + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(world), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + CommandResult result = commandManager.execute(CHAD_SENDER, List.of("hello", "world")); + + Assertions.assertEquals(CommandResult.successful("Hello World"), result); + } + + { + AtomicReference worldReference = new AtomicReference<>(); + IntegerCommandNode world = new IntegerCommandNode( + "world", + List.of(), + AllowedIntegerInputs.unlimited(), + null, + context -> { + Assertions.assertEquals(Map.of("world", new CommandContext.CommandNodeArgument<>(worldReference.get(), "10", 10)), context.arguments().getArguments()); + return CommandResult.successful(context.arguments().getArgumentData("world").orElseThrow()); + }, + null + ); + worldReference.set(world); + + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(world), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + CommandResult result = commandManager.execute(CHAD_SENDER, List.of("hello", "10")); + + Assertions.assertEquals(CommandResult.successful(10), result); + } + + { + AtomicReference worldReference = new AtomicReference<>(); + LiteralCommandNode test = new LiteralCommandNode( + "test", + List.of(), + context -> { + Assertions.assertEquals(Map.of("world", new CommandContext.CommandNodeArgument<>(worldReference.get(), "10", 10)), context.arguments().getArguments()); + return CommandResult.successful("Hello " + context.arguments().getArgumentData("world").orElseThrow() + " world"); + }, + null + ); + IntegerCommandNode world = new IntegerCommandNode( + "world", + List.of(test), + AllowedIntegerInputs.unlimited(), + null, + null, + null + ); + worldReference.set(world); + + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(world), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + CommandResult result = commandManager.execute(CHAD_SENDER, List.of("hello", "10", "test")); + + Assertions.assertEquals(CommandResult.successful("Hello 10 world"), result); + } + } + + @Test + void testNullArgumentsSuccess() { + Command command = new Command( + new LiteralCommandNode( + "test", + List.of( + new StringCommandNode( + "nullable0", + List.of(), + AllowedStringInputs.anyNullable(), + null, + context -> CommandResult.successful(context.arguments().getArgumentData("nullable0").orElse(null)), + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.successful("Hello World"), + commandManager.execute(CHAD_SENDER, List.of("test", "Hello World")) + ); + Assertions.assertEquals( + CommandResult.successful(), + commandManager.execute(CHAD_SENDER, CollectionUtil.listOf("test", null)) + ); + } + + @Test + void testNullArgumentsError() { + Command command = new Command( + new LiteralCommandNode( + "test", + List.of( + new StringCommandNode( + "nullable0", + List.of(), + AllowedStringInputs.anyNullable(), + null, + context -> CommandResult.successful(context.arguments().getArgumentData("nullable0").orElse(null)), + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.successful("Hello World"), + commandManager.execute(CHAD_SENDER, List.of("test", "Hello World")) + ); + Assertions.assertEquals( + CommandResult.successful(), + commandManager.execute(CHAD_SENDER, CollectionUtil.listOf("test", null)) + ); + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/SuggestionTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/SuggestionTests.java new file mode 100644 index 00000000..e2917b49 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/SuggestionTests.java @@ -0,0 +1,605 @@ +package com.wizardlybump17.wlib.test.command; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.input.object.AllowedUUIDInputs; +import com.wizardlybump17.wlib.command.input.primitive.number.*; +import com.wizardlybump17.wlib.command.input.string.AllowedStringInputs; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.node.object.UUIDCommandNode; +import com.wizardlybump17.wlib.command.node.primitive.number.*; +import com.wizardlybump17.wlib.command.node.string.StringCommandNode; +import com.wizardlybump17.wlib.command.result.CommandResult; +import com.wizardlybump17.wlib.command.sender.BasicCommandSender; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.object.UUIDSuggester; +import com.wizardlybump17.wlib.command.suggestion.primitive.number.*; +import com.wizardlybump17.wlib.command.suggestion.string.StringSuggester; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +class SuggestionTests { + + static final @NotNull Consumer SENDER_MESSAGE_CONSUMER = System.out::println; + static final @NotNull CommandSender CHAD_SENDER = new BasicCommandSender<>(new Object(), "Chad", UUID.nameUUIDFromBytes("Chad".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> true); + static final @NotNull CommandSender BETA_SENDER = new BasicCommandSender<>(new Object(), "Beta", UUID.nameUUIDFromBytes("Beta".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> false); + + @Test + @DisplayName("Test Success: root 0") + void testSuccess0() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + List expected = List.of("hello"); + List actual = Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of())); + + Assertions.assertEquals(expected, actual); + } + + @Test + @DisplayName("Test Success: root 1") + void testSuccess1() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + List expected = List.of("hello"); + List actual = Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("he"))); + + Assertions.assertEquals(expected, actual); + } + + @Test + @DisplayName("Test Success: children 0") + void testSuccess2() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ), + new LiteralCommandNode( + "hi", + List.of(), + context -> CommandResult.successful("hello hi"), + null + ), + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hello there"), + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + List expected = List.of("world", "hi", "there"); + List actual = Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))); + + Assertions.assertEquals(expected, actual); + } + + @Test + @DisplayName("Test Success: children 1") + void testSuccess3() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ), + new LiteralCommandNode( + "hi", + List.of(), + context -> CommandResult.successful("hello hi"), + null + ), + new IntegerCommandNode( + "there", + List.of(), + AllowedIntegerInputs.range(1, 10), + IntegerSuggester.range(1, 10), + context -> CommandResult.successful("hello there"), + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + List expected = List.of("world", "hi", "1", "3", "5", "7", "10"); + List actual = Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))); + + Assertions.assertEquals(expected, actual); + } + + @Test + @DisplayName("Test Success: children 2") + void testSuccess4() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + context -> CommandResult.successful("hello world"), + null + ), + new LiteralCommandNode( + "hi", + List.of(), + context -> CommandResult.successful("hello hi"), + null + ), + new IntegerCommandNode( + "there", + List.of(), + AllowedIntegerInputs.range(1, 10), + IntegerSuggester.range(1, 10), + context -> CommandResult.successful("hello there"), + null + ), + new LiteralCommandNode( + "happy", + List.of(), + context -> CommandResult.successful("hello happy"), + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + List expected = List.of("world", "hi", "1", "3", "5", "7", "10", "happy"); + List actual = Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "h"))); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testDefaultLiteral() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("hello"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of())) + ); + Assertions.assertEquals( + List.of("hello"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of(""))) + ); + Assertions.assertEquals( + List.of("hello"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hel"))) + ); + } + + @Test + void testDefaultByte() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new ByteCommandNode( + "world", + List.of(), + AllowedByteInputs.unlimited(), + ByteSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("-128", "-100", "-50", "0", "50", "100", "127"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("-128", "-100", "-50", "0", "50", "100", "127"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "0"))) + ); + Assertions.assertEquals( + List.of("-128", "-100", "-50", "0", "50", "100", "127"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "10"))) + ); + } + + @Test + void testDefaultShort() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new ShortCommandNode( + "world", + List.of(), + AllowedShortInputs.unlimited(), + ShortSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("-32768", "-3000", "-100", "-50", "0", "50", "100", "3000", "32767"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("-32768", "-3000", "-100", "-50", "0", "50", "100", "3000", "32767"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "0"))) + ); + Assertions.assertEquals( + List.of("-32768", "-3000", "-100", "-50", "0", "50", "100", "3000", "32767"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "10"))) + ); + } + + @Test + void testDefaultInt() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new IntegerCommandNode( + "world", + List.of(), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("-100000", "-50000", "-3000", "-200", "-50", "0", "50", "200", "3000", "50000", "100000"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("-100000", "-50000", "-3000", "-200", "-50", "0", "50", "200", "3000", "50000", "100000"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "0"))) + ); + Assertions.assertEquals( + List.of("-100000", "-50000", "-3000", "-200", "-50", "0", "50", "200", "3000", "50000", "100000"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "10"))) + ); + } + + @Test + void testDefaultLong() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LongCommandNode( + "world", + List.of(), + AllowedLongInputs.unlimited(), + LongSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("-100000", "-50000", "-3000", "-200", "-50", "0", "50", "200", "3000", "50000", "100000"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("-100000", "-50000", "-3000", "-200", "-50", "0", "50", "200", "3000", "50000", "100000"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "0"))) + ); + Assertions.assertEquals( + List.of("-100000", "-50000", "-3000", "-200", "-50", "0", "50", "200", "3000", "50000", "100000"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "10"))) + ); + } + + @Test + void testDefaultFloat() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new FloatCommandNode( + "world", + List.of(), + AllowedFloatInputs.unlimited(), + FloatSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("-100000.5", "-50000.5", "-3000.5", "-200.5", "-50.5", "0.0", "50.5", "200.5", "3000.5", "50000.5", "100000.5"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("-100000.5", "-50000.5", "-3000.5", "-200.5", "-50.5", "0.0", "50.5", "200.5", "3000.5", "50000.5", "100000.5"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "0"))) + ); + Assertions.assertEquals( + List.of("-100000.5", "-50000.5", "-3000.5", "-200.5", "-50.5", "0.0", "50.5", "200.5", "3000.5", "50000.5", "100000.5"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "10"))) + ); + } + + @Test + void testDefaultDouble() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new DoubleCommandNode( + "world", + List.of(), + AllowedDoubleInputs.unlimited(), + DoubleSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("-100000.5", "-50000.5", "-3000.5", "-200.5", "-50.5", "0.0", "50.5", "200.5", "3000.5", "50000.5", "100000.5"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("-100000.5", "-50000.5", "-3000.5", "-200.5", "-50.5", "0.0", "50.5", "200.5", "3000.5", "50000.5", "100000.5"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "0"))) + ); + Assertions.assertEquals( + List.of("-100000.5", "-50000.5", "-3000.5", "-200.5", "-50.5", "0.0", "50.5", "200.5", "3000.5", "50000.5", "100000.5"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "10"))) + ); + } + + @Test + void testDefaultString() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new StringCommandNode( + "world", + List.of(), + AllowedStringInputs.anyNullable(), + StringSuggester.any(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("\"\""), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("\"wo\""), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "wo"))) + ); + Assertions.assertEquals( + List.of("\"spaced \""), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "spaced "))) + ); + Assertions.assertEquals( + List.of("\"spaced string\""), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "spaced string"))) + ); + } + + @Test + void testDefaultUUID() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new UUIDCommandNode( + "world", + List.of(), + AllowedUUIDInputs.anyNullable(), + UUIDSuggester.values( + UUID.nameUUIDFromBytes("WizardlyBump17".getBytes()), + UUID.nameUUIDFromBytes("WizardlyBump18".getBytes()), + UUID.nameUUIDFromBytes("WizardlyBump18".getBytes()) + ), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals( + List.of("9b07bd8a-a4c0-3681-997f-6b6df78c0abe", "2931e955-084c-3d9e-aea4-8e5c2c1089c1", "2931e955-084c-3d9e-aea4-8e5c2c1089c1"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", ""))) + ); + Assertions.assertEquals( + List.of("9b07bd8a-a4c0-3681-997f-6b6df78c0abe", "2931e955-084c-3d9e-aea4-8e5c2c1089c1", "2931e955-084c-3d9e-aea4-8e5c2c1089c1"), + Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "9b07bd8a-a4c0"))) + ); + } + + @Test + void testEmptyStrings0() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals(List.of("world"), Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "")))); + Assertions.assertEquals(List.of(), Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "", "")))); + } + + @Test + void testEmptyStrings1() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ), + null, + null + ) + ), + null, + null + ) + ); + + CommandManager commandManager = new CommandManager(); + commandManager.registerCommand("test", command); + + Assertions.assertEquals(List.of("world"), Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "")))); + Assertions.assertEquals(List.of(), Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "", "")))); + Assertions.assertEquals(List.of("test0"), Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "world", "")))); + Assertions.assertEquals(List.of(), Assertions.assertDoesNotThrow(() -> commandManager.getSuggestions(CHAD_SENDER, List.of("hello", "world", "test0", "")))); + } +} \ No newline at end of file diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorRightExecutorTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorRightExecutorTests.java new file mode 100644 index 00000000..16b380f1 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorRightExecutorTests.java @@ -0,0 +1,266 @@ +package com.wizardlybump17.wlib.test.command.extractor.method; + +import com.wizardlybump17.wlib.command.context.CommandContext; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.extractor.method.MethodCommandExtractor; +import com.wizardlybump17.wlib.command.extractor.method.executor.AbstractMethodCommandNodeExecutor; +import com.wizardlybump17.wlib.command.result.CommandResult; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +class MethodCommandExtractorRightExecutorTests { + + /* + No parameters + */ + + @Test + void testNoArgumentsCommandResult() { + TestNoArgumentsCommandResultExecutor object = new TestNoArgumentsCommandResultExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello")); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.NoArgumentsCommandResultExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello"); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestNoArgumentsCommandResultExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world") + public CommandResult hello() { + return null; + } + } + + @Test + void testNoArguments() { + TestNoArgumentsExecutor object = new TestNoArgumentsExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello")); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.NoArgumentsExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello"); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestNoArgumentsExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world") + public void hello() { + } + } + + /* + CommandSender/CommandContext only + */ + + @Test + void testCommandSenderCommandResult() { + TestCommandSenderCommandResultExecutor object = new TestCommandSenderCommandResultExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandSender.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandSenderCommandResultExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandSenderCommandResultExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world") + public CommandResult hello(@NotNull CommandSender sender) { + return null; + } + } + + @Test + void testCommandSender() { + TestCommandSenderExecutor object = new TestCommandSenderExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandSender.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandSenderExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandSenderExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world") + public void hello(@NotNull CommandSender sender) { + } + } + + @Test + void testCommandContextCommandResult() { + TestCommandContextCommandResultExecutor object = new TestCommandContextCommandResultExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandContext.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandContextCommandResultExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandContextCommandResultExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world") + public CommandResult hello(@NotNull CommandContext context) { + return null; + } + } + + @Test + void testCommandContext() { + TestCommandContextExecutor object = new TestCommandContextExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandContext.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandContextExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandContextExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world") + public void hello(@NotNull CommandContext context) { + } + } + + /* + CommandSender/CommandContext and parameters + */ + + @Test + void testCommandSenderAndArgumentsCommandResult() { + TestCommandSenderAndArgumentsCommandResultExecutor object = new TestCommandSenderAndArgumentsCommandResultExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandSender.class, int.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandSenderAndArgumentsCommandResultExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class, int.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandSenderAndArgumentsCommandResultExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world ") + public CommandResult hello(@NotNull CommandSender sender, int arg0) { + return null; + } + } + + @Test + void testCommandSenderAndArguments() { + TestCommandSenderAndArgumentsExecutor object = new TestCommandSenderAndArgumentsExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandSender.class, int.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandSenderAndArgumentsExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class, int.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandSenderAndArgumentsExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world ") + public void hello(@NotNull CommandSender sender, int arg0) { + } + } + + @Test + void testCommandContextAndArgumentsCommandResult() { + TestCommandContextAndArgumentsCommandResultExecutor object = new TestCommandContextAndArgumentsCommandResultExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandContext.class, int.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandContextAndArgumentsCommandResultExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class, int.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandContextAndArgumentsCommandResultExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world ") + public CommandResult hello(@NotNull CommandContext context, int arg0) { + return null; + } + } + + @Test + void testCommandContextAndArguments() { + TestCommandContextAndArgumentsExecutor object = new TestCommandContextAndArgumentsExecutor(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", CommandContext.class, int.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.CommandContextAndArgumentsExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class, int.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestCommandContextAndArgumentsExecutor { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world ") + public void hello(@NotNull CommandContext context, int arg0) { + } + } + + /* + Parameters only + */ + + @Test + void testArgumentsCommandResult() { + TestArgumentsCommandResult object = new TestArgumentsCommandResult(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", int.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.ArgumentsCommandResultExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", int.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestArgumentsCommandResult { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world ") + public CommandResult hello(int arg0) { + return null; + } + } + + @Test + void testArguments() { + TestArguments object = new TestArguments(); + + Method method = Assertions.assertDoesNotThrow(() -> object.getClass().getMethod("hello", int.class)); + + CommandNodeExecutor expected = new AbstractMethodCommandNodeExecutor.ArgumentsExecutor<>(object, method); + CommandNodeExecutor actual = MethodCommandExtractor.createExecutor(object, "hello", int.class); + + Assertions.assertEquals(expected, actual); + } + + public static final class TestArguments { + + @com.wizardlybump17.wlib.command.annotation.Command("hello world ") + public void hello(int arg0) { + } + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorStructureTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorStructureTests.java new file mode 100644 index 00000000..66381a97 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorStructureTests.java @@ -0,0 +1,191 @@ +package com.wizardlybump17.wlib.test.command.extractor.method; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.extractor.CommandExtractor; +import com.wizardlybump17.wlib.command.extractor.method.MethodCommandExtractor; +import com.wizardlybump17.wlib.command.input.primitive.number.AllowedIntegerInputs; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.node.primitive.number.IntegerCommandNode; +import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry; +import com.wizardlybump17.wlib.command.suggestion.primitive.number.IntegerSuggester; +import com.wizardlybump17.wlib.test.util.AssertionUtil; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +class MethodCommandExtractorStructureTests { + + static MethodCommandExtractor methodCommandExtractor; + + @BeforeAll + static void setup() { + MethodCommandNodeFactoryRegistry registry = new MethodCommandNodeFactoryRegistry(); + registry.registerDefaults(); + methodCommandExtractor = CommandExtractor.method(registry); + } + + @AfterAll + static void clear() { + if (methodCommandExtractor == null) + return; + + MethodCommandNodeFactoryRegistry registry = methodCommandExtractor.getFactoryRegistry(); + registry.clear(); + methodCommandExtractor = null; + } + + @Test + void test0() { + Command test0 = new Command( + new LiteralCommandNode( + "test0", + List.of( + new LiteralCommandNode( + "123", + List.of(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test1", + List.of( + new LiteralCommandNode( + "123", + List.of( + new LiteralCommandNode( + "456", + List.of(), + null, + null + ) + ), + null, + null + ) + ), + null, + null + ) + ); + + Test0 object = new Test0(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static class Test0 { + + @com.wizardlybump17.wlib.command.annotation.Command("test0 123") + public void test0() { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test1 123 456") + public void test1() { + } + } + + @Test + void test1() { + Command test0 = new Command( + new LiteralCommandNode( + "test0", + List.of( + new IntegerCommandNode( + "int", + List.of( + new LiteralCommandNode( + "123", + List.of(), + null, + null + ) + ), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test1", + List.of( + new IntegerCommandNode( + "int0", + List.of( + new IntegerCommandNode( + "int1", + List.of( + new LiteralCommandNode( + "123", + List.of( + new LiteralCommandNode( + "abc", + List.of(), + null, + null + ) + ), + null, + null + ) + ), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + Test1 object = new Test1(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static class Test1 { + + @com.wizardlybump17.wlib.command.annotation.Command("test0 123") + public void test0(int arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test1 123 abc") + public void test1(int arg0, int arg1) { + } + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorTests.java new file mode 100644 index 00000000..5ca50b18 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandExtractorTests.java @@ -0,0 +1,1446 @@ +//package com.wizardlybump17.wlib.test.command.extractor.method; +// +//import com.wizardlybump17.wlib.command.Command; +//import com.wizardlybump17.wlib.command.context.CommandContext; +//import com.wizardlybump17.wlib.command.extractor.CommandExtractor; +//import com.wizardlybump17.wlib.command.extractor.method.MethodCommandExtractor; +//import com.wizardlybump17.wlib.command.input.primitive.number.AllowedIntegerInputs; +//import com.wizardlybump17.wlib.command.input.string.AllowedStringInputs; +//import com.wizardlybump17.wlib.command.manager.CommandManager; +//import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +//import com.wizardlybump17.wlib.command.node.primitive.number.IntegerCommandNode; +//import com.wizardlybump17.wlib.command.node.string.StringCommandNode; +//import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry; +//import com.wizardlybump17.wlib.command.result.CommandResult; +//import com.wizardlybump17.wlib.command.sender.BasicCommandSender; +//import com.wizardlybump17.wlib.command.sender.CommandSender; +//import com.wizardlybump17.wlib.command.suggestion.primitive.number.IntegerSuggester; +//import com.wizardlybump17.wlib.command.suggestion.string.StringSuggester; +//import org.jetbrains.annotations.NotNull; +//import org.junit.jupiter.api.AfterAll; +//import org.junit.jupiter.api.Assertions; +//import org.junit.jupiter.api.BeforeAll; +//import org.junit.jupiter.api.Test; +// +//import java.util.ArrayList; +//import java.util.List; +//import java.util.UUID; +//import java.util.function.Consumer; +// +//class MethodCommandExtractorTests { +// +// static MethodCommandExtractor methodCommandExtractor; +// +// @BeforeAll +// static void setup() { +// MethodCommandNodeFactoryRegistry registry = new MethodCommandNodeFactoryRegistry(); +// registry.registerDefaults(); +// methodCommandExtractor = CommandExtractor.method(registry); +// } +// +// @AfterAll +// static void clear() { +// if (methodCommandExtractor == null) +// return; +// +// MethodCommandNodeFactoryRegistry registry = methodCommandExtractor.getFactoryRegistry(); +// registry.clear(); +// methodCommandExtractor = null; +// } +// +// /* +// No parameters +// */ +// @Test +// void testNoParameters() { +// TestNoParameters object = new TestNoParameters(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello")), +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld")), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi")), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestNoParameters { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello") +// public void hello() { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world") +// public void helloWorld() { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi") +// public void helloThereHi() { +// } +// } +// +// /* +// No parameters returning CommandResult +// */ +// @Test +// void testNoParametersCommandResult() { +// TestNoParametersCommandResult object = new TestNoParametersCommandResult(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello")), +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld")), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi")), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestNoParametersCommandResult { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello") +// public CommandResult hello() { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world") +// public CommandResult helloWorld() { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi") +// public CommandResult helloThereHi() { +// return null; +// } +// } +// +// /* +// CommandSender/CommandContext only +// */ +// +// @Test +// void testCommandSender() { +// TestCommandSender object = new TestCommandSender(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class)), +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandSender.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandSender.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandSender { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello") +// public void hello(@NotNull CommandSender sender) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world") +// public void helloWorld(@NotNull CommandSender sender) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi") +// public void helloThereHi(@NotNull CommandSender sender) { +// } +// } +// +// @Test +// void testCommandContext() { +// TestCommandContext object = new TestCommandContext(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class)), +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandContext.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandContext.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandContext { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello") +// public void hello(@NotNull CommandContext context) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world") +// public void helloWorld(@NotNull CommandContext context) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi") +// public void helloThereHi(@NotNull CommandContext context) { +// } +// } +// +// /* +// CommandSender/CommandSender returning CommandResult +// */ +// +// @Test +// void testCommandContextCommandResult() { +// TestCommandContextCommandResult object = new TestCommandContextCommandResult(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class)), +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandContext.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandContext.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandContextCommandResult { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello") +// public @NotNull CommandResult hello(@NotNull CommandContext context) { +// return CommandResult.successful(context, null); +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world") +// public @NotNull CommandResult helloWorld(@NotNull CommandContext context) { +// return CommandResult.successful(context, null); +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi") +// public @NotNull CommandResult helloThereHi(@NotNull CommandContext context) { +// return CommandResult.successful(context, null); +// } +// } +// +// @Test +// void testCommandSenderCommandResult() { +// TestCommandSenderCommandResult object = new TestCommandSenderCommandResult(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class)), +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandSender.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandSender.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandSenderCommandResult { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello") +// public CommandResult hello(@NotNull CommandSender sender) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world") +// public CommandResult helloWorld(@NotNull CommandSender sender) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi") +// public CommandResult helloThereHi(@NotNull CommandSender sender) { +// return null; +// } +// } +// +// /* +// CommandSender/CommandContext + parameters +// */ +// +// @Test +// void testCommandSenderParameters() { +// TestCommandSenderParameters object = new TestCommandSenderParameters(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandSender.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandSender.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandSenderParameters { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello ") +// public void hello(@NotNull CommandSender sender, int arg0) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world ") +// public void helloWorld(@NotNull CommandSender sender, int arg0) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi ") +// public void helloThereHi(@NotNull CommandSender sender, int arg0) { +// } +// } +// +// @Test +// void testCommandContextParameters() { +// TestCommandContextParameters object = new TestCommandContextParameters(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandContext.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandContext.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandContextParameters { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello ") +// public void hello(@NotNull CommandContext context, int arg0) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world ") +// public void helloWorld(@NotNull CommandContext context, int arg0) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi ") +// public void helloThereHi(@NotNull CommandContext context, int arg0) { +// } +// } +// +// /* +// CommandSender/CommandContext + parameters returning CommandResult +// */ +// +// @Test +// void testCommandSenderParametersCommandResult() { +// TestCommandSenderParametersCommandResult object = new TestCommandSenderParametersCommandResult(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandSender.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandSender.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandSender.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandSenderParametersCommandResult { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello ") +// public CommandResult hello(@NotNull CommandSender sender, int arg0) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world ") +// public CommandResult helloWorld(@NotNull CommandSender sender, int arg0) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi ") +// public CommandResult helloThereHi(@NotNull CommandSender sender, int arg0) { +// return null; +// } +// } +// +// @Test +// void testCommandContextParametersCommandResult() { +// TestCommandContextParametersCommandResult object = new TestCommandContextParametersCommandResult(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", CommandContext.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", CommandContext.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", CommandContext.class, int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestCommandContextParametersCommandResult { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello ") +// public CommandResult hello(@NotNull CommandContext context, int arg0) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world ") +// public CommandResult helloWorld(@NotNull CommandContext context, int arg0) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi ") +// public CommandResult helloThereHi(@NotNull CommandContext context, int arg0) { +// return null; +// } +// } +// +// /* +// Parameters not starting with CommandSender/CommandContext +// */ +// +// @Test +// void testParametersPure() { +// TestParametersPure object = new TestParametersPure(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestParametersPure { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello ") +// public void hello(int arg0) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world ") +// public void helloWorld(int arg0) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi ") +// public void helloThereHi(int arg0) { +// } +// } +// +// /* +// Parameters not starting with CommandSender/CommandContext returning CommandResult +// */ +// +// @Test +// void testParametersPureCommandResult() { +// TestParametersPureCommandResult object = new TestParametersPureCommandResult(); +// +// List expected = new ArrayList<>(List.of( +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello", int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld", int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "there", +// List.of( +// new LiteralCommandNode( +// "hi", +// List.of( +// new IntegerCommandNode( +// "int", +// List.of(), +// AllowedIntegerInputs.unlimited(), +// IntegerSuggester.unlimited(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloThereHi", int.class)), +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ), +// null, +// null +// ) +// ) +// )); +// List actual = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// expected.sort(null); +// actual.sort(null); +// +// Assertions.assertEquals(expected, actual); +// } +// +// public static class TestParametersPureCommandResult { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello ") +// public CommandResult hello(int arg0) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world ") +// public CommandResult helloWorld(int arg0) { +// return null; +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello there hi ") +// public CommandResult helloThereHi(int arg0) { +// return null; +// } +// } +// +// /* +// Lets execute some commands +// */ +// +// static final @NotNull Consumer SENDER_MESSAGE_CONSUMER = System.out::println; +// static final @NotNull CommandSender CHAD_SENDER = new BasicCommandSender<>(new Object(), "Chad", UUID.nameUUIDFromBytes("Chad".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> true); +// static final @NotNull CommandSender BETA_SENDER = new BasicCommandSender<>(new Object(), "Beta", UUID.nameUUIDFromBytes("Beta".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> false); +// +// @Test +// void testExecute() { +// TestExecute object = new TestExecute(); +// +// Command hello = new Command( +// new LiteralCommandNode( +// "hello", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "helloWorld")), +// null +// ) +// ), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hello")), +// null +// ) +// ); +// Command hi = new Command( +// new LiteralCommandNode( +// "hi", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hiWorld", CommandSender.class)), +// null +// ) +// ), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "hi", CommandSender.class)), +// null +// ) +// ); +// Command greetings = new Command( +// new LiteralCommandNode( +// "greetings", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "greetingsWorld", CommandContext.class)), +// null +// ) +// ), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "greetings", CommandContext.class)), +// null +// ) +// ); +// Command welcome = new Command( +// new LiteralCommandNode( +// "welcome", +// List.of( +// new StringCommandNode( +// "name", +// List.of( +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "welcomeWorld", CommandSender.class, String.class)), +// null +// ) +// ), +// AllowedStringInputs.anyNullable(), +// StringSuggester.any(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "welcome", CommandSender.class, String.class)), +// null +// ) +// ), +// null, +// null +// ) +// ); +// Command wassup = new Command( +// new LiteralCommandNode( +// "wassup", +// List.of( +// new StringCommandNode( +// "name", +// List.of( +// new LiteralCommandNode( +// "nice", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "wassupNice", CommandContext.class, String.class)), +// null +// ) +// ), +// AllowedStringInputs.anyNullable(), +// StringSuggester.any(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "wassup", CommandContext.class, String.class)), +// null +// ) +// ), +// null, +// null +// ) +// ); +// Command aye = new Command( +// new LiteralCommandNode( +// "aye", +// List.of( +// new StringCommandNode( +// "name", +// List.of( +// new LiteralCommandNode( +// "nice", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "ayeNice", String.class)), +// null +// ) +// ), +// AllowedStringInputs.anyNullable(), +// StringSuggester.any(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(object, "aye", String.class)), +// null +// ) +// ), +// null, +// null +// ) +// ); +// +// List extractedCommands = Assertions.assertDoesNotThrow(() -> methodCommandExtractor.extract(object)); +// +// CommandManager manager = new CommandManager(); +// +// List expectedCommands = new ArrayList<>(List.of(hello, hi, greetings, welcome, wassup, aye)); +// List actualCommands = manager.registerCommands("test", extractedCommands); +// +// expectedCommands.sort(null); +// actualCommands.sort(null); +// +// //We already know that the register method works +// actualCommands.remove(0); +// actualCommands.remove(1); +// actualCommands.remove(2); +// actualCommands.remove(3); +// actualCommands.remove(4); +// actualCommands.remove(5); +// +// Assertions.assertEquals(expectedCommands, actualCommands); +// +// Assertions.assertEquals( +// CommandResult.successful(0, hello.getRoot(), null), +// manager.execute(CHAD_SENDER, "hello") +// ); +// Assertions.assertEquals( +// CommandResult.successful(1, hello.findNode("world"), "Hello World"), +// manager.execute(CHAD_SENDER, "hello world") +// ); +// +// Assertions.assertEquals( +// CommandResult.successful(0, hi.getRoot(), null), +// manager.execute(CHAD_SENDER, "hi") +// ); +// Assertions.assertEquals( +// CommandResult.successful(1, hi.findNode("world"), "Hi World"), +// manager.execute(CHAD_SENDER, "hi world") +// ); +// +// Assertions.assertEquals( +// CommandResult.successful(0, greetings.getRoot(), null), +// manager.execute(CHAD_SENDER, "greetings") +// ); +// Assertions.assertEquals( +// CommandResult.successful(1, greetings.findNode("world"), "Hello, world!"), +// manager.execute(CHAD_SENDER, "greetings world") +// ); +// +// Assertions.assertEquals( +// CommandResult.successful(1, welcome.findNode("name"), null), +// manager.execute(CHAD_SENDER, "welcome test") +// ); +// Assertions.assertEquals( +// CommandResult.successful(2, welcome.findNode("world"), "Welcome test"), +// manager.execute(CHAD_SENDER, "welcome test world") +// ); +// +// Assertions.assertEquals( +// CommandResult.successful(1, wassup.findNode("name"), null), +// manager.execute(CHAD_SENDER, "wassup test") +// ); +// Assertions.assertEquals( +// CommandResult.successful(2, wassup.findNode("nice"), "Nice to meet you, test!"), +// manager.execute(CHAD_SENDER, "wassup test nice") +// ); +// +// Assertions.assertEquals( +// CommandResult.successful(1, aye.findNode("name"), null), +// manager.execute(CHAD_SENDER, "aye test") +// ); +// Assertions.assertEquals( +// CommandResult.successful(2, aye.findNode("nice"), "Aye test"), +// manager.execute(CHAD_SENDER, "aye test nice") +// ); +// } +// +// public static class TestExecute { +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello") +// public void hello() { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hello world") +// public CommandResult helloWorld() { +// return CommandResult.successful( +// 1, +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(this, "helloWorld")), +// null +// ), +// "Hello World" +// ); +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hi") +// public void hi(@NotNull CommandSender sender) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("hi world") +// public CommandResult hiWorld(@NotNull CommandSender sender) { +// return CommandResult.successful( +// 1, +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(this, "hiWorld", CommandSender.class)), +// null +// ), +// "Hi World" +// ); +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("greetings") +// public void greetings(@NotNull CommandContext context) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("greetings world") +// public @NotNull CommandResult greetingsWorld(@NotNull CommandContext context) { +// return CommandResult.successful(context, "Hello, world!"); +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("welcome ") +// public void welcome(@NotNull CommandSender sender, @NotNull String name) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("welcome world") +// public CommandResult welcomeWorld(@NotNull CommandSender sender, @NotNull String name) { +// return CommandResult.successful( +// 2, +// new LiteralCommandNode( +// "world", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(this, "welcomeWorld", CommandSender.class, String.class)), +// null +// ), +// "Welcome " + name +// ); +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("wassup ") +// public void wassup(@NotNull CommandContext context, @NotNull String name) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("wassup nice") +// public @NotNull CommandResult wassupNice(@NotNull CommandContext context, @NotNull String name) { +// return CommandResult.successful(context, "Nice to meet you, " + name + "!"); +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("aye ") +// public void aye(@NotNull String name) { +// } +// +// @com.wizardlybump17.wlib.command.annotation.Command("aye nice") +// public CommandResult ayeNice(@NotNull String name) { +// return CommandResult.successful( +// 2, +// new LiteralCommandNode( +// "nice", +// List.of(), +// Assertions.assertDoesNotThrow(() -> MethodCommandExtractor.createExecutor(this, "ayeNice", String.class)), +// null +// ), +// "Aye " + name +// ); +// } +// } +//} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandNodeFactoryTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandNodeFactoryTests.java new file mode 100644 index 00000000..8fc74708 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/extractor/method/MethodCommandNodeFactoryTests.java @@ -0,0 +1,974 @@ +package com.wizardlybump17.wlib.test.command.extractor.method; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.extractor.method.MethodCommandExtractor; +import com.wizardlybump17.wlib.command.input.object.AllowedUUIDInputs; +import com.wizardlybump17.wlib.command.input.primitive.AllowedCharacterInputs; +import com.wizardlybump17.wlib.command.input.primitive.number.*; +import com.wizardlybump17.wlib.command.input.string.AllowedStringInputs; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.node.object.UUIDCommandNode; +import com.wizardlybump17.wlib.command.node.primitive.CharacterCommandNode; +import com.wizardlybump17.wlib.command.node.primitive.number.*; +import com.wizardlybump17.wlib.command.node.string.StringCommandNode; +import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry; +import com.wizardlybump17.wlib.command.suggestion.primitive.number.*; +import com.wizardlybump17.wlib.command.suggestion.string.StringSuggester; +import com.wizardlybump17.wlib.test.util.AssertionUtil; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +class MethodCommandNodeFactoryTests { + + static MethodCommandExtractor commandExtractor; + + @BeforeAll + static void setup() { + MethodCommandNodeFactoryRegistry registry = new MethodCommandNodeFactoryRegistry(); + registry.registerDefaults(); + commandExtractor = new MethodCommandExtractor(registry); + } + + @AfterAll + static void shutdown() { + if (commandExtractor == null) + return; + + MethodCommandNodeFactoryRegistry registry = commandExtractor.getFactoryRegistry(); + registry.clear(); + commandExtractor = null; + } + + @Test + void testByte() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new ByteCommandNode( + "arg0", + List.of(), + AllowedByteInputs.unlimited(), + ByteSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new ByteCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new ByteCommandNode( + "arg1", + List.of(), + AllowedByteInputs.unlimited(), + ByteSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedByteInputs.unlimited(), + ByteSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + TestByte object = new TestByte(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestByte { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(byte arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(byte arg0, byte arg1) { + } + } + + @Test + void testShort() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new ShortCommandNode( + "arg0", + List.of(), + AllowedShortInputs.unlimited(), + ShortSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new ShortCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new ShortCommandNode( + "arg1", + List.of(), + AllowedShortInputs.unlimited(), + ShortSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedShortInputs.unlimited(), + ShortSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + TestShort object = new TestShort(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestShort { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(short arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(short arg0, short arg1) { + } + } + + @Test + void testInt() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new IntegerCommandNode( + "arg0", + List.of(), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new IntegerCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new IntegerCommandNode( + "arg1", + List.of(), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + TestInt object = new TestInt(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestInt { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(int arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(int arg0, int arg1) { + } + } + + @Test + void testLong() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LongCommandNode( + "arg0", + List.of(), + AllowedLongInputs.unlimited(), + LongSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LongCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new LongCommandNode( + "arg1", + List.of(), + AllowedLongInputs.unlimited(), + LongSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedLongInputs.unlimited(), + LongSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + TestLong object = new TestLong(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestLong { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(long arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(long arg0, long arg1) { + } + } + + @Test + void testFloat() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new FloatCommandNode( + "arg0", + List.of(), + AllowedFloatInputs.unlimited(), + FloatSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new FloatCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new FloatCommandNode( + "arg1", + List.of(), + AllowedFloatInputs.unlimited(), + FloatSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedFloatInputs.unlimited(), + FloatSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + TestFloat object = new TestFloat(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestFloat { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(float arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(float arg0, float arg1) { + } + } + + @Test + void testDouble() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new DoubleCommandNode( + "arg0", + List.of(), + AllowedDoubleInputs.unlimited(), + DoubleSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new DoubleCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new DoubleCommandNode( + "arg1", + List.of(), + AllowedDoubleInputs.unlimited(), + DoubleSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedDoubleInputs.unlimited(), + DoubleSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ); + + TestDouble object = new TestDouble(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestDouble { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(double arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(double arg0, double arg1) { + } + } + + @Test + void testChar() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new CharacterCommandNode( + "arg0", + List.of(), + AllowedCharacterInputs.anyNotNull(), + null, + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new CharacterCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new CharacterCommandNode( + "arg1", + List.of(), + AllowedCharacterInputs.anyNotNull(), + null, + null, + null + ) + ), + null, + null + ) + ), + AllowedCharacterInputs.anyNotNull(), + null, + null, + null + ) + ), + null, + null + ) + ); + + TestChar object = new TestChar(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestChar { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(char arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(char arg0, char arg1) { + } + } + + @Test + void testString() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new StringCommandNode( + "arg0", + List.of(), + AllowedStringInputs.anyNullable(), + StringSuggester.any(), + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new StringCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new StringCommandNode( + "arg1", + List.of(), + AllowedStringInputs.anyNullable(), + StringSuggester.any(), + null, + null + ) + ), + null, + null + ) + ), + AllowedStringInputs.anyNullable(), + StringSuggester.any(), + null, + null + ) + ), + null, + null + ) + ); + + TestString object = new TestString(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestString { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(String arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(String arg0, String arg1) { + } + } + + @Test + void testUUID() { + Command test0 = new Command( + new LiteralCommandNode( + "test", + List.of( + new UUIDCommandNode( + "arg0", + List.of(), + AllowedUUIDInputs.anyNullable(), + null, + null, + null + ) + ), + null, + null + ) + ); + Command test1 = new Command( + new LiteralCommandNode( + "test", + List.of( + new UUIDCommandNode( + "arg0", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new UUIDCommandNode( + "arg1", + List.of(), + AllowedUUIDInputs.anyNullable(), + null, + null, + null + ) + ), + null, + null + ) + ), + AllowedUUIDInputs.anyNullable(), + null, + null, + null + ) + ), + null, + null + ) + ); + + TestUUID object = new TestUUID(); + + List expected = new ArrayList<>(List.of(test0, test1)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestUUID { + + @com.wizardlybump17.wlib.command.annotation.Command("test ") + public void test(UUID arg0) { + } + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 ") + public void test(UUID arg0, UUID arg1) { + } + } + + @Test + void testMix() { + Command test0 = new Command(new LiteralCommandNode( + "test", + List.of( + new IntegerCommandNode( + "int0", + List.of( + new IntegerCommandNode( + "int1", + List.of( + new LiteralCommandNode( + "test1", + List.of( + new ByteCommandNode( + "byte0", + List.of( + new ByteCommandNode( + "byte1", + List.of( + new LiteralCommandNode( + "test2", + List.of( + new FloatCommandNode( + "float0", + List.of( + new LiteralCommandNode( + "test3", + List.of( + new FloatCommandNode( + "float1", + List.of( + new LongCommandNode( + "long0", + List.of( + new LongCommandNode( + "long1", + List.of( + new LiteralCommandNode( + "test4", + List.of( + new ShortCommandNode( + "short0", + List.of( + new ShortCommandNode( + "short1", + List.of( + new LiteralCommandNode( + "test5", + List.of( + new DoubleCommandNode( + "double0", + List.of( + new DoubleCommandNode( + "double1", + List.of( + new LiteralCommandNode( + "test6", + List.of( + new CharacterCommandNode( + "char0", + List.of( + new CharacterCommandNode( + "char1", + List.of( + new LiteralCommandNode( + "test7", + List.of( + new StringCommandNode( + "string0", + List.of( + new LiteralCommandNode( + "test8", + List.of( + new StringCommandNode( + "string1", + List.of( + new UUIDCommandNode( + "uuid0", + List.of( + new LiteralCommandNode( + "test9", + List.of( + new UUIDCommandNode( + "uuid1", + List.of(), + AllowedUUIDInputs.anyNullable(), + null, + null, + null + ) + ), + null, + null + ) + ), + AllowedUUIDInputs.anyNullable(), + null, + null, + null + ) + ), + AllowedStringInputs.anyNullable(), + StringSuggester.any(), + null, + null + ) + ), + null, + null + ) + ), + AllowedStringInputs.anyNullable(), + StringSuggester.any(), + null, + null + ) + ), + null, + null + ) + ), + AllowedCharacterInputs.anyNotNull(), + null, + null, + null + ) + ), + AllowedCharacterInputs.anyNotNull(), + null, + null, + null + ) + ), + null, + null + ) + ), + AllowedDoubleInputs.unlimited(), + DoubleSuggester.unlimited(), + null, + null + ) + ), + AllowedDoubleInputs.unlimited(), + DoubleSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedShortInputs.unlimited(), + ShortSuggester.unlimited(), + null, + null + ) + ), + AllowedShortInputs.unlimited(), + ShortSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedLongInputs.unlimited(), + LongSuggester.unlimited(), + null, + null + ) + ), + AllowedLongInputs.unlimited(), + LongSuggester.unlimited(), + null, + null + ) + ), + AllowedFloatInputs.unlimited(), + FloatSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedFloatInputs.unlimited(), + FloatSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedByteInputs.unlimited(), + ByteSuggester.unlimited(), + null, + null + ) + ), + AllowedByteInputs.unlimited(), + ByteSuggester.unlimited(), + null, + null + ) + ), + null, + null + ) + ), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + AllowedIntegerInputs.unlimited(), + IntegerSuggester.unlimited(), + null, + null + ) + ), + null, + null + )); + + TestMix object = new TestMix(); + + List expected = new ArrayList<>(List.of(test0)); + List actual = Assertions.assertDoesNotThrow(() -> commandExtractor.extract(object)); + + expected.sort(null); + actual.sort(null); + + AssertionUtil.assertCommandsEqualsIgnoreExecutor(expected, actual); + } + + public static final class TestMix { + + @com.wizardlybump17.wlib.command.annotation.Command("test test1 test2 test3 test4 test5 test6 test7 test8 test9 ") + public void test(int int0, int int1, byte byte0, byte byte1, float float0, float float1, long long0, long log1, short short0, short short1, double double0, double double1, char char0, char char1, String string0, String string1, UUID uuid0, UUID uuid1) { + } + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/CommandManagerSuggestionTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/CommandManagerSuggestionTests.java new file mode 100644 index 00000000..bb2ab2e9 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/CommandManagerSuggestionTests.java @@ -0,0 +1,1013 @@ +package com.wizardlybump17.wlib.test.command.manager; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.input.primitive.number.AllowedIntegerInputs; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.node.primitive.number.IntegerCommandNode; +import com.wizardlybump17.wlib.command.result.CommandResult; +import com.wizardlybump17.wlib.command.sender.BasicCommandSender; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import com.wizardlybump17.wlib.command.suggestion.primitive.number.IntegerSuggester; +import com.wizardlybump17.wlib.util.CollectionUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +class CommandManagerSuggestionTests { + + static final @NotNull Consumer SENDER_MESSAGE_CONSUMER = System.out::println; + static final @NotNull CommandSender CHAD_SENDER = new BasicCommandSender<>(new Object(), "Chad", UUID.nameUUIDFromBytes("Chad".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> true); + static final @NotNull CommandSender BETA_SENDER = new BasicCommandSender<>(new Object(), "Beta", UUID.nameUUIDFromBytes("Beta".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> false); + + @Test + void testSuggestionsSuccessListChad0() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of(), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("hello", "hi", "welcome"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, List.of())); + + Assertions.assertTrue(CollectionUtil.contentEquals(expected, actual)); + } + + @Test + void testSuggestionsSuccessListChad1() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of(), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("hello", "hi", "welcome"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, List.of("he"))); + + Assertions.assertTrue(CollectionUtil.contentEquals(expected, actual)); + } + + @Test + void testSuggestionsSuccessListChad2() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of(), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("there"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, List.of("hi", ""))); + + Assertions.assertTrue(CollectionUtil.contentEquals(expected, actual)); + } + + @Test + void testSuggestionsSuccessListChad3() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of(), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("there"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, List.of("hi", "ther"))); + + Assertions.assertTrue(CollectionUtil.contentEquals(expected, actual)); + } + + @Test + void testSuggestionsSuccessListChad4() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("0", "50", "200", "3000", "50000", "100000"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, List.of("welcome", ""))); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessListChad6() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("0", "50", "200", "3000", "50000", "100000"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, List.of("welcome", "10"))); + + Assertions.assertTrue(CollectionUtil.contentEquals(expected, actual)); + } + + @Test + void testSuggestionsFailListBeta0() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of(), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of(); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(BETA_SENDER, List.of())); + + Assertions.assertTrue(CollectionUtil.contentEquals(expected, actual)); + } + + @Test + void testSuggestionsSuccessStringChad0() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("hi", "hello", "welcome"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, "")); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringChad1() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("there"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, "hi t")); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringChad2() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + List expected = List.of("0", "50", "200", "3000", "50000", "100000"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, "welcome 1")); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringChad3() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + Command command3 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "spaced string", + List.of(), + context -> CommandResult.successful("spaced string"), + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + manager.registerCommand("test", command3); + + List expected = List.of("spaced string"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, "test \"spaced ")); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringArrayChad0() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + Command command3 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "spaced string", + List.of(), + context -> CommandResult.successful("spaced string"), + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + manager.registerCommand("test", command3); + + List expected = List.of("hi", "test", "hello", "welcome"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, new String[]{})); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringArrayChad1() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + Command command3 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "spaced string", + List.of(), + context -> CommandResult.successful("spaced string"), + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + manager.registerCommand("test", command3); + + List expected = List.of("hi", "test", "hello", "welcome"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, new String[]{"he"})); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringArrayChad2() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + Command command3 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "spaced string", + List.of(), + context -> CommandResult.successful("spaced string"), + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + manager.registerCommand("test", command3); + + List expected = List.of("0", "50", "200", "3000", "50000", "100000"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, new String[]{"welcome", ""})); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringArrayChad3() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + Command command3 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "spaced string", + List.of(), + context -> CommandResult.successful("spaced string"), + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + manager.registerCommand("test", command3); + + List expected = List.of("spaced string"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, new String[]{"test", ""})); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringArrayChad4() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + Command command3 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "spaced string", + List.of(), + context -> CommandResult.successful("spaced string"), + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + manager.registerCommand("test", command3); + + List expected = List.of("spaced string"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, new String[]{"test", "spaced"})); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuggestionsSuccessStringArrayChad5() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of( + new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of( + new IntegerCommandNode( + "repeat", + List.of(), + AllowedIntegerInputs.positive(), + IntegerSuggester.positive(), + context -> CommandResult.successful("welcome".repeat(context.arguments().getArgument("repeat").orElseThrow().data())), + "permission" + ) + ), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + Command command3 = new Command( + new LiteralCommandNode( + "test", + List.of( + new LiteralCommandNode( + "spaced string", + List.of(), + context -> CommandResult.successful("spaced string"), + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + manager.registerCommand("test", command3); + + List expected = List.of("spaced string"); + List actual = Assertions.assertDoesNotThrow(() -> manager.getSuggestions(CHAD_SENDER, new String[]{"test", "\"space"})); + + Assertions.assertEquals(expected, actual); + } + + //TODO: add more tests +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/CommandManagerTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/CommandManagerTests.java new file mode 100644 index 00000000..7fc34522 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/CommandManagerTests.java @@ -0,0 +1,558 @@ +package com.wizardlybump17.wlib.test.command.manager; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.command.result.CommandResult; +import com.wizardlybump17.wlib.command.sender.BasicCommandSender; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +class CommandManagerTests { + + static final @NotNull Consumer SENDER_MESSAGE_CONSUMER = System.out::println; + static final @NotNull CommandSender CHAD_SENDER = new BasicCommandSender<>(new Object(), "Chad", UUID.nameUUIDFromBytes("Chad".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> true); + static final @NotNull CommandSender BETA_SENDER = new BasicCommandSender<>(new Object(), "Beta", UUID.nameUUIDFromBytes("Beta".getBytes()), SENDER_MESSAGE_CONSUMER, $ -> false); + + @Test + void testRegisterDifferent() { + Command command0 = new Command( + new LiteralCommandNode( + "hello0", + List.of(), + context -> CommandResult.successful("hello0"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hello1", + List.of(), + context -> CommandResult.successful("hello1"), + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "hello2", + List.of(), + context -> CommandResult.successful("hello2"), + "permission") + ); + Command command3 = new Command( + new LiteralCommandNode( + "hello3", + List.of(), + context -> CommandResult.successful("hello3"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + + Command registeredCommand0 = manager.registerCommand("test", command0); + Command registeredCommand1 = manager.registerCommand("test", command1); + Command registeredCommand2 = manager.registerCommand("test", command2); + Command registeredCommand3 = manager.registerCommand("test", command3); + + Assertions.assertEquals(command0, registeredCommand0); + Assertions.assertEquals(command1, registeredCommand1); + Assertions.assertEquals(command2, registeredCommand2); + Assertions.assertEquals(command3, registeredCommand3); + } + + @Test + void testRegisterMerging0() { + CommandNodeExecutor helloExecutor = context -> CommandResult.successful("hello"); + CommandNodeExecutor helloWorldExecutor = context -> CommandResult.successful("hello world"); + CommandNodeExecutor helloWorldHiExecutor = context -> CommandResult.successful("hello world hi"); + + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + helloExecutor, + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + helloWorldExecutor, + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of( + new LiteralCommandNode( + "hi", + List.of(), + helloWorldHiExecutor, + "permission" + ) + ), + null, + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + + Command registeredCommand0 = manager.registerCommand("test", command0); + Command registeredCommand1 = manager.registerCommand("test", command1); + Command registeredCommand2 = manager.registerCommand("test", command2); + + Command expectedCommand0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + helloExecutor, + "permission" + ) + ); + Command expectedCommand1 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + helloWorldExecutor, + "permission" + ) + ), + helloExecutor, + "permission" + ) + ); + Command expectedCommand2 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of( + new LiteralCommandNode( + "hi", + List.of(), + helloWorldHiExecutor, + "permission" + ) + ), + helloWorldExecutor, + "permission" + ) + ), + helloExecutor, + "permission" + ) + ); + + Assertions.assertEquals(expectedCommand0, registeredCommand0); + Assertions.assertEquals(expectedCommand1, registeredCommand1); + Assertions.assertEquals(expectedCommand2, registeredCommand2); + } + + @Test + void testRegisterMerging1() { + CommandNodeExecutor helloExecutor = context -> CommandResult.successful("hello"); + CommandNodeExecutor helloWorldExecutor = context -> CommandResult.successful("hello world"); + CommandNodeExecutor helloWorldHiExecutor = context -> CommandResult.successful("hello world hi"); + CommandNodeExecutor helloThereExecutor = context -> CommandResult.successful("hello there"); + + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + helloExecutor, + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + helloWorldExecutor, + "permission" + ), + new LiteralCommandNode( + "there", + List.of(), + helloThereExecutor, + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of( + new LiteralCommandNode( + "hi", + List.of(), + helloWorldHiExecutor, + "permission" + ) + ), + null, + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + + Command registeredCommand0 = manager.registerCommand("test", command0); + Command registeredCommand1 = manager.registerCommand("test", command1); + Command registeredCommand2 = manager.registerCommand("test", command2); + + Command expectedCommand0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + helloExecutor, + "permission" + ) + ); + Command expectedCommand1 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + helloWorldExecutor, + "permission" + ), + new LiteralCommandNode( + "there", + List.of(), + helloThereExecutor, + "permission" + ) + ), + helloExecutor, + "permission" + ) + ); + Command expectedCommand2 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of( + new LiteralCommandNode( + "hi", + List.of(), + helloWorldHiExecutor, + "permission" + ) + ), + helloWorldExecutor, + "permission" + ), + new LiteralCommandNode( + "there", + List.of(), + helloThereExecutor, + "permission" + ) + ), + helloExecutor, + "permission" + ) + ); + + Assertions.assertEquals(expectedCommand0, registeredCommand0); + Assertions.assertEquals(expectedCommand1, registeredCommand1); + Assertions.assertEquals(expectedCommand2, registeredCommand2); + } + + @Test + void testCommandNotFound0() { + Command command = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command); + + Assertions.assertEquals( + CommandResult.notFound(CommandResult.ErrorDetails.nodeNotFound(0, "hello0")), + manager.execute(CHAD_SENDER, List.of("hello0")) + ); + } + + @Test + void testSuccess0() { + LiteralCommandNode helloNode = new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ); + Command command0 = new Command(helloNode); + + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of(), + context -> CommandResult.successful("hi"), + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of(), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + CommandResult expected = CommandResult.successful("hello"); + CommandResult actual = manager.execute(CHAD_SENDER, List.of("hello")); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testSuccess1() { + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + context -> CommandResult.successful("hello"), + "permission" + ) + ); + + LiteralCommandNode thereNode = new LiteralCommandNode( + "there", + List.of(), + context -> CommandResult.successful("hi there"), + "permission" + ); + Command command1 = new Command( + new LiteralCommandNode( + "hi", + List.of(thereNode), + null, + "permission" + ) + ); + + Command command2 = new Command( + new LiteralCommandNode( + "welcome", + List.of(), + context -> CommandResult.successful("welcome"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + manager.registerCommand("test", command0); + manager.registerCommand("test", command1); + manager.registerCommand("test", command2); + + CommandResult expected = CommandResult.successful("hi there"); + CommandResult actual = manager.execute(CHAD_SENDER, List.of("hi", "there")); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testRegisterMultipleDifferent0() { + Command command0 = new Command( + new LiteralCommandNode( + "hello0", + List.of(), + context -> CommandResult.successful("hello0"), + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hello1", + List.of(), + context -> CommandResult.successful("hello1"), + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "hello2", + List.of(), + context -> CommandResult.successful("hello2"), + "permission") + ); + Command command3 = new Command( + new LiteralCommandNode( + "hello3", + List.of(), + context -> CommandResult.successful("hello3"), + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + + List expected = List.of(command0, command1, command2, command3); + List actual = manager.registerCommands("test", List.of(command0, command1, command2, command3)); + + Assertions.assertEquals(expected, actual); + } + + @Test + void testRegisterMultipleMerging0() { + CommandNodeExecutor helloExecutor = context -> CommandResult.successful("hello"); + CommandNodeExecutor helloWorldExecutor = context -> CommandResult.successful("hello world"); + CommandNodeExecutor helloWorldHiExecutor = context -> CommandResult.successful("hello world hi"); + + Command command0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + helloExecutor, + "permission" + ) + ); + Command command1 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + helloWorldExecutor, + "permission" + ) + ), + null, + "permission" + ) + ); + Command command2 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of( + new LiteralCommandNode( + "hi", + List.of(), + helloWorldHiExecutor, + "permission" + ) + ), + null, + "permission" + ) + ), + null, + "permission" + ) + ); + + CommandManager manager = new CommandManager(); + + Command expectedCommand0 = new Command( + new LiteralCommandNode( + "hello", + List.of(), + helloExecutor, + "permission" + ) + ); + Command expectedCommand1 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of(), + helloWorldExecutor, + "permission" + ) + ), + helloExecutor, + "permission" + ) + ); + Command expectedCommand2 = new Command( + new LiteralCommandNode( + "hello", + List.of( + new LiteralCommandNode( + "world", + List.of( + new LiteralCommandNode( + "hi", + List.of(), + helloWorldHiExecutor, + "permission" + ) + ), + helloWorldExecutor, + "permission" + ) + ), + helloExecutor, + "permission" + ) + ); + + List expected = List.of(expectedCommand0, expectedCommand1, expectedCommand2); + List actual = manager.registerCommands("test", List.of(command0, command1, command2)); + Assertions.assertEquals(expected, actual); + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/listener/CommandManagerListenerTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/listener/CommandManagerListenerTests.java new file mode 100644 index 00000000..e04c7055 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/manager/listener/CommandManagerListenerTests.java @@ -0,0 +1,613 @@ +package com.wizardlybump17.wlib.test.command.manager.listener; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.manager.listener.CommandManagerListener; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import com.wizardlybump17.wlib.test.util.AssertionUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +class CommandManagerListenerTests { + + @Test + void testBeingCalledRegister() { + AtomicInteger counter = new AtomicInteger(); + + CommandManager manager = new CommandManager(); + manager.addListener(new CommandManagerListener() { + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + counter.incrementAndGet(); + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + }); + + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test", + List.of(), + null, + null + ) + ) + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test", + List.of(), + null, + null + ) + ) + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test", + List.of(), + null, + null + ) + ) + ); + + Assertions.assertEquals(3, counter.get()); + } + + @Test + void testSeeingRightCommands() { + List seenCommands = new ArrayList<>(); + + CommandManager manager = new CommandManager(); + manager.addListener(new CommandManagerListener() { + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + seenCommands.add(identifier + CommandManager.SEPARATOR + command.getRoot().getName()); + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + }); + + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ) + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test1", + List.of(), + null, + null + ) + ) + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test2", + List.of(), + null, + null + ) + ) + ); + + Assertions.assertEquals( + List.of( + "test:test0", + "test:test1", + "test:test2" + ), + seenCommands + ); + } + + @Test + void testBeingCalledClear() { + AtomicBoolean called = new AtomicBoolean(); + + CommandManager manager = new CommandManager(); + + manager.addListener(new CommandManagerListener() { + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + Assertions.assertTrue(manager.isEmpty()); + called.set(true); + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + }); + + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ) + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test1", + List.of(), + null, + null + ) + ) + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test2", + List.of(), + null, + null + ) + ) + ); + + manager.clear(); + + Assertions.assertTrue(called.get()); + } + + @Test + void testBeingCalledUnregisterByIdentifierAndName() { + List seenCommands = new ArrayList<>(); + + CommandManager manager = new CommandManager(); + manager.addListener(new CommandManagerListener() { + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + seenCommands.add(identifier + CommandManager.SEPARATOR + command.getName()); + } + }); + + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + )); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test1", + List.of(), + null, + null + ) + )); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test2", + List.of(), + null, + null + ) + )); + + manager.unregister("test", "test0"); + manager.unregister("test", "test1"); + manager.unregister("test", "test2"); + + Assertions.assertEquals( + List.of( + "test:test0", + "test:test1", + "test:test2" + ), + seenCommands + ); + } + + @Test + void testBeingCalledUnregisterByIdentifierAndHolder0() { + List seenCommands = new ArrayList<>(); + + CommandManager manager = new CommandManager(); + manager.addListener(new CommandManagerListener() { + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + seenCommands.add(identifier + CommandManager.SEPARATOR + command.getName()); + } + }); + + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test1", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test2", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test10", + List.of(), + null, + null + ) + ), + "test1" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test11", + List.of(), + null, + null + ) + ), + "test1" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test12", + List.of(), + null, + null + ) + ), + "test1" + ); + + manager.unregisterByHolder("test", "test"); + + AssertionUtil.assertContentEquals( + List.of( + "test:test0", + "test:test1", + "test:test2" + ), + seenCommands + ); + } + + @Test + void testBeingCalledUnregisterByIdentifierAndHolder1() { + List seenCommands = new ArrayList<>(); + + CommandManager manager = new CommandManager(); + manager.addListener(new CommandManagerListener() { + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + seenCommands.add(identifier + CommandManager.SEPARATOR + command.getName()); + } + }); + + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test1", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test2", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test10", + List.of(), + null, + null + ) + ), + "test1" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test11", + List.of(), + null, + null + ) + ), + "test1" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test12", + List.of(), + null, + null + ) + ), + "test1" + ); + + manager.unregisterByHolder("test1", "test1"); + + AssertionUtil.assertContentEquals( + List.of( + "test1:test10", + "test1:test11", + "test1:test12" + ), + seenCommands + ); + } + + @Test + void testBeingCalledUnregisterByIdentifierAndHolder2() { + List seenCommands = new ArrayList<>(); + + CommandManager manager = new CommandManager(); + manager.addListener(new CommandManagerListener() { + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + seenCommands.add(identifier + CommandManager.SEPARATOR + command.getName()); + } + }); + + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test0", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test1", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test", + new Command( + new LiteralCommandNode( + "test2", + List.of(), + null, + null + ) + ), + "test" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test10", + List.of(), + null, + null + ) + ), + "test1" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test11", + List.of(), + null, + null + ) + ), + "test1" + ); + manager.registerCommand( + "test1", + new Command( + new LiteralCommandNode( + "test12", + List.of(), + null, + null + ) + ), + "test1" + ); + + manager.unregisterByHolder("test", "test"); + manager.unregisterByHolder("test1", "test1"); + + AssertionUtil.assertContentEquals( + List.of( + "test:test0", + "test:test1", + "test:test2", + "test1:test10", + "test1:test11", + "test1:test12" + ), + seenCommands + ); + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/command/sender/BasicCommandSenderTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/command/sender/BasicCommandSenderTests.java new file mode 100644 index 00000000..c19eef75 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/command/sender/BasicCommandSenderTests.java @@ -0,0 +1,64 @@ +package com.wizardlybump17.wlib.test.command.sender; + +import com.wizardlybump17.wlib.command.sender.BasicCommandSender; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +class BasicCommandSenderTests { + + @Test + void testPermissionsAllTrue() { + String name = "Test Sender"; + BasicCommandSender sender = BasicCommandSender.builder() + .handle(new Object()) + .name(name) + .id(UUID.nameUUIDFromBytes(name.getBytes())) + .permissionTest(permission -> true) + .build(); + Assertions.assertTrue(sender.hasPermission("test0.permission0")); + Assertions.assertTrue(sender.hasPermission("test0.permission1")); + Assertions.assertTrue(sender.hasPermission("test0.permission2")); + Assertions.assertTrue(sender.hasPermission("test1.permission0")); + Assertions.assertTrue(sender.hasPermission("test1.permission1")); + Assertions.assertTrue(sender.hasPermission("test1.permission2")); + } + + @Test + void testPermissionsAllFalse() { + String name = "Test Sender"; + BasicCommandSender sender = BasicCommandSender.builder() + .handle(new Object()) + .name(name) + .id(UUID.nameUUIDFromBytes(name.getBytes())) + .permissionTest(permission -> false) + .build(); + Assertions.assertFalse(sender.hasPermission("test0.permission0")); + Assertions.assertFalse(sender.hasPermission("test0.permission1")); + Assertions.assertFalse(sender.hasPermission("test0.permission2")); + Assertions.assertFalse(sender.hasPermission("test1.permission0")); + Assertions.assertFalse(sender.hasPermission("test1.permission1")); + Assertions.assertFalse(sender.hasPermission("test1.permission2")); + } + + @Test + void testSpecificPermissions() { + String name = "Test Sender"; + BasicCommandSender sender = BasicCommandSender.builder() + .handle(new Object()) + .name(name) + .id(UUID.nameUUIDFromBytes(name.getBytes())) + .permissionTest(permission -> permission.startsWith("test0.")) + .build(); + Assertions.assertTrue(sender.hasPermission("test0.permission0")); + Assertions.assertTrue(sender.hasPermission("test0.permission1")); + Assertions.assertTrue(sender.hasPermission("test0.permission2")); + Assertions.assertFalse(sender.hasPermission("test1.permission0")); + Assertions.assertFalse(sender.hasPermission("test1.permission1")); + Assertions.assertFalse(sender.hasPermission("test1.permission2")); + Assertions.assertFalse(sender.hasPermission("test2.permission0")); + Assertions.assertFalse(sender.hasPermission("test2.permission1")); + Assertions.assertFalse(sender.hasPermission("test2.permission2")); + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/util/AssertionUtil.java b/commands/src/test/java/com/wizardlybump17/wlib/test/util/AssertionUtil.java new file mode 100644 index 00000000..7f332bf8 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/util/AssertionUtil.java @@ -0,0 +1,100 @@ +package com.wizardlybump17.wlib.test.util; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.util.CollectionUtil; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.AssertionFailureBuilder; + +import java.util.Collection; +import java.util.List; + +public final class AssertionUtil { + + private AssertionUtil() { + } + + public static void assertCommandsEqualsIgnoreExecutor(@Nullable List expected, @Nullable List actual) { + if (expected == null) { + if (actual != null) { + AssertionFailureBuilder.assertionFailure() + .expected(null) + .actual(actual) + .buildAndThrow(); + } + return; + } + + if (expected != null) { + if (actual == null) { + AssertionFailureBuilder.assertionFailure() + .expected(expected) + .actual(null) + .buildAndThrow(); + return; + } + } + + if (expected.size() != actual.size()) { + AssertionFailureBuilder.assertionFailure() + .expected(expected) + .actual(actual) + .message("Expected and actual lists have different sizes") + .buildAndThrow(); + return; + } + + for (int i = 0; i < expected.size(); i++) { + Command expectedCommand = expected.get(i); + Command actualCommand = actual.get(i); + + if (!expectedCommand.equalsIgnoreExecutor(actualCommand)) { + AssertionFailureBuilder.assertionFailure() + .expected(expectedCommand) + .actual(actualCommand) + .message("Commands at index " + i + " are not equal") + .buildAndThrow(); + return; + } + } + } + + public static void assertContentEquals(@Nullable Collection expected, @Nullable Collection actual) { + if (expected == null) { + if (actual != null) { + AssertionFailureBuilder.assertionFailure() + .expected(null) + .actual(actual) + .buildAndThrow(); + } + return; + } + + if (expected != null) { + if (actual == null) { + AssertionFailureBuilder.assertionFailure() + .expected(expected) + .actual(null) + .buildAndThrow(); + return; + } + } + + if (expected.size() != actual.size()) { + AssertionFailureBuilder.assertionFailure() + .expected(expected) + .actual(actual) + .message("Expected and actual collections have different sizes") + .buildAndThrow(); + return; + } + + if (!CollectionUtil.contentEquals(expected, actual)) { + AssertionFailureBuilder.assertionFailure() + .expected(expected) + .actual(actual) + .message("Expected and actual collections do not contain the same elements") + .buildAndThrow(); + return; + } + } +} diff --git a/commands/src/test/java/com/wizardlybump17/wlib/test/util/AssertionUtilTests.java b/commands/src/test/java/com/wizardlybump17/wlib/test/util/AssertionUtilTests.java new file mode 100644 index 00000000..62842155 --- /dev/null +++ b/commands/src/test/java/com/wizardlybump17/wlib/test/util/AssertionUtilTests.java @@ -0,0 +1,279 @@ +package com.wizardlybump17.wlib.test.util; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +import java.util.List; +import java.util.Set; + +class AssertionUtilTests { + +// @Test +// void testCommandsEqualsIgnoreExecutorTrue() { +// List left = List.of( +// new Command( +// new LiteralCommandNode( +// "test0", +// List.of(), +// context -> CommandResult.successful(context, "Hi"), +// "test0" +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "test1", +// List.of( +// new LiteralCommandNode( +// "test1_0", +// List.of( +// new LiteralCommandNode( +// "test1_0_0", +// List.of(), +// context -> CommandResult.successful(context, "Hello world"), +// null +// ) +// ), +// context -> CommandResult.successful(context, "Hello there"), +// null +// ), +// new LiteralCommandNode( +// "test1_1", +// List.of(), +// context -> CommandResult.successful(context, "I ran out of ideas"), +// null +// ) +// ), +// context -> CommandResult.successful(context, "Hello"), +// "test1" +// ) +// ) +// ); +// List right = List.of( +// new Command( +// new LiteralCommandNode( +// "test0", +// List.of(), +// null, +// "test0" +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "test1", +// List.of( +// new LiteralCommandNode( +// "test1_0", +// List.of( +// new LiteralCommandNode( +// "test1_0_0", +// List.of(), +// context -> CommandResult.successful(context, "Welcome"), +// null +// ) +// ), +// CommandResult::genericError, +// null +// ), +// new LiteralCommandNode( +// "test1_1", +// List.of(), +// CommandResult::noPermission, +// null +// ) +// ), +// context -> CommandResult.successful(context, "Hello"), +// "test1" +// ) +// ) +// ); +// +// Assertions.assertDoesNotThrow(() -> AssertionUtil.assertCommandsEqualsIgnoreExecutor(left, right)); +// } +// +// @Test +// void testCommandsEqualsIgnoreExecutorFalse() { +// List left = List.of( +// new Command( +// new LiteralCommandNode( +// "test0", +// List.of(), +// context -> CommandResult.successful(context, "Hi"), +// "test0" +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "test1", +// List.of( +// new LiteralCommandNode( +// "test1_0", +// List.of( +// new LiteralCommandNode( +// "test1_0_0", +// List.of(), +// context -> CommandResult.successful(context, "Hello world"), +// null +// ) +// ), +// context -> CommandResult.successful(context, "Hello there"), +// null +// ), +// new LiteralCommandNode( +// "test1_1", +// List.of(), +// context -> CommandResult.successful(context, "I ran out of ideas"), +// null +// ) +// ), +// context -> CommandResult.successful(context, "Hello"), +// "test1" +// ) +// ) +// ); +// List right = List.of( +// new Command( +// new LiteralCommandNode( +// "test1", +// List.of(), +// null, +// "test0" +// ) +// ), +// new Command( +// new LiteralCommandNode( +// "test1", +// List.of( +// new LiteralCommandNode( +// "test1_0", +// List.of( +// new LiteralCommandNode( +// "test1_0_0", +// List.of(), +// context -> CommandResult.successful(context, "Welcome"), +// null +// ) +// ), +// CommandResult::genericError, +// null +// ), +// new LiteralCommandNode( +// "test1_3", +// List.of(), +// CommandResult::noPermission, +// null +// ) +// ), +// context -> CommandResult.successful(context, "Hello"), +// "test1" +// ) +// ) +// ); +// +// Assertions.assertThrows(AssertionFailedError.class, () -> AssertionUtil.assertCommandsEqualsIgnoreExecutor(left, right)); +// } + + @Test + void testContentEqualsTrueLists() { + List right = List.of( + "hi", + "welcome", + "hello", + "nice to see you" + ); + List left = List.of( + "welcome", + "hi", + "nice to see you", + "hello" + ); + + Assertions.assertDoesNotThrow(() -> AssertionUtil.assertContentEquals(right, left)); + } + + @Test + void testContentEqualsTrueListAndSet() { + List right = List.of( + "hi", + "welcome", + "hello", + "nice to see you" + ); + Set left = Set.of( + "welcome", + "hi", + "nice to see you", + "hello" + ); + + Assertions.assertDoesNotThrow(() -> AssertionUtil.assertContentEquals(right, left)); + } + + @Test + void testContentEqualsFalseExtra() { + List right = List.of( + "hi", + "welcome", + "hello", + "nice to see you", + "nice to see you" + ); + List left = List.of( + "welcome", + "hi", + "nice to see you", + "hello" + ); + + Assertions.assertThrows(AssertionFailedError.class, () -> AssertionUtil.assertContentEquals(right, left)); + } + + @Test + void testContentEqualsFalseMissing() { + List right = List.of( + "hi", + "welcome", + "hello" + ); + List left = List.of( + "welcome", + "hi", + "nice to see you", + "hello" + ); + + Assertions.assertThrows(AssertionFailedError.class, () -> AssertionUtil.assertContentEquals(right, left)); + } + + @Test + void testContentEqualsFalseDifferent0() { + List right = List.of( + "test0", + "test1" + ); + List left = List.of( + "welcome", + "hi", + "nice to see you", + "hello" + ); + + Assertions.assertThrows(AssertionFailedError.class, () -> AssertionUtil.assertContentEquals(right, left)); + } + + @Test + void testContentEqualsFalseDifferent1() { + List right = List.of( + "test0", + "test1" + ); + Set left = Set.of( + "welcome", + "hi", + "nice to see you", + "hello" + ); + + Assertions.assertThrows(AssertionFailedError.class, () -> AssertionUtil.assertContentEquals(right, left)); + } +} diff --git a/core/build.gradle b/core/build.gradle index 4706f9f0..4fb49c34 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,8 +1,10 @@ +var gson = "2.13.2" + dependencies { compileOnly( 'org.projectlombok:lombok:1.18.32', 'org.jetbrains:annotations:23.1.0', - "io.papermc.paper:paper-api:1.20.6-R0.1-20240702.153951-124", + "io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT", ) annotationProcessor('org.projectlombok:lombok:1.18.32') @@ -10,26 +12,34 @@ dependencies { project(':utils'), project(':versions:adapter'), - project(':versions:v1_20_R4'), - project(':versions:v1_21_R1'), - project(':versions:v1_21_R3'), - project(':versions:v1_21_R5'), - project(':versions:v1_21_r7'), - project(':database'), project(':objects'), - project(':commands'), project(':config'), - project(':bukkit-utils'), + project(':bukkit-utils') ) + + implementation(project(':commands')) { + exclude group: "com.google.code.gson", module: "gson" + } + + implementation(project(":versions:v1_20_r4")) + implementation(project(":versions:v1_21_r1")) + implementation(project(":versions:v1_21_r3")) + implementation(project(":versions:v1_21_r5")) + implementation(project(":versions:v1_21_r7")) + + compileOnly("com.google.code.gson:gson:${gson}") } processResources { filesMatching('**/plugin.yml') { - expand 'version': project.version + expand(Map.of( + "version", project.version, + "gson", gson + )) } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/WLib.java b/core/src/main/java/com/wizardlybump17/wlib/WLib.java index 2b7bbfca..6ebe908f 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/WLib.java +++ b/core/src/main/java/com/wizardlybump17/wlib/WLib.java @@ -1,10 +1,20 @@ package com.wizardlybump17.wlib; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.wizardlybump17.wlib.adapter.AttributeAdapter; import com.wizardlybump17.wlib.adapter.ItemAdapter; +import com.wizardlybump17.wlib.adapter.command.CommandMapAdapter; import com.wizardlybump17.wlib.adapter.player.PlayerAdapter; -import com.wizardlybump17.wlib.command.args.ArgsReaderRegistry; -import com.wizardlybump17.wlib.command.reader.*; +import com.wizardlybump17.wlib.command.WLibCommandExecutor; +import com.wizardlybump17.wlib.command.extractor.method.MethodCommandExtractor; +import com.wizardlybump17.wlib.command.extractor.method.factory.OfflinePlayerMethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.extractor.method.factory.PlayerMethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.extractor.method.factory.object.JsonElementMethodCommandNodeFactory; +import com.wizardlybump17.wlib.command.listener.BukkitCommandManagerListener; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry; +import com.wizardlybump17.wlib.command.sender.BukkitCommandSender; import com.wizardlybump17.wlib.config.holder.BukkitConfigHolderFactory; import com.wizardlybump17.wlib.config.registry.ConfigHandlerRegistry; import com.wizardlybump17.wlib.config.registry.ConfigHolderFactoryRegistry; @@ -25,23 +35,30 @@ import com.wizardlybump17.wlib.util.bukkit.config.wrapper.potion.PotionDataWrapper; import com.wizardlybump17.wlib.util.bukkit.config.wrapper.potion.PotionEffectWrapper; import com.wizardlybump17.wlib.util.bukkit.particle.*; -import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.event.HandlerList; import org.bukkit.plugin.java.JavaPlugin; -@Getter public class WLib extends JavaPlugin { private final SaveControllersTask saveControllersTask = new SaveControllersTask(getLogger()); + private MethodCommandExtractor methodCommandExtractor; + private MethodCommandNodeFactoryRegistry methodCommandNodeFactoryRegistry; + private CommandManager commandManager; + private WLibCommandExecutor commandExecutor; + private BukkitCommandManagerListener commandManagerListener; + private Gson gson; + @Override public void onLoad() { + gson = new GsonBuilder().create(); + + initCommandSystem(); ItemMetaHandlerModel.initModels(); initAdapters(); initSerializables(); - initCommandSystem(); DatabaseRegister databaseRegister = DatabaseRegister.getInstance(); databaseRegister.registerDatabaseModel(new MySQLDatabaseModel()); @@ -50,6 +67,23 @@ public void onLoad() { initConfigs(); } + private void initCommandSystem() { + methodCommandNodeFactoryRegistry = new MethodCommandNodeFactoryRegistry(); + methodCommandExtractor = new MethodCommandExtractor(methodCommandNodeFactoryRegistry); + + methodCommandNodeFactoryRegistry.registerDefaults(); + methodCommandNodeFactoryRegistry.addFactory(new OfflinePlayerMethodCommandNodeFactory()); + methodCommandNodeFactoryRegistry.addFactory(new PlayerMethodCommandNodeFactory()); + methodCommandNodeFactoryRegistry.addFactory(new JsonElementMethodCommandNodeFactory(gson)); + + commandManager = new CommandManager(); + + commandExecutor = new WLibCommandExecutor(commandManager, getLogger()); + + commandManagerListener = new BukkitCommandManagerListener(commandExecutor); + commandManager.addListener(commandManagerListener); + } + protected void initConfigs() { getLogger().info("Initializing configs..."); @@ -71,20 +105,31 @@ public void onEnable() { @Override public void onDisable() { + gson = null; + + clearCommandSystem(); HandlerList.unregisterAll(this); saveControllersTask.cancel(); } - private void initCommandSystem() { - ArgsReaderRegistry.INSTANCE.add(new PlayerReader()); - ArgsReaderRegistry.INSTANCE.add(new OfflinePlayerReader()); - ArgsReaderRegistry.INSTANCE.add(new EntityTypeArgsReader()); - ArgsReaderRegistry.INSTANCE.add(new MaterialReader()); - ArgsReaderRegistry.INSTANCE.add(new BlockDataArgsReader()); - ArgsReaderRegistry.INSTANCE.add(new MapJsonArgsReader()); - ArgsReaderRegistry.INSTANCE.add(new PotionEffectTypeReader()); - ArgsReaderRegistry.INSTANCE.add(new EnchantmentReader()); - ArgsReaderRegistry.INSTANCE.add(new NamespacedKeyReader()); + private void clearCommandSystem() { + if (methodCommandNodeFactoryRegistry != null) + methodCommandNodeFactoryRegistry.clear(); + methodCommandNodeFactoryRegistry = null; + + methodCommandExtractor = null; + + if (commandManager != null) { + commandManager.clear(); + commandManager.clearListeners(); + } + commandManager = null; + + commandExecutor = null; + + commandManagerListener = null; + + BukkitCommandSender.clearCache(); } private void initSerializables() { @@ -119,21 +164,25 @@ private void setupAdapters() { ItemAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_20_R4.ItemAdapter()); PlayerAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_20_R4.player.PlayerAdapter()); AttributeAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_20_R4.AttributeAdapter()); + CommandMapAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_20_R4.command.CommandMapAdapter()); } case "1.21", "1.21.1" -> { ItemAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R1.ItemAdapter()); PlayerAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R1.player.PlayerAdapter()); AttributeAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R1.AttributeAdapter()); + CommandMapAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R1.command.CommandMapAdapter()); } case "1.21.4" -> { ItemAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R3.ItemAdapter()); PlayerAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R3.player.PlayerAdapter()); AttributeAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R3.AttributeAdapter()); + CommandMapAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R3.command.CommandMapAdapter()); } - case "1.21.6", "1.21.7", "1.21.8" -> { + case "1.21.6", "1.21.7" -> { ItemAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R5.ItemAdapter()); PlayerAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R5.player.PlayerAdapter()); AttributeAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R5.AttributeAdapter()); + CommandMapAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_R5.command.CommandMapAdapter()); } case "1.21.11" -> { ItemAdapter.setInstance(new com.wizardlybump17.wlib.adapter.v1_21_r7.ItemAdapter()); @@ -153,4 +202,32 @@ private void setupAdapters() { public static WLib getInstance() { return getPlugin(WLib.class); } + + public MethodCommandExtractor getMethodCommandExtractor() { + return methodCommandExtractor; + } + + public MethodCommandNodeFactoryRegistry getMethodCommandNodeFactoryRegistry() { + return methodCommandNodeFactoryRegistry; + } + + public WLibCommandExecutor getCommandExecutor() { + return commandExecutor; + } + + public BukkitCommandManagerListener getCommandManagerListener() { + return commandManagerListener; + } + + public CommandManager getCommandManager() { + return commandManager; + } + + public Gson getGson() { + return gson; + } + + public SaveControllersTask getSaveControllersTask() { + return saveControllersTask; + } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/BukkitCommandExecutor.java b/core/src/main/java/com/wizardlybump17/wlib/command/BukkitCommandExecutor.java deleted file mode 100644 index c5577b03..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/BukkitCommandExecutor.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.wizardlybump17.wlib.command; - -import com.wizardlybump17.wlib.command.sender.ConsoleSender; -import com.wizardlybump17.wlib.command.sender.GenericSender; -import com.wizardlybump17.wlib.command.sender.PlayerSender; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.*; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -@RequiredArgsConstructor -public class BukkitCommandExecutor implements TabExecutor, com.wizardlybump17.wlib.command.holder.CommandExecutor { - - private final CommandManager manager; - - @SneakyThrows - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - execute(getSender(sender), command.getName(), args); - return false; - } - - @Override - public void execute(com.wizardlybump17.wlib.command.CommandSender sender, String commandName, String[] args) { - String commandExecution = commandName + " " + String.join(" ", args); - manager.execute(sender, commandExecution); - } - - @Nullable - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { -// String fullCommand = command.getName() + " " + String.join(" ", args); -// List suggestions = manager.autoComplete(getSender(sender), fullCommand); -// ArrayList strings = StringUtil.copyPartialMatches(args[args.length - 1], suggestions, new ArrayList<>(suggestions.size())); -// return strings; - return null; - } - - private com.wizardlybump17.wlib.command.CommandSender getSender(CommandSender original) { - if (original instanceof Player player) - return new PlayerSender(player); - if (original instanceof ConsoleCommandSender consoleSender) - return new ConsoleSender(consoleSender); - if (original instanceof BlockCommandSender blockSender) - return new com.wizardlybump17.wlib.command.sender.BlockCommandSender(blockSender); - return new GenericSender(original); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/WLibCommandExecutor.java b/core/src/main/java/com/wizardlybump17/wlib/command/WLibCommandExecutor.java new file mode 100644 index 00000000..1cd1f8fa --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/WLibCommandExecutor.java @@ -0,0 +1,74 @@ +package com.wizardlybump17.wlib.command; + +import com.wizardlybump17.wlib.command.exception.CommandExecutionException; +import com.wizardlybump17.wlib.command.exception.SuggesterException; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.sender.BukkitCommandSender; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class WLibCommandExecutor implements CommandExecutor, TabCompleter { + + private final @NotNull CommandManager commandManager; + private final @NotNull Logger logger; + + public WLibCommandExecutor(@NotNull CommandManager commandManager, @NotNull Logger logger) { + this.commandManager = commandManager; + this.logger = logger; + } + + public @NotNull CommandManager getCommandManager() { + return commandManager; + } + + public @NotNull Logger getLogger() { + return logger; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + com.wizardlybump17.wlib.command.sender.CommandSender wlibSender = BukkitCommandSender.from(sender); + + String wlibArgs = command.getName() + " " + String.join(" ", args); + + try { + commandManager.execute(wlibSender, wlibArgs); + } catch (CommandExecutionException e) { + sender.sendMessage("§cAn internal error occurred while executing this command."); + getLogger().log(Level.SEVERE, "Error while " + sender.getName() + " tried to execute \"" + wlibArgs + "\"", e); + return false; + } + + return false; + } + + @Override + public @NotNull List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + com.wizardlybump17.wlib.command.sender.CommandSender wlibSender = BukkitCommandSender.from(sender); + + String[] wlibArgs = new String[args.length + 1]; + wlibArgs[0] = command.getName(); + System.arraycopy(args, 0, wlibArgs, 1, args.length); + + String current = args.length == 1 ? args[0] : args[args.length - 1]; + String currentLowerCase = current.toLowerCase(); + + try { + return commandManager.getSuggestions(wlibSender, wlibArgs) + .stream() + .filter(suggestion -> suggestion.toLowerCase().startsWith(currentLowerCase)) + .toList(); + } catch (SuggesterException e) { + logger.log(Level.SEVERE, "Error while getting suggestions for " + sender + ": " + Arrays.toString(wlibArgs), e); + return List.of(); + } + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/bukkit/BukkitCommand.java b/core/src/main/java/com/wizardlybump17/wlib/command/bukkit/BukkitCommand.java new file mode 100644 index 00000000..5bfa6c5d --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/bukkit/BukkitCommand.java @@ -0,0 +1,24 @@ +package com.wizardlybump17.wlib.command.bukkit; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.node.LiteralCommandNode; +import org.jetbrains.annotations.NotNull; + +public class BukkitCommand extends Command { + + private final @NotNull InternalBukkitCommand internalCommand; + + public BukkitCommand(@NotNull LiteralCommandNode root) { + super(root); + internalCommand = new InternalBukkitCommand(this); + } + + public @NotNull InternalBukkitCommand getInternalCommand() { + return internalCommand; + } + + @Override + public @NotNull BukkitCommand merge(@NotNull Command other) { + return new BukkitCommand(getRoot().merge(other.getRoot())); + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/bukkit/InternalBukkitCommand.java b/core/src/main/java/com/wizardlybump17/wlib/command/bukkit/InternalBukkitCommand.java new file mode 100644 index 00000000..c11ba911 --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/bukkit/InternalBukkitCommand.java @@ -0,0 +1,51 @@ +package com.wizardlybump17.wlib.command.bukkit; + +import com.wizardlybump17.wlib.command.WLibCommandExecutor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +@ApiStatus.Internal +public class InternalBukkitCommand extends Command { + + private final @NotNull BukkitCommand command; + private WLibCommandExecutor executor; + + public InternalBukkitCommand(@NotNull BukkitCommand command) { + super(command.getName()); + this.command = command; + } + + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + if (executor == null) + return false; + + executor.onCommand(sender, this, commandLabel, args); + return false; + } + + @Override + public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args, @Nullable Location location) throws IllegalArgumentException { + if (executor == null) + return List.of(); + return executor.onTabComplete(sender, this, alias, args); + } + + public @NotNull BukkitCommand getCommand() { + return command; + } + + public WLibCommandExecutor getExecutor() { + return executor; + } + + public void setExecutor(WLibCommandExecutor executor) { + this.executor = executor; + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/completer/EntityTypeArgumentCompleter.java b/core/src/main/java/com/wizardlybump17/wlib/command/completer/EntityTypeArgumentCompleter.java deleted file mode 100644 index de7bf8f4..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/completer/EntityTypeArgumentCompleter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.wizardlybump17.wlib.command.completer; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; -import org.bukkit.entity.EntityType; - -import java.util.Arrays; -import java.util.List; - -public class EntityTypeArgumentCompleter implements ArgumentCompleter { - - public static final @NonNull List ENTITIES = Arrays.stream(EntityType.values()) - .map(Enum::name) - .toList(); - - @Override - public @NonNull List complete(@NonNull CommandSender sender, @NonNull String @NonNull [] args) { - return ENTITIES; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/completer/MaterialArgumentCompleter.java b/core/src/main/java/com/wizardlybump17/wlib/command/completer/MaterialArgumentCompleter.java deleted file mode 100644 index 6ded4874..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/completer/MaterialArgumentCompleter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.wizardlybump17.wlib.command.completer; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; -import org.bukkit.Material; - -import java.util.Arrays; -import java.util.List; - -public class MaterialArgumentCompleter implements ArgumentCompleter { - - public static final List MATERIALS = Arrays.stream(Material.values()) - .map(Enum::name) - .toList(); - - @Override - public @NonNull List complete(@NonNull CommandSender sender, @NonNull String @NonNull [] args) { - return MATERIALS; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/completer/PlayerArgumentCompleter.java b/core/src/main/java/com/wizardlybump17/wlib/command/completer/PlayerArgumentCompleter.java deleted file mode 100644 index 2d963104..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/completer/PlayerArgumentCompleter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.wizardlybump17.wlib.command.completer; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.NonNull; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.List; - -public class PlayerArgumentCompleter implements ArgumentCompleter { - - @Override - public @NonNull List complete(@NonNull CommandSender sender, @NonNull String @NonNull [] args) { - return Bukkit.getOnlinePlayers().stream() - .map(Player::getName) - .toList(); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/OfflinePlayerMethodCommandNodeFactory.java b/core/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/OfflinePlayerMethodCommandNodeFactory.java new file mode 100644 index 00000000..90a0df9b --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/OfflinePlayerMethodCommandNodeFactory.java @@ -0,0 +1,41 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.annotation.NonNullInput; +import com.wizardlybump17.wlib.command.input.AllowedOfflinePlayerInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.OfflinePlayerCommandNode; +import com.wizardlybump17.wlib.command.suggestion.OfflinePlayerSuggester; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; + +public class OfflinePlayerMethodCommandNodeFactory extends MethodCommandNodeFactory { + + @Override + public @NotNull CommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + return new OfflinePlayerCommandNode( + name, + root == null ? List.of() : List.of(root), + parameter.isAnnotationPresent(NonNullInput.class) ? AllowedOfflinePlayerInputs.anyNotNull() : AllowedOfflinePlayerInputs.anyNullable(), + OfflinePlayerSuggester.online(), + null, + null + ); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] {OfflinePlayer.class, Player.class}; + } + + @Override + public boolean isStrict() { + return false; + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/PlayerMethodCommandNodeFactory.java b/core/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/PlayerMethodCommandNodeFactory.java new file mode 100644 index 00000000..7c9b3893 --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/PlayerMethodCommandNodeFactory.java @@ -0,0 +1,40 @@ +package com.wizardlybump17.wlib.command.extractor.method.factory; + +import com.wizardlybump17.wlib.command.annotation.Command; +import com.wizardlybump17.wlib.command.annotation.NonNullInput; +import com.wizardlybump17.wlib.command.input.AllowedPlayerInputs; +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.node.PlayerCommandNode; +import com.wizardlybump17.wlib.command.suggestion.PlayerSuggester; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; + +public class PlayerMethodCommandNodeFactory extends MethodCommandNodeFactory { + + @Override + public @NotNull CommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode root) { + return new PlayerCommandNode( + name, + root == null ? List.of() : List.of(root), + parameter.isAnnotationPresent(NonNullInput.class) ? AllowedPlayerInputs.anyNotNull() : AllowedPlayerInputs.anyNullable(), + PlayerSuggester.online(), + null, + null + ); + } + + @Override + public @NotNull Class @NotNull [] getSupportedTypes() { + return new Class[] {Player.class}; + } + + @Override + public boolean isStrict() { + return false; + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/holder/BukkitCommand.java b/core/src/main/java/com/wizardlybump17/wlib/command/holder/BukkitCommand.java deleted file mode 100644 index 5b115f0b..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/holder/BukkitCommand.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.wizardlybump17.wlib.command.holder; - -import com.wizardlybump17.wlib.command.BukkitCommandExecutor; -import com.wizardlybump17.wlib.command.CommandManager; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.bukkit.command.PluginCommand; - -@RequiredArgsConstructor -public class BukkitCommand implements Command { - - private final PluginCommand command; - @Getter - private CommandExecutor executor; - - @Override - public void setExecutor(CommandExecutor executor) { - if (executor instanceof org.bukkit.command.CommandExecutor bukkitExecutor) - command.setExecutor(bukkitExecutor); - this.executor = executor; - } - - @Override - public CommandExecutor getDefaultExecutor(CommandManager manager, String name) { - return new BukkitCommandExecutor(manager); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/holder/BukkitCommandHolder.java b/core/src/main/java/com/wizardlybump17/wlib/command/holder/BukkitCommandHolder.java deleted file mode 100644 index 0c716fb4..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/holder/BukkitCommandHolder.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.wizardlybump17.wlib.command.holder; - -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.bukkit.plugin.java.JavaPlugin; - -import java.util.logging.Logger; - -@RequiredArgsConstructor -@Getter -public class BukkitCommandHolder implements CommandHolder { - - private final JavaPlugin handle; - - public static BukkitCommandHolder of(JavaPlugin plugin) { - return new BukkitCommandHolder(plugin); - } - - @Override - public Command getCommand(String name) { - return new BukkitCommand(handle.getCommand(name)); - } - - @Override - public @NonNull Logger getLogger() { - return handle.getLogger(); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/input/AllowedOfflinePlayerInputs.java b/core/src/main/java/com/wizardlybump17/wlib/command/input/AllowedOfflinePlayerInputs.java new file mode 100644 index 00000000..f5c3a01d --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/input/AllowedOfflinePlayerInputs.java @@ -0,0 +1,136 @@ +package com.wizardlybump17.wlib.command.input; + +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public interface AllowedOfflinePlayerInputs extends AllowedInputs { + + static @NotNull AllowedOfflinePlayerInputs.Value value(@NotNull OfflinePlayer value) { + return new AllowedOfflinePlayerInputs.Value(value); + } + + static @NotNull AllowedOfflinePlayerInputs.Values values(@NotNull List values) { + return new AllowedOfflinePlayerInputs.Values(List.copyOf(values)); + } + + static @NotNull AllowedOfflinePlayerInputs.Values values(@NotNull OfflinePlayer @NotNull ... values) { + return new AllowedOfflinePlayerInputs.Values(List.of(values)); + } + + static @NotNull AllowedOfflinePlayerInputs.Any anyNullable() { + return AllowedOfflinePlayerInputs.Any.NULLABLE; + } + + static @NotNull AllowedOfflinePlayerInputs.Any anyNotNull() { + return AllowedOfflinePlayerInputs.Any.NOT_NULL; + } + + final class Value implements AllowedOfflinePlayerInputs, SingleValueInput { + + private final @NotNull OfflinePlayer value; + + Value(@NotNull OfflinePlayer value) { + this.value = value; + } + + @Override + public @NotNull OfflinePlayer value() { + return value; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Value value1 = (Value) object; + return Objects.equals(value, value1.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public String toString() { + return "AllowedOfflinePlayerInputs$Value{" + + "value=" + value + + '}'; + } + } + + final class Values implements AllowedOfflinePlayerInputs, AllowedListInputs { + + private final @NotNull List values; + + private Values(@NotNull List values) { + this.values = values; + } + + @Override + public @NotNull List allowedValues() { + return values; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedOfflinePlayerInputs.Values values1 = (AllowedOfflinePlayerInputs.Values) object; + return Objects.equals(values, values1.values); + } + + @Override + public int hashCode() { + return Objects.hashCode(values); + } + + @Override + public String toString() { + return "AllowedOfflinePlayerInputs$Values{" + + "values=" + values + + '}'; + } + } + + final class Any implements AllowedOfflinePlayerInputs { + + private static final @NotNull AllowedOfflinePlayerInputs.Any NULLABLE = new AllowedOfflinePlayerInputs.Any(true); + private static final @NotNull AllowedOfflinePlayerInputs.Any NOT_NULL = new AllowedOfflinePlayerInputs.Any(false); + + private final boolean nullable; + + private Any(boolean nullable) { + this.nullable = nullable; + } + + @Override + public boolean isAllowed(@Nullable OfflinePlayer input) { + return nullable || input != null; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedOfflinePlayerInputs.Any any = (AllowedOfflinePlayerInputs.Any) object; + return nullable == any.nullable; + } + + @Override + public int hashCode() { + return Objects.hashCode(nullable); + } + + @Override + public String toString() { + return "AllowedOfflinePlayerInputs$Any{" + + "nullable=" + nullable + + '}'; + } + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/input/AllowedPlayerInputs.java b/core/src/main/java/com/wizardlybump17/wlib/command/input/AllowedPlayerInputs.java new file mode 100644 index 00000000..14430ccb --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/input/AllowedPlayerInputs.java @@ -0,0 +1,136 @@ +package com.wizardlybump17.wlib.command.input; + +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public interface AllowedPlayerInputs extends AllowedInputs { + + static @NotNull AllowedPlayerInputs.Value value(@NotNull Player value) { + return new AllowedPlayerInputs.Value(value); + } + + static @NotNull AllowedPlayerInputs.Values values(@NotNull List values) { + return new AllowedPlayerInputs.Values(List.copyOf(values)); + } + + static @NotNull AllowedPlayerInputs.Values values(@NotNull Player @NotNull ... values) { + return new AllowedPlayerInputs.Values(List.of(values)); + } + + static @NotNull AllowedPlayerInputs.Any anyNullable() { + return AllowedPlayerInputs.Any.NULLABLE; + } + + static @NotNull AllowedPlayerInputs.Any anyNotNull() { + return AllowedPlayerInputs.Any.NOT_NULL; + } + + final class Value implements AllowedPlayerInputs, SingleValueInput { + + private final @NotNull Player value; + + Value(@NotNull Player value) { + this.value = value; + } + + @Override + public @NotNull Player value() { + return value; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + Value value1 = (Value) object; + return Objects.equals(value, value1.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public String toString() { + return "AllowedPlayerInputs$Value{" + + "value=" + value + + '}'; + } + } + + final class Values implements AllowedPlayerInputs, AllowedListInputs { + + private final @NotNull List values; + + private Values(@NotNull List values) { + this.values = values; + } + + @Override + public @NotNull List allowedValues() { + return values; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedPlayerInputs.Values values1 = (AllowedPlayerInputs.Values) object; + return Objects.equals(values, values1.values); + } + + @Override + public int hashCode() { + return Objects.hashCode(values); + } + + @Override + public String toString() { + return "AllowedPlayerInputs$Values{" + + "values=" + values + + '}'; + } + } + + final class Any implements AllowedPlayerInputs { + + private static final @NotNull AllowedPlayerInputs.Any NULLABLE = new AllowedPlayerInputs.Any(true); + private static final @NotNull AllowedPlayerInputs.Any NOT_NULL = new AllowedPlayerInputs.Any(false); + + private final boolean nullable; + + private Any(boolean nullable) { + this.nullable = nullable; + } + + @Override + public boolean isAllowed(@Nullable Player input) { + return nullable || input != null; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) + return false; + AllowedPlayerInputs.Any any = (AllowedPlayerInputs.Any) object; + return nullable == any.nullable; + } + + @Override + public int hashCode() { + return Objects.hashCode(nullable); + } + + @Override + public String toString() { + return "AllowedPlayerInputs$Any{" + + "nullable=" + nullable + + '}'; + } + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/listener/BukkitCommandManagerListener.java b/core/src/main/java/com/wizardlybump17/wlib/command/listener/BukkitCommandManagerListener.java new file mode 100644 index 00000000..0383696a --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/listener/BukkitCommandManagerListener.java @@ -0,0 +1,83 @@ +package com.wizardlybump17.wlib.command.listener; + +import com.wizardlybump17.wlib.command.Command; +import com.wizardlybump17.wlib.command.WLibCommandExecutor; +import com.wizardlybump17.wlib.command.bukkit.BukkitCommand; +import com.wizardlybump17.wlib.command.bukkit.InternalBukkitCommand; +import com.wizardlybump17.wlib.command.manager.CommandManager; +import com.wizardlybump17.wlib.command.manager.listener.CommandManagerListener; +import org.bukkit.Bukkit; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.logging.Level; + +public class BukkitCommandManagerListener implements CommandManagerListener { + + private final @NotNull WLibCommandExecutor commandExecutor; + + public BukkitCommandManagerListener(@NotNull WLibCommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + } + + @Override + public void onRegister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + if (!(holder instanceof JavaPlugin plugin)) + return; + + if (command instanceof BukkitCommand bukkitCommand) { + InternalBukkitCommand internalCommand = bukkitCommand.getInternalCommand(); + internalCommand.setExecutor(commandExecutor); + + Bukkit.getCommandMap().register(command.getName(), identifier, internalCommand); + } else { + PluginCommand pluginCommand = plugin.getCommand(command.getName()); + if (pluginCommand == null) { + plugin.getLogger().log(Level.WARNING, "Command not found on plugin.yml while trying to register it to WLib: " + command.getName()); + return; + } + + pluginCommand.setExecutor(commandExecutor); + } + } + + @Override + public void onPreClear(@NotNull CommandManager manager) { + Map holdersByFullName = manager.getHoldersByFullName(); + manager.getCommandsByFullName().forEach((name, command) -> { + Object holder = holdersByFullName.get(name); + onUnregister(name.substring(0, name.indexOf(CommandManager.SEPARATOR)), command, holder, manager); + }); + } + + @Override + public void onPostClear(@NotNull CommandManager manager) { + } + + @Override + public void onUnregister(@NotNull String identifier, @NotNull Command command, @Nullable Object holder, @NotNull CommandManager manager) { + if (!(holder instanceof JavaPlugin plugin)) + return; + + if (command instanceof BukkitCommand bukkitCommand) { + bukkitCommand.getInternalCommand().setExecutor(null); + + Map knownCommands = Bukkit.getCommandMap().getKnownCommands(); + knownCommands.remove(identifier + CommandManager.SEPARATOR + command.getName()); + knownCommands.remove(command.getName()); + } else { + PluginCommand pluginCommand = plugin.getCommand(command.getName()); + if (pluginCommand == null) + return; + + pluginCommand.setExecutor(null); + } + } + + public @NotNull WLibCommandExecutor getCommandExecutor() { + return commandExecutor; + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/node/OfflinePlayerCommandNode.java b/core/src/main/java/com/wizardlybump17/wlib/command/node/OfflinePlayerCommandNode.java new file mode 100644 index 00000000..cd6e91bd --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/node/OfflinePlayerCommandNode.java @@ -0,0 +1,56 @@ +package com.wizardlybump17.wlib.command.node; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.UUID; + +public class OfflinePlayerCommandNode extends CommandNode { + + public OfflinePlayerCommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @Nullable OfflinePlayer parse(@Nullable String input) throws InputParsingException { + if (input == null) + throw new InputParsingException("The input cannot be null"); + + OfflinePlayer player = Bukkit.getOfflinePlayerIfCached(input); + if (player != null) + return player; + + if (input.length() == 36) { + try { + return Bukkit.getOfflinePlayer(UUID.fromString(input)); + } catch (IllegalArgumentException e) { + throw new InputParsingException("Could not parse as UUID to get an OfflinePlayer: " + input, e); + } + } + + return null; + } + + @Override + public @NotNull OfflinePlayerCommandNode withChildren(@NotNull List> children) { + return new OfflinePlayerCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull OfflinePlayerCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new OfflinePlayerCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull OfflinePlayerCommandNode withPermission(@Nullable String permission) { + return new OfflinePlayerCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/node/PlayerCommandNode.java b/core/src/main/java/com/wizardlybump17/wlib/command/node/PlayerCommandNode.java new file mode 100644 index 00000000..45d56cad --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/node/PlayerCommandNode.java @@ -0,0 +1,56 @@ +package com.wizardlybump17.wlib.command.node; + +import com.wizardlybump17.wlib.command.exception.InputParsingException; +import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor; +import com.wizardlybump17.wlib.command.input.AllowedInputs; +import com.wizardlybump17.wlib.command.suggestion.Suggester; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.UUID; + +public class PlayerCommandNode extends CommandNode { + + public PlayerCommandNode(@NotNull String name, @NotNull @Unmodifiable List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor executor, @Nullable String permission) { + super(name, children, allowedInputs, suggester, executor, permission); + } + + @Override + public @Nullable Player parse(@Nullable String input) throws InputParsingException { + if (input == null) + throw new InputParsingException("The input cannot be null"); + + Player player = Bukkit.getPlayerExact(input); + if (player != null) + return player; + + if (input.length() == 36) { + try { + return Bukkit.getPlayer(UUID.fromString(input)); + } catch (IllegalArgumentException e) { + throw new InputParsingException("Could not parse as UUID to get an Player: " + input, e); + } + } + + return null; + } + + @Override + public @NotNull PlayerCommandNode withChildren(@NotNull List> children) { + return new PlayerCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission()); + } + + @Override + public @NotNull PlayerCommandNode withExecutor(@Nullable CommandNodeExecutor executor) { + return new PlayerCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission()); + } + + @Override + public @NotNull PlayerCommandNode withPermission(@Nullable String permission) { + return new PlayerCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission); + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/BlockDataArgsReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/BlockDataArgsReader.java deleted file mode 100644 index dca102c3..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/BlockDataArgsReader.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException; -import lombok.NonNull; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.block.data.BlockData; - -import java.util.Arrays; -import java.util.List; - -public class BlockDataArgsReader extends ArgsReader { - - public static final List SUGGESTIONS = Arrays.stream(Material.values()) - .filter(Material::isBlock) - .map(material -> material.createBlockData().getAsString(true)) - .toList(); - - @Override - public Class getType() { - return BlockData.class; - } - - @Override - public BlockData read(String string) throws ArgsReaderException { - try { - return Bukkit.createBlockData(string); - } catch (IllegalArgumentException e) { - throw new ArgsReaderException(e); - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/EnchantmentReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/EnchantmentReader.java deleted file mode 100644 index dcd0d1ad..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/EnchantmentReader.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import org.bukkit.NamespacedKey; -import org.bukkit.Registry; -import org.bukkit.enchantments.Enchantment; -import org.jetbrains.annotations.Nullable; - -public class EnchantmentReader extends ArgsReader { - - @Override - public @Nullable Class getType() { - return Enchantment.class; - } - - @Override - public Enchantment read(String string) { - NamespacedKey key = NamespacedKey.fromString(string); - return key == null ? null : Registry.ENCHANTMENT.get(key); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/EntityTypeArgsReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/EntityTypeArgsReader.java deleted file mode 100644 index 4ca9a7ce..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/EntityTypeArgsReader.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import lombok.NonNull; -import org.bukkit.entity.EntityType; - -import java.util.Arrays; -import java.util.List; - -public class EntityTypeArgsReader extends ArgsReader { - - public static final List SUGGESTIONS = Arrays.stream(EntityType.values()) - .filter(EntityType::isAlive) - .map(EntityType::name) - .toList(); - - @Override - public Class getType() { - return EntityType.class; - } - - @Override - public EntityType read(String string) { - try { - return EntityType.valueOf(string.toUpperCase()); - } catch (IllegalArgumentException e) { - return null; - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/MapJsonArgsReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/MapJsonArgsReader.java deleted file mode 100644 index a456c1c8..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/MapJsonArgsReader.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException; -import lombok.NonNull; - -import java.util.List; -import java.util.Map; - -public class MapJsonArgsReader extends ArgsReader> { - - public static final List SUGGESTIONS = List.of("{key=value,key2=value2}"); - - private final Gson gson = new Gson(); - - @Override - public Class> getType() { - return null; - } - - @SuppressWarnings("unchecked") - @Override - public Map read(String string) throws ArgsReaderException { - try { - return gson.fromJson(string, Map.class); - } catch (JsonSyntaxException exception) { - throw new ArgsReaderException("expected a valid JSON", exception); - } - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/MaterialReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/MaterialReader.java deleted file mode 100644 index 8251dd9c..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/MaterialReader.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import lombok.NonNull; -import org.bukkit.Material; - -import java.util.Arrays; -import java.util.List; - -public class MaterialReader extends ArgsReader { - - public static final List SUGGESTION = Arrays.stream(Material.values()).map(Material::name).toList(); - - @Override - public Class getType() { - return Material.class; - } - - @Override - public Material read(String string) { - return Material.matchMaterial(string); - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTION; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/NamespacedKeyReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/NamespacedKeyReader.java deleted file mode 100644 index b9e78278..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/NamespacedKeyReader.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import lombok.NonNull; -import org.bukkit.NamespacedKey; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class NamespacedKeyReader extends ArgsReader { - - private static final @NotNull List SUGGESTIONS = List.of("key", "namespace:key"); - - @Override - public @NotNull Class getType() { - return NamespacedKey.class; - } - - @Override - public NamespacedKey read(String string) { - return NamespacedKey.fromString(string.toLowerCase()); - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - return SUGGESTIONS; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/OfflinePlayerReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/OfflinePlayerReader.java deleted file mode 100644 index 5da69881..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/OfflinePlayerReader.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import lombok.NonNull; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -public class OfflinePlayerReader extends ArgsReader { - - @Override - public Class getType() { - return OfflinePlayer.class; - } - - @Override - public OfflinePlayer read(String string) { - Player player = Bukkit.getPlayerExact(string); - if (player != null) - return player; - - try { - UUID uuid = UUID.fromString(string); - return Bukkit.getOfflinePlayer(uuid); - } catch (IllegalArgumentException ignored) { - for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) - if (string.equalsIgnoreCase(offlinePlayer.getName())) - return offlinePlayer; - } - - return null; - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - OfflinePlayer[] players = Bukkit.getOfflinePlayers(); - List suggestions = new ArrayList<>(players.length); - for (OfflinePlayer player : players) - suggestions.add(player.getName()); - return suggestions; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/PlayerReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/PlayerReader.java deleted file mode 100644 index ddb38aa7..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/PlayerReader.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.CommandSender; -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import lombok.NonNull; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class PlayerReader extends ArgsReader { - - @Override - public Class getType() { - return Player.class; - } - - @Override - public Player read(String string) { - return Bukkit.getPlayerExact(string); - } - - @Override - public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender sender, @NonNull String current) { - Collection players = Bukkit.getOnlinePlayers(); - List suggestions = new ArrayList<>(players.size()); - for (Player player : players) - suggestions.add(player.getName()); - return suggestions; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/reader/PotionEffectTypeReader.java b/core/src/main/java/com/wizardlybump17/wlib/command/reader/PotionEffectTypeReader.java deleted file mode 100644 index f6454523..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/reader/PotionEffectTypeReader.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.wizardlybump17.wlib.command.reader; - -import com.wizardlybump17.wlib.command.args.reader.ArgsReader; -import org.bukkit.NamespacedKey; -import org.bukkit.Registry; -import org.bukkit.potion.PotionEffectType; -import org.jetbrains.annotations.Nullable; - -public class PotionEffectTypeReader extends ArgsReader { - - @Override - public @Nullable Class getType() { - return PotionEffectType.class; - } - - @Override - public PotionEffectType read(String string) { - NamespacedKey key = NamespacedKey.fromString(string); - return key == null ? null : Registry.POTION_EFFECT_TYPE.get(key); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/sender/AbstractSender.java b/core/src/main/java/com/wizardlybump17/wlib/command/sender/AbstractSender.java deleted file mode 100644 index 98af7103..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/sender/AbstractSender.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.wizardlybump17.wlib.command.sender; - -import com.wizardlybump17.wlib.command.CommandSender; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Getter -public abstract class AbstractSender implements CommandSender { - - private final S handle; - - @Override - public void sendMessage(String message) { - handle.sendMessage(message); - } - - @Override - public void sendMessage(String... message) { - handle.sendMessage(String.join("\n", message)); - } - - @Override - public String getName() { - return handle.getName(); - } - - @Override - public boolean hasPermission(String permission) { - return handle.hasPermission(permission); - } - - @Override - @NonNull - public GenericSender toGeneric() { - return new GenericSender(handle); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/sender/BlockCommandSender.java b/core/src/main/java/com/wizardlybump17/wlib/command/sender/BlockCommandSender.java deleted file mode 100644 index 2f3b8455..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/sender/BlockCommandSender.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.wizardlybump17.wlib.command.sender; - -public class BlockCommandSender extends AbstractSender { - - public BlockCommandSender(org.bukkit.command.BlockCommandSender handle) { - super(handle); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/sender/BukkitCommandSender.java b/core/src/main/java/com/wizardlybump17/wlib/command/sender/BukkitCommandSender.java new file mode 100644 index 00000000..83730ddf --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/sender/BukkitCommandSender.java @@ -0,0 +1,128 @@ +package com.wizardlybump17.wlib.command.sender; + +import com.wizardlybump17.wlib.util.bukkit.collector.ComponentCollector; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.bukkit.Bukkit; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class BukkitCommandSender implements CommandSender { + + public static final @NotNull BukkitCommandSender CONSOLE = new BukkitCommandSender(Bukkit.getConsoleSender()); + private static final @NotNull Map SENDERS_BY_ID = new ConcurrentHashMap<>(); + + private final @NotNull org.bukkit.command.CommandSender handle; + + public BukkitCommandSender(@NotNull org.bukkit.command.CommandSender handle) { + this.handle = handle; + } + + @Override + public org.bukkit.command.CommandSender getHandle() { + return handle; + } + + @Override + public void sendMessage(String message) { + handle.sendMessage(message); + } + + @Override + public void sendMessage(String... message) { + handle.sendMessage(String.join("\n", message)); + } + + @Override + public void sendMessage(@Nullable Object message) { + switch (message) { + case null -> handle.sendMessage("null"); + case ComponentLike component -> handle.sendMessage(component); + case String string -> handle.sendMessage(string); + default -> handle.sendMessage(String.valueOf(message)); + } + } + + @Override + public void sendMessage(@Nullable Object @Nullable ... messages) { + if (messages == null) { + sendMessage((Object) null); + return; + } + + List components = new ArrayList<>(messages.length); + for (Object message : messages) { + switch (message) { + case null -> components.add(Component.text("null")); + case ComponentLike componentLike -> components.add(componentLike.asComponent()); + case String string -> components.add(Component.text(string)); + default -> components.add(Component.text(String.valueOf(message))); + } + } + + handle.sendMessage(components.stream().collect(ComponentCollector.NEW_LINE)); + } + + @Override + public String getName() { + return handle.getName(); + } + + @Override + public boolean hasPermission(String permission) { + return handle.hasPermission(permission); + } + + public @NotNull BlockCommandSender asBlockCommand() { + return (BlockCommandSender) handle; + } + + public @NotNull ConsoleCommandSender asConsole() { + return (ConsoleCommandSender) handle; + } + + public @NotNull Player asPlayer() { + return (Player) handle; + } + + @Override + public boolean hasId(@NotNull UUID id) { + return handle instanceof Entity entity && entity.getUniqueId().equals(id); + } + + @Override + public @NotNull UUID getId() throws IllegalStateException { + if (handle instanceof Entity entity) + return entity.getUniqueId(); + throw new IllegalStateException(handle + " does not have an ID"); + } + + @ApiStatus.Internal + public static void clearCache() { + SENDERS_BY_ID.clear(); + } + + public static @NotNull BukkitCommandSender from(@NotNull org.bukkit.command.CommandSender sender) { + return switch (sender) { + case ConsoleCommandSender ignored -> BukkitCommandSender.CONSOLE; + case Entity entity -> SENDERS_BY_ID.computeIfAbsent(entity.getUniqueId(), $ -> new BukkitCommandSender(sender)); + default -> new BukkitCommandSender(sender); + }; + } + + @ApiStatus.Internal + public static void removeFromCache(@NotNull UUID id) { + SENDERS_BY_ID.remove(id); + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/sender/ConsoleSender.java b/core/src/main/java/com/wizardlybump17/wlib/command/sender/ConsoleSender.java deleted file mode 100644 index 46e55e01..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/sender/ConsoleSender.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.wizardlybump17.wlib.command.sender; - -import org.bukkit.command.ConsoleCommandSender; - -public class ConsoleSender extends AbstractSender { - - public ConsoleSender(ConsoleCommandSender handle) { - super(handle); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/sender/GenericSender.java b/core/src/main/java/com/wizardlybump17/wlib/command/sender/GenericSender.java deleted file mode 100644 index d223526d..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/sender/GenericSender.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.wizardlybump17.wlib.command.sender; - -import lombok.NonNull; -import org.bukkit.command.CommandSender; - -public class GenericSender extends AbstractSender { - - public GenericSender(CommandSender handle) { - super(handle); - } - - @Override - @NonNull - public GenericSender toGeneric() { - return this; - } - - public static boolean isGeneric() { - return true; - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/sender/PlayerSender.java b/core/src/main/java/com/wizardlybump17/wlib/command/sender/PlayerSender.java deleted file mode 100644 index ac2f08eb..00000000 --- a/core/src/main/java/com/wizardlybump17/wlib/command/sender/PlayerSender.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.wizardlybump17.wlib.command.sender; - -import org.bukkit.entity.Player; - -public class PlayerSender extends AbstractSender { - - public PlayerSender(Player handle) { - super(handle); - } -} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/suggestion/OfflinePlayerSuggester.java b/core/src/main/java/com/wizardlybump17/wlib/command/suggestion/OfflinePlayerSuggester.java new file mode 100644 index 00000000..5fe61e6a --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/suggestion/OfflinePlayerSuggester.java @@ -0,0 +1,55 @@ +package com.wizardlybump17.wlib.command.suggestion; + +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface OfflinePlayerSuggester extends Suggester { + + @Override + default @NotNull String getStringRepresentation(@NotNull OfflinePlayer value) { + String name = value.getName(); + return name == null ? value.getUniqueId().toString() : name; + } + + static @NotNull Online online() { + return Online.INSTANCE; + } + + static @NotNull Cached cached() { + return Cached.INSTANCE; + } + + final class Online implements OfflinePlayerSuggester { + + private static final @NotNull Online INSTANCE = new Online(); + + private Online() { + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return Bukkit.getOnlinePlayers() + .stream() + .map(OfflinePlayer.class::cast) + .toList(); + } + } + + final class Cached implements OfflinePlayerSuggester { + + private static final @NotNull Cached INSTANCE = new Cached(); + + private Cached() { + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return List.of(Bukkit.getOfflinePlayers()); + } + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/command/suggestion/PlayerSuggester.java b/core/src/main/java/com/wizardlybump17/wlib/command/suggestion/PlayerSuggester.java new file mode 100644 index 00000000..db9c608c --- /dev/null +++ b/core/src/main/java/com/wizardlybump17/wlib/command/suggestion/PlayerSuggester.java @@ -0,0 +1,40 @@ +package com.wizardlybump17.wlib.command.suggestion; + +import com.wizardlybump17.wlib.command.node.CommandNode; +import com.wizardlybump17.wlib.command.sender.CommandSender; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public interface PlayerSuggester extends Suggester { + + @Override + default @NotNull String getStringRepresentation(@NotNull Player value) { + return value.getName(); + } + + static @NotNull Online online() { + return Online.INSTANCE; + } + + final class Online implements PlayerSuggester { + + private static final @NotNull Online INSTANCE = new Online(); + + private Online() { + } + + @Override + public @NotNull List getSuggestions(@NotNull CommandSender sender, @NotNull List input, @NotNull String current, @NotNull CommandNode currentNode) { + return new ArrayList<>(Bukkit.getOnlinePlayers()); + } + + @Override + public String toString() { + return "PlayerSuggester$Online{}"; + } + } +} diff --git a/core/src/main/java/com/wizardlybump17/wlib/inventory/paginated/PaginatedInventoryBuilder.java b/core/src/main/java/com/wizardlybump17/wlib/inventory/paginated/PaginatedInventoryBuilder.java index 709a9c7d..e199dfe0 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/inventory/paginated/PaginatedInventoryBuilder.java +++ b/core/src/main/java/com/wizardlybump17/wlib/inventory/paginated/PaginatedInventoryBuilder.java @@ -6,6 +6,7 @@ import com.wizardlybump17.wlib.inventory.item.InventoryNavigator; import com.wizardlybump17.wlib.inventory.item.ItemButton; import com.wizardlybump17.wlib.inventory.listener.InventoryListener; +import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.util.MapUtils; import com.wizardlybump17.wlib.util.ObjectUtil; import lombok.AccessLevel; @@ -22,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.Function; import java.util.function.Supplier; import java.util.function.UnaryOperator; @@ -105,7 +107,7 @@ public PaginatedInventoryBuilder shapeReplacements(@NonNull Map itemSupplier) { + for (ItemButton button : shapeReplacements.values()) + if (Objects.equals(button.getCustomData().get(key), value)) + button.setItem(itemSupplier); + return this; + } + + public @NotNull PaginatedInventoryBuilder setReplacementItemStackByCustomData(@NotNull Object key, @Nullable Object value, @NotNull ItemStack item) { + Supplier itemSupplier = () -> item; + for (ItemButton button : shapeReplacements.values()) + if (Objects.equals(button.getCustomData().get(key), value)) + button.setItem(itemSupplier); + return this; + } + + public @NotNull PaginatedInventoryBuilder setReplacementItemStackByCustomData(@NotNull Object key, @Nullable Object value, @NotNull Function replacer) { + for (ItemButton button : shapeReplacements.values()) { + ItemStack originalItem = button.getItem().get(); + if (Objects.equals(button.getCustomData().get(key), value)) { + button.setItem(() -> replacer.apply(originalItem)); + } + } + return this; + } + + //ItemBuilder + + public @NotNull PaginatedInventoryBuilder setReplacementItemByCustomData(@NotNull Object key, @Nullable Object value, @NotNull Supplier itemSupplier) { + Supplier itemStackSupplier = () -> itemSupplier.get().build(); + for (ItemButton button : shapeReplacements.values()) + if (Objects.equals(button.getCustomData().get(key), value)) + button.setItem(itemStackSupplier); + return this; + } + + public @NotNull PaginatedInventoryBuilder setReplacementItemByCustomData(@NotNull Object key, @Nullable Object value, @NotNull ItemBuilder item) { + Supplier itemSupplier = item::build; + for (ItemButton button : shapeReplacements.values()) + if (Objects.equals(button.getCustomData().get(key), value)) + button.setItem(itemSupplier); + return this; + } + + public @NotNull PaginatedInventoryBuilder setReplacementItemByCustomData(@NotNull Object key, @Nullable Object value, @NotNull Function replacer) { + for (ItemButton button : shapeReplacements.values()) { + ItemStack originalItem = button.getItem().get(); + if (Objects.equals(button.getCustomData().get(key), value)) { + button.setItem(() -> replacer.apply(ItemBuilder.fromItemStack(originalItem)).build()); + } + } + return this; + } + + //ItemButton + + public @NotNull PaginatedInventoryBuilder setReplacementItemButtonByCustomData(@NotNull Object key, @Nullable Object value, @NotNull Supplier itemSupplier) { + ItemButton newButton = itemSupplier.get(); + for (Map.Entry entry : shapeReplacements.entrySet()) { + ItemButton button = entry.getValue(); + if (Objects.equals(button.getCustomData().get(key), value)) + entry.setValue(newButton); + } + return this; + } + + public @NotNull PaginatedInventoryBuilder setReplacementItemButtonByCustomData(@NotNull Object key, @Nullable Object value, @NotNull ItemButton item) { + for (Map.Entry entry : shapeReplacements.entrySet()) { + ItemButton button = entry.getValue(); + if (Objects.equals(button.getCustomData().get(key), value)) + entry.setValue(item); + } + return this; + } + + public @NotNull PaginatedInventoryBuilder setReplacementItemButtonByCustomData(@NotNull Object key, @Nullable Object value, @NotNull Function replacer) { + for (Map.Entry entry : shapeReplacements.entrySet()) { + ItemButton button = entry.getValue(); + if (Objects.equals(button.getCustomData().get(key), value)) + entry.setValue(replacer.apply(button)); + } + return this; + } + + //rest of the code + public PaginatedInventoryBuilder content(@Nullable List content) { this.content = content == null ? new ArrayList<>() : content; checkNullContent(); diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/ItemBuilder.java b/core/src/main/java/com/wizardlybump17/wlib/item/ItemBuilder.java index e55b5657..3e519624 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/ItemBuilder.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/ItemBuilder.java @@ -41,61 +41,79 @@ @SerializableAs("item-builder") public class ItemBuilder implements ConfigurationSerializable, Cloneable { - private @NotNull Material type; - private int amount; - private final @NotNull Map customData; + private @NotNull Material type = Material.AIR; + private int amount = 1; + private final @NotNull Map customData = new TreeMap<>(); private @Nullable ItemMetaHandler metaHandler; private @Nullable ItemMeta itemMeta; - public ItemBuilder(@NotNull Material type, int amount, @NotNull Map customData, @Nullable ItemMeta itemMeta) { - this.type = type; - this.amount = amount; - this.customData = customData; - - ItemMetaHandlerModel metaHandlerModel = ItemMetaHandlerModel.getApplicableModel(type); - if (metaHandlerModel != null) - this.metaHandler = metaHandlerModel.createHandler(this); - - this.itemMeta = itemMeta; + public ItemBuilder() { } - public ItemBuilder(@NotNull Material type, int amount, @NotNull Map customData) { - this(type, amount, customData, null); + public ItemBuilder(@NotNull Material type) { + this.type = type; + itemMeta = Bukkit.getItemFactory().getItemMeta(type); + metaHandler = ItemMetaHandlerModel.getApplicableModelOptional(type) + .map(model -> model.createHandler(itemMeta)) + .orElse(null); } public ItemBuilder(@NotNull Material type, int amount) { - this(type, amount, new HashMap<>()); + this.type = type; + this.amount = amount; + itemMeta = Bukkit.getItemFactory().getItemMeta(type); + metaHandler = ItemMetaHandlerModel.getApplicableModelOptional(type) + .map(model -> model.createHandler(itemMeta)) + .orElse(null); } - public ItemBuilder(@NotNull Material type) { - this(type, 1); + public ItemBuilder(@NotNull Material type, int amount, @NotNull Map customData) { + this.type = type; + this.amount = amount; + itemMeta = Bukkit.getItemFactory().getItemMeta(type); + metaHandler = ItemMetaHandlerModel.getApplicableModelOptional(type) + .map(model -> model.createHandler(itemMeta)) + .orElse(null); + this.customData.putAll(customData); } - public ItemBuilder(@Nullable ItemStack item, @NotNull Map customData) { - this( - item == null ? Material.AIR : item.getType(), - item == null ? 1 : item.getAmount(), - customData, - item == null ? null : item.getItemMeta() - ); + public ItemBuilder(@NotNull Material type, int amount, @NotNull Map customData, @Nullable ItemMeta itemMeta) { + this.type = type; + this.amount = amount; + this.itemMeta = itemMeta; + if (itemMeta != null) { + metaHandler = ItemMetaHandlerModel.getApplicableModelOptional(type) + .map(model -> model.createHandler(itemMeta)) + .orElse(null); + } + this.customData.putAll(customData); } - public ItemBuilder(@Nullable ItemStack item) { - this( - item == null ? Material.AIR : item.getType(), - item == null ? 1 : item.getAmount(), - new HashMap<>(), - item == null ? null : item.getItemMeta() - ); + /** + * @deprecated use {@link #fromItemStack(ItemStack)} instead + */ + @Deprecated(forRemoval = true) + public ItemBuilder(@NotNull ItemStack item) { + type = item.getType(); + amount = item.getAmount(); + itemMeta = item.getItemMeta(); + if (itemMeta != null) { + metaHandler = ItemMetaHandlerModel.getApplicableModelOptional(type) + .map(model -> model.createHandler(itemMeta)) + .orElse(null); + } } - public ItemBuilder() { - this( - Material.AIR, - 1, - new HashMap<>(), - null - ); + public ItemBuilder(@NotNull ItemStack item, @NotNull Map customData) { + type = item.getType(); + amount = item.getAmount(); + itemMeta = item.getItemMeta(); + if (itemMeta != null) { + metaHandler = ItemMetaHandlerModel.getApplicableModelOptional(type) + .map(model -> model.createHandler(itemMeta)) + .orElse(null); + } + this.customData.putAll(customData); } @SuppressWarnings("unchecked") @@ -145,7 +163,7 @@ public ItemBuilder type(@NonNull Material material) { itemMeta = itemMeta == null ? itemFactory.getItemMeta(type) : itemFactory.asMetaFor(itemMeta, type); ItemMetaHandlerModel model = ItemMetaHandlerModel.getApplicableModel(type); - metaHandler = model == null ? null : model.createHandler(this); + metaHandler = model == null || itemMeta == null ? null : model.createHandler(itemMeta); return this; } @@ -495,7 +513,7 @@ public ItemBuilder clone() { return new ItemBuilder( type, amount, - new HashMap<>(customData), + customData, itemMeta == null ? null : itemMeta.clone() ); } @@ -521,7 +539,13 @@ public ItemBuilder clone() { * @return a new builder with the data from the given item */ public static ItemBuilder fromItemStack(@Nullable ItemStack item) { - return new ItemBuilder(item, new HashMap<>()); + if (item == null) + return empty(); + return new ItemBuilder(item.getType(), item.getAmount(), Map.of(), item.getItemMeta()); + } + + public static @NotNull ItemBuilder empty() { + return new ItemBuilder(); } public static ItemBuilder deserialize(Map map) { @@ -559,7 +583,7 @@ public static ItemBuilder deserialize(Map map) { .ifPresent(result::itemCustomData); ItemMetaHandlerModel metaHandlerModel = ItemMetaHandlerModel.getApplicableModel(result.type()); - result.metaHandler(metaHandlerModel == null ? null : metaHandlerModel.createHandler(result)); + result.metaHandler(metaHandlerModel == null || result.itemMeta == null ? null : metaHandlerModel.createHandler(result.itemMeta)); if (result.metaHandler != null) result.metaHandler.deserialize(map); diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/FireworkMetaHandler.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/FireworkMetaHandler.java index 724bd80f..278d4717 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/FireworkMetaHandler.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/FireworkMetaHandler.java @@ -1,57 +1,66 @@ package com.wizardlybump17.wlib.item.handler; -import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.model.FireworkMetaHandlerModel; import org.bukkit.FireworkEffect; import org.bukkit.inventory.meta.FireworkMeta; +import org.jetbrains.annotations.NotNull; -import java.util.Collections; import java.util.List; import java.util.Map; public class FireworkMetaHandler extends ItemMetaHandler { - public FireworkMetaHandler(FireworkMetaHandlerModel model, ItemBuilder builder) { - super(model, builder); + public FireworkMetaHandler(FireworkMetaHandlerModel model, @NotNull FireworkMeta itemMeta) { + super(model, itemMeta); + } + + @Override + public @NotNull FireworkMeta getItemMeta() { + return (FireworkMeta) super.getItemMeta(); } @Override public void serialize(Map map) { - List effects = getBuilder().getFromMeta(FireworkMeta::getEffects, Collections.emptyList()); - map.put("effects", effects.isEmpty() ? null : effects); - int power = getBuilder().getFromMeta(FireworkMeta::getPower, 0); - map.put("power", power == 0 ? null : power); + FireworkMeta itemMeta = getItemMeta(); + + List effects = itemMeta.getEffects(); + if (!effects.isEmpty()) + map.put("effects", effects); + + int power = itemMeta.getPower(); + map.put("power", power); } - @SuppressWarnings("unchecked") @Override public void deserialize(Map map) { - getBuilder().consumeMeta(meta -> { - meta.addEffects((List) map.getOrDefault("effects", Collections.emptyList())); - meta.setPower((Integer) map.getOrDefault("power", 0)); - }); + FireworkMeta itemMeta = getItemMeta(); + + itemMeta.addEffects((FireworkEffect) map.getOrDefault("effects", List.of())); + itemMeta.setPower((int) map.getOrDefault("power", 0)); } public FireworkMetaHandler effects(FireworkEffect... effects) { - getBuilder().consumeMeta(meta -> meta.addEffects(effects)); + FireworkMeta itemMeta = getItemMeta(); + itemMeta.clearEffects(); + itemMeta.addEffects(effects); return this; } public FireworkMetaHandler power(int power) { - getBuilder().consumeMeta(meta -> meta.setPower(power)); + getItemMeta().setPower(power); return this; } public FireworkMetaHandler clearEffects() { - getBuilder().consumeMeta(FireworkMeta::clearEffects); + getItemMeta().clearEffects(); return this; } public List effects() { - return getBuilder().getFromMeta(FireworkMeta::getEffects, Collections.emptyList()); + return getItemMeta().getEffects(); } public int power() { - return getBuilder().getFromMeta(FireworkMeta::getPower, 0); + return getItemMeta().getPower(); } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/ItemMetaHandler.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/ItemMetaHandler.java index 6c137fa5..8a9bc9e4 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/ItemMetaHandler.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/ItemMetaHandler.java @@ -1,8 +1,9 @@ package com.wizardlybump17.wlib.item.handler; -import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.model.ItemMetaHandlerModel; import lombok.Data; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -10,9 +11,13 @@ public abstract class ItemMetaHandler> { private final M model; - private final ItemBuilder builder; + private final @NotNull ItemMeta itemMeta; public abstract void serialize(Map map); public abstract void deserialize(Map map); + + public @NotNull ItemMeta getItemMeta() { + return itemMeta; + } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/LeatherArmorMetaHandler.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/LeatherArmorMetaHandler.java index 981091fc..4781c604 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/LeatherArmorMetaHandler.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/LeatherArmorMetaHandler.java @@ -1,35 +1,44 @@ package com.wizardlybump17.wlib.item.handler; -import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.model.LeatherArmorMetaHandlerModel; import org.bukkit.Color; import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.jetbrains.annotations.NotNull; import java.util.Map; public class LeatherArmorMetaHandler extends ItemMetaHandler { - public LeatherArmorMetaHandler(LeatherArmorMetaHandlerModel model, ItemBuilder builder) { - super(model, builder); + public LeatherArmorMetaHandler(LeatherArmorMetaHandlerModel model, LeatherArmorMeta itemMeta) { + super(model, itemMeta); } @Override - public void serialize(Map map) { - map.put("color", getBuilder().getFromMeta(LeatherArmorMeta::getColor, (Color) null)); + public @NotNull LeatherArmorMeta getItemMeta() { + return (LeatherArmorMeta) super.getItemMeta(); } @Override - public void deserialize(Map map) { - getBuilder().consumeMeta(meta -> meta.setColor(getColor(map.get("color")))); + public void serialize(@NotNull Map map) { + LeatherArmorMeta itemMeta = getItemMeta(); + + map.put("color", itemMeta.getColor()); + } + + @Override + public void deserialize(@NotNull Map map) { + LeatherArmorMeta itemMeta = getItemMeta(); + + itemMeta.setColor(getColor(map.get("color"))); } - public LeatherArmorMetaHandler color(Color color) { - getBuilder().consumeMeta(meta -> meta.setColor(color)); + public @NotNull LeatherArmorMetaHandler color(@NotNull Color color) { + getItemMeta().setColor(color); return this; } - public Color color() { - return getBuilder().getFromMeta(LeatherArmorMeta::getColor, (Color) null); + public @NotNull Color color() { + return getItemMeta().getColor(); } private static Color getColor(Object object) { diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/PotionMetaHandler.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/PotionMetaHandler.java index 3d752ea9..837a6e0e 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/PotionMetaHandler.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/PotionMetaHandler.java @@ -1,6 +1,5 @@ package com.wizardlybump17.wlib.item.handler; -import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.model.PotionMetaHandlerModel; import com.wizardlybump17.wlib.util.bukkit.config.wrapper.potion.PotionDataWrapper; import lombok.NonNull; @@ -9,6 +8,7 @@ import org.bukkit.potion.PotionData; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; @@ -17,8 +17,13 @@ public class PotionMetaHandler extends ItemMetaHandler { - public PotionMetaHandler(PotionMetaHandlerModel model, ItemBuilder builder) { - super(model, builder); + public PotionMetaHandler(@NotNull PotionMetaHandlerModel model, @NotNull PotionMeta itemMeta) { + super(model, itemMeta); + } + + @Override + public @NotNull PotionMeta getItemMeta() { + return (PotionMeta) super.getItemMeta(); } @Override @@ -40,48 +45,48 @@ public void deserialize(Map map) { } public PotionMetaHandler basePotionData(@NonNull PotionData data) { - getBuilder().consumeMeta(meta -> meta.setBasePotionData(data)); + getItemMeta().setBasePotionData(data); return this; } public PotionData basePotionData() { - return getBuilder().getFromMeta(PotionMeta::getBasePotionData, (PotionData) null); + return getItemMeta().getBasePotionData(); } public boolean hasCustomEffects() { - return getBuilder().getFromMeta(PotionMeta::hasCustomEffects, false); + return getItemMeta().hasCustomEffects(); } public @NonNull List customEffects() { - return getBuilder().getFromMeta(PotionMeta::getCustomEffects, Collections.emptyList()); + return getItemMeta().getCustomEffects(); } public boolean customEffect(@NonNull PotionEffect effect, boolean overwrite) { - return getBuilder().consumeMetaAndReturn(meta -> meta.addCustomEffect(effect, overwrite), false); + return getItemMeta().addCustomEffect(effect, overwrite); } public boolean removeCustomEffect(@NonNull PotionEffectType type) { - return getBuilder().consumeMetaAndReturn(meta -> meta.removeCustomEffect(type), false); + return getItemMeta().removeCustomEffect(type); } public boolean hasCustomEffect(@NonNull PotionEffectType type) { - return getBuilder().getFromMeta(meta -> meta.hasCustomEffect(type), false); + return getItemMeta().hasCustomEffect(type); } public boolean clearCustomEffects() { - return getBuilder().consumeMetaAndReturn(PotionMeta::clearCustomEffects, false); + return getItemMeta().clearCustomEffects(); } public boolean hasColor() { - return getBuilder().getFromMeta(PotionMeta::hasColor, false); + return getItemMeta().hasColor(); } public @Nullable Color color() { - return getBuilder().getFromMeta(PotionMeta::getColor, (Color) null); + return getItemMeta().getColor(); } public PotionMetaHandler color(@Nullable Color color) { - getBuilder().consumeMeta(meta -> meta.setColor(color)); + getItemMeta().setColor(color); return this; } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/SkullMetaHandler.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/SkullMetaHandler.java index a66017c4..523d601e 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/SkullMetaHandler.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/SkullMetaHandler.java @@ -1,12 +1,13 @@ package com.wizardlybump17.wlib.item.handler; import com.destroystokyo.paper.profile.PlayerProfile; -import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.model.SkullMetaHandlerModel; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.profile.PlayerTextures; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.net.MalformedURLException; import java.net.URI; @@ -16,16 +17,24 @@ public class SkullMetaHandler extends ItemMetaHandler { - public SkullMetaHandler(SkullMetaHandlerModel model, ItemBuilder builder) { - super(model, builder); + public SkullMetaHandler(@NotNull SkullMetaHandlerModel model, @NotNull SkullMeta itemMeta) { + super(model, itemMeta); + } + + @Override + public @NotNull SkullMeta getItemMeta() { + return (SkullMeta) super.getItemMeta(); } @Override public void serialize(Map map) { - if (skullUrl() != null) - map.put("skull", skullUrl()); - if (skullOwner() != null) - map.put("owner", skullOwner().getUniqueId().toString()); + String skullUrl = skullUrl(); + if (skullUrl != null) + map.put("skull", skullUrl); + + OfflinePlayer skullOwner = skullOwner(); + if (skullOwner != null) + map.put("owner", skullOwner.getUniqueId().toString()); } @Override @@ -41,41 +50,53 @@ public void deserialize(Map map) { skull(Bukkit.getOfflinePlayer(UUID.fromString(owner))); } - public String skullUrl() { - return getBuilder().getFromMeta(meta -> { - PlayerProfile profile = meta.getPlayerProfile(); - if (profile == null) - return null; + public @Nullable String skullUrl() { + PlayerProfile profile = getItemMeta().getPlayerProfile(); + if (profile == null) + return null; - URL skin = profile.getTextures().getSkin(); - return skin == null ? null : skin.toString(); - }, () -> null); + URL skin = profile.getTextures().getSkin(); + return skin == null ? null : skin.toString(); } - public SkullMetaHandler skull(String url) { - getBuilder().consumeMeta(meta -> { - try { - PlayerProfile profile = Bukkit.createProfile(UUID.nameUUIDFromBytes(url.getBytes())); + public @NotNull SkullMetaHandler skull(@Nullable String url) { + SkullMeta itemMeta = getItemMeta(); + + if (url == null) { + PlayerProfile profile = itemMeta.getPlayerProfile(); + if (profile == null) + return this; - PlayerTextures textures = profile.getTextures(); - textures.setSkin(URI.create(url).toURL()); + PlayerTextures textures = profile.getTextures(); - profile.setTextures(textures); + textures.setSkin(null); + profile.setTextures(textures); - meta.setPlayerProfile(profile); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Invalid URL " + url, e); - } - }); - return this; + itemMeta.setPlayerProfile(profile); + return this; + } + + try { + PlayerProfile profile = Bukkit.createProfile(UUID.nameUUIDFromBytes(url.getBytes())); + + PlayerTextures textures = profile.getTextures(); + textures.setSkin(URI.create(url).toURL()); + + profile.setTextures(textures); + + itemMeta.setPlayerProfile(profile); + return this; + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid URL " + url, e); + } } - public OfflinePlayer skullOwner() { - return getBuilder().getFromMeta(SkullMeta::getOwningPlayer, (OfflinePlayer) null); + public @Nullable OfflinePlayer skullOwner() { + return getItemMeta().getOwningPlayer(); } - public SkullMetaHandler skull(OfflinePlayer owner) { - getBuilder().consumeMeta(meta -> meta.setOwningPlayer(owner)); + public @NotNull SkullMetaHandler skull(@Nullable OfflinePlayer owner) { + getItemMeta().setOwningPlayer(owner); return this; } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/FireworkMetaHandlerModel.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/FireworkMetaHandlerModel.java index f732074c..2eeebf66 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/FireworkMetaHandlerModel.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/FireworkMetaHandlerModel.java @@ -3,6 +3,9 @@ import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.FireworkMetaHandler; import org.bukkit.Material; +import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import java.util.Set; @@ -14,6 +17,11 @@ public FireworkMetaHandlerModel() { @Override public FireworkMetaHandler createHandler(ItemBuilder builder) { - return new FireworkMetaHandler(this, builder); + return new FireworkMetaHandler(this, builder.getItemMeta()); + } + + @Override + public @NotNull FireworkMetaHandler createHandler(@NotNull ItemMeta itemMeta) { + return new FireworkMetaHandler(this, (FireworkMeta) itemMeta); } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/ItemMetaHandlerModel.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/ItemMetaHandlerModel.java index 74b3a487..2b6ad166 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/ItemMetaHandlerModel.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/ItemMetaHandlerModel.java @@ -4,10 +4,13 @@ import com.wizardlybump17.wlib.item.handler.ItemMetaHandler; import lombok.Data; import org.bukkit.Material; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.EnumMap; import java.util.Map; +import java.util.Optional; import java.util.Set; @Data @@ -23,8 +26,14 @@ public ItemMetaHandlerModel(Set applicableMaterials) { registerModel(this); } + /** + * @deprecated use {@link #createHandler(ItemMeta)} instead + */ + @Deprecated(forRemoval = true) public abstract H createHandler(ItemBuilder builder); + public abstract @NotNull H createHandler(@NotNull ItemMeta itemMeta); + public boolean isApplicable(Material material) { return applicableMaterials.contains(material); } @@ -34,6 +43,10 @@ public static ItemMetaHandlerModel getApplicableModel(Material material) { return MODELS.get(material); } + public static @NotNull Optional> getApplicableModelOptional(@NotNull Material material) { + return Optional.ofNullable(getApplicableModel(material)); + } + public static void registerModel(ItemMetaHandlerModel model) { for (Material material : model.getApplicableMaterials()) MODELS.put(material, model); diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/LeatherArmorMetaHandlerModel.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/LeatherArmorMetaHandlerModel.java index a4cff9dc..078702ef 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/LeatherArmorMetaHandlerModel.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/LeatherArmorMetaHandlerModel.java @@ -3,6 +3,9 @@ import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.LeatherArmorMetaHandler; import org.bukkit.Material; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.jetbrains.annotations.NotNull; import java.util.Set; @@ -14,6 +17,11 @@ public LeatherArmorMetaHandlerModel() { @Override public LeatherArmorMetaHandler createHandler(ItemBuilder builder) { - return new LeatherArmorMetaHandler(this, builder); + return new LeatherArmorMetaHandler(this, builder.getItemMeta()); + } + + @Override + public @NotNull LeatherArmorMetaHandler createHandler(@NotNull ItemMeta itemMeta) { + return new LeatherArmorMetaHandler(this, (LeatherArmorMeta) itemMeta); } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/PotionMetaHandlerModel.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/PotionMetaHandlerModel.java index b119af08..652bbcfe 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/PotionMetaHandlerModel.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/PotionMetaHandlerModel.java @@ -3,6 +3,9 @@ import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.PotionMetaHandler; import org.bukkit.Material; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.jetbrains.annotations.NotNull; import java.util.Set; @@ -14,6 +17,11 @@ public PotionMetaHandlerModel() { @Override public PotionMetaHandler createHandler(ItemBuilder builder) { - return new PotionMetaHandler(this, builder); + return new PotionMetaHandler(this, builder.getItemMeta()); + } + + @Override + public @NotNull PotionMetaHandler createHandler(@NotNull ItemMeta itemMeta) { + return new PotionMetaHandler(this, (PotionMeta) itemMeta); } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/SkullMetaHandlerModel.java b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/SkullMetaHandlerModel.java index 2445e3de..8bdf022b 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/SkullMetaHandlerModel.java +++ b/core/src/main/java/com/wizardlybump17/wlib/item/handler/model/SkullMetaHandlerModel.java @@ -3,6 +3,9 @@ import com.wizardlybump17.wlib.item.ItemBuilder; import com.wizardlybump17.wlib.item.handler.SkullMetaHandler; import org.bukkit.Material; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.jetbrains.annotations.NotNull; import java.util.Set; @@ -14,6 +17,11 @@ public SkullMetaHandlerModel() { @Override public SkullMetaHandler createHandler(ItemBuilder builder) { - return new SkullMetaHandler(this, builder); + return new SkullMetaHandler(this, builder.getItemMeta()); + } + + @Override + public @NotNull SkullMetaHandler createHandler(@NotNull ItemMeta itemMeta) { + return new SkullMetaHandler(this, (SkullMeta) itemMeta); } } diff --git a/core/src/main/java/com/wizardlybump17/wlib/listener/EntityListener.java b/core/src/main/java/com/wizardlybump17/wlib/listener/EntityListener.java index 28382af8..62545845 100644 --- a/core/src/main/java/com/wizardlybump17/wlib/listener/EntityListener.java +++ b/core/src/main/java/com/wizardlybump17/wlib/listener/EntityListener.java @@ -1,5 +1,7 @@ package com.wizardlybump17.wlib.listener; +import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; +import com.wizardlybump17.wlib.command.sender.BukkitCommandSender; import com.wizardlybump17.wlib.inventory.CustomInventory; import com.wizardlybump17.wlib.inventory.CustomInventoryHolder; import com.wizardlybump17.wlib.inventory.item.ClickAction; @@ -15,6 +17,7 @@ import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; @RequiredArgsConstructor public class EntityListener implements Listener { @@ -63,4 +66,9 @@ public void onClose(InventoryCloseEvent event) { paginatedInventory.stopListeners(); } + + @EventHandler + public void onRemove(@NotNull EntityRemoveFromWorldEvent event) { + BukkitCommandSender.removeFromCache(event.getEntity().getUniqueId()); + } } diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index dee76513..f4a373e2 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -3,4 +3,9 @@ main: com.wizardlybump17.wlib.WLib version: '${version}' author: WizardlyBump17 api-version: '1.13' -load: STARTUP \ No newline at end of file +load: STARTUP +libraries: + - net.kyori:adventure-api:4.18.0 + - net.kyori:adventure-text-minimessage:4.18.0 + - net.kyori:adventure-platform-bukkit:4.3.4 + - com.google.code.gson:gson:${gson} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ac57dd15..d706aba6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index e82789f4..1773dc8a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,13 +20,13 @@ include 'bukkit-utils' include 'versions:adapter' findProject(':versions:adapter')?.name = 'adapter' + include 'versions:v1_20_R4' -findProject(':versions:v1_20_R4')?.name = 'v1_20_R4' +findProject(':versions:v1_20_R4')?.name = 'v1_20_r4' include 'versions:v1_21_R1' -findProject(':versions:v1_21_R1')?.name = 'v1_21_R1' +findProject(':versions:v1_21_R1')?.name = 'v1_21_r1' include 'versions:v1_21_R3' -findProject(':versions:v1_21_R3')?.name = 'v1_21_R3' +findProject(':versions:v1_21_R3')?.name = 'v1_21_r3' include 'versions:v1_21_R5' -findProject(':versions:v1_21_R5')?.name = 'v1_21_R5' - -include 'versions:v1_21_r7' \ No newline at end of file +findProject(':versions:v1_21_R5')?.name = 'v1_21_r5' +include 'versions:v1_21_r7' diff --git a/utils/src/main/java/com/wizardlybump17/wlib/util/CollectionUtil.java b/utils/src/main/java/com/wizardlybump17/wlib/util/CollectionUtil.java index 8c355141..09b4ac26 100644 --- a/utils/src/main/java/com/wizardlybump17/wlib/util/CollectionUtil.java +++ b/utils/src/main/java/com/wizardlybump17/wlib/util/CollectionUtil.java @@ -2,6 +2,7 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -235,4 +236,10 @@ public static > T deepClone(Supplier supplier, T o public static @NonNull List sort(@NonNull Collection collection) { return sort(collection, null); } + + public static boolean contentEquals(@NotNull Collection left, @NotNull Collection right) { + if (left.size() != right.size()) + return false; + return left.containsAll(right) && right.containsAll(left); + } } diff --git a/utils/src/main/java/com/wizardlybump17/wlib/util/StringUtil.java b/utils/src/main/java/com/wizardlybump17/wlib/util/StringUtil.java index cf848b11..d7b88239 100644 --- a/utils/src/main/java/com/wizardlybump17/wlib/util/StringUtil.java +++ b/utils/src/main/java/com/wizardlybump17/wlib/util/StringUtil.java @@ -3,6 +3,7 @@ import com.wizardlybump17.wlib.util.exception.PlaceholderException; import com.wizardlybump17.wlib.util.exception.QuotedStringException; import lombok.NonNull; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -301,7 +302,15 @@ public static String fixName(String name) { * * {@code Hello "Beautiful World"} * {@code [Hello, Beautiful World]} - * + * + * + * {@code Hello World } (there is an extra space at the end) + * {@code [Hello, World, ]} + * + * + * Empty String + * {@code []} + * * *

* @@ -312,8 +321,6 @@ public static String fixName(String name) { * @return the {@link List} of {@link String}s * @throws QuotedStringException if any of the following situations happen: *
    - *
  • A quoted string is started right after a normal (or escaped) {@code char};
  • - *
  • A quoted string is ended right before a normal (or escaped) {@code char};
  • *
  • A escape {@code char} is found at the end of the input;
  • *
  • A quoted string is not closed.
  • *
@@ -323,70 +330,86 @@ public static String fixName(String name) { public static @NonNull List parseQuotedStrings(@NonNull String input, char quote, char escape, char delimiter) throws QuotedStringException { List strings = new ArrayList<>(); + if (input.isEmpty()) + return strings; + char[] chars = input.toCharArray(); StringBuilder builder = new StringBuilder(); - StringBuilder quoted = new StringBuilder(); + StringBuilder quotes = new StringBuilder(); boolean escaped = false; - boolean delimited = true; - boolean hadQuote = false; - for (char current : chars) { - if (current == escape && !escaped) { // start of an escaped char + for (char currentChar : chars) { + if (!escaped && currentChar == escape) { escaped = true; continue; } - if (escaped) { // end of the escaped char - (quoted.isEmpty() ? builder : quoted).append(current); + if (escaped) { + (quotes.isEmpty() ? builder : quotes).append(currentChar); escaped = false; continue; } - if (current == quote) { - if (!delimited) // the previous char was not the delimiter. Example case: string"quoted" - throw new QuotedStringException(QuotedStringException.QUOTED_WITHOUT_DELIMITER); - - if (quoted.isEmpty()) { // begin of quoted string - quoted.append(quote); - continue; + if (currentChar == quote) { + if (!quotes.isEmpty()) { + quotes.deleteCharAt(0); + builder.append(quotes); + quotes.setLength(0); + } else { + quotes.append(quote); } - - // end of quoted string - strings.add(quoted.substring(1)); - delimited = false; - hadQuote = true; - quoted.setLength(0); continue; } - if (current == delimiter && quoted.isEmpty()) { // delimiter (space) - if (!builder.isEmpty()) { - strings.add(builder.toString()); - builder.setLength(0); - } - delimited = true; - hadQuote = false; + if (!quotes.isEmpty()) { + quotes.append(currentChar); continue; } - if (hadQuote) // the previous char was a quote. Example case: "quoted"string - throw new QuotedStringException(QuotedStringException.NON_QUOTED_AFTER_QUOTED); + if (currentChar == delimiter) { + strings.add(builder.toString()); + builder.setLength(0); + continue; + } - (quoted.isEmpty() ? builder : quoted).append(current); // any char - if (quoted.isEmpty()) - delimited = false; + builder.append(currentChar); } if (escaped) throw new QuotedStringException(QuotedStringException.INVALID_ESCAPE); - if (!quoted.isEmpty()) + if (!quotes.isEmpty()) throw new QuotedStringException(QuotedStringException.UNCLOSED_QUOTE); if (!builder.isEmpty()) strings.add(builder.toString()); + return strings; } + public static boolean isProperlyQuoted(@NotNull String input, char quote, char escape) throws QuotedStringException { + char[] chars = input.toCharArray(); + boolean escaped = false; + boolean onQuotes = false; + + for (char currentChar : chars) { + if (currentChar == escape) { + escaped = true; + continue; + } + + escaped = false; + + if (currentChar == quote) + onQuotes = !onQuotes; + } + + return !escaped && !onQuotes; + } + + public static boolean isProperlyQuoted(@NotNull String input) throws QuotedStringException { + return isProperlyQuoted(input, QUOTE, QUOTE_ESCAPE); + } + /** *

* Removes any extra space from the {@link String}. @@ -445,4 +468,66 @@ public static String fixName(String name) { return builder.toString(); } + + /** + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
InputOutput
Hello"Hello"
Hello World"Hello\ World"
"Hello World""\"Hello\ World\""
\"\\"
\Hello World"\\Hello\ World"
+ */ + public static @NotNull String escapeString(@NotNull String input, char quote, char escape) { + StringBuilder result = new StringBuilder(input.length()); + result.append(quote); + + for (char currentChar : input.toCharArray()) { + if (currentChar == quote || currentChar == escape) { + result.append(escape).append(currentChar); + } else { + result.append(currentChar); + } + } + + result.append(quote); + return result.toString(); + } + + public static @NotNull String escapeString(@NotNull String input) { + return escapeString(input, QUOTE, ESCAPE); + } + + public static @NotNull String pascalToCamel(@NotNull String input) { + StringBuilder builder = new StringBuilder(input.length()); + for (char currentChar : input.toCharArray()) { + if (currentChar >= 'A' && currentChar <= 'Z') { + if (!builder.isEmpty()) + builder.append('_'); + builder.append(Character.toLowerCase(currentChar)); + continue; + } + builder.append(currentChar); + } + return builder.toString(); + } } diff --git a/utils/src/main/java/com/wizardlybump17/wlib/util/exception/QuotedStringException.java b/utils/src/main/java/com/wizardlybump17/wlib/util/exception/QuotedStringException.java index ed8713b5..4701ac19 100644 --- a/utils/src/main/java/com/wizardlybump17/wlib/util/exception/QuotedStringException.java +++ b/utils/src/main/java/com/wizardlybump17/wlib/util/exception/QuotedStringException.java @@ -4,8 +4,6 @@ public class QuotedStringException extends RuntimeException { - public static final @NonNull String QUOTED_WITHOUT_DELIMITER = "Can not have quoted strings without the delimiter"; - public static final @NonNull String NON_QUOTED_AFTER_QUOTED = "Can not have non-quoted strings after quoted strings"; public static final @NonNull String INVALID_ESCAPE = "Invalid escape sequence"; public static final @NonNull String UNCLOSED_QUOTE = "Unclosed quote"; diff --git a/utils/src/test/java/com/wizardlybump17/wlib/util/test/QuotedStringsTests.java b/utils/src/test/java/com/wizardlybump17/wlib/util/test/QuotedStringsTests.java index 4108d47c..14ee3b6c 100644 --- a/utils/src/test/java/com/wizardlybump17/wlib/util/test/QuotedStringsTests.java +++ b/utils/src/test/java/com/wizardlybump17/wlib/util/test/QuotedStringsTests.java @@ -2,12 +2,12 @@ import com.wizardlybump17.wlib.util.StringUtil; import com.wizardlybump17.wlib.util.exception.QuotedStringException; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; class QuotedStringsTests { @@ -26,7 +26,7 @@ void testNoQuotes() { StringUtil.parseQuotedStrings("Hello Beautiful World", QUOTE, ESCAPE, DELIMITER) ); assertEquals( - List.of("Hello", "World", "Hi"), + List.of("Hello", "World", "", "", "Hi"), StringUtil.parseQuotedStrings("Hello World Hi", QUOTE, ESCAPE, DELIMITER) ); } @@ -77,47 +77,177 @@ void testQuotesInTheMiddle() { @Test void testNotEndedQuotes() { - assertThrows( - QuotedStringException.class, - () -> StringUtil.parseQuotedStrings("Hello \"World", QUOTE, ESCAPE, DELIMITER), - QuotedStringException.UNCLOSED_QUOTE + Assertions.assertEquals( + QuotedStringException.UNCLOSED_QUOTE, + Assertions.assertThrowsExactly( + QuotedStringException.class, + () -> StringUtil.parseQuotedStrings("Hello \"World", QUOTE, ESCAPE, DELIMITER) + ).getMessage() + ); + Assertions.assertEquals( + QuotedStringException.UNCLOSED_QUOTE, + Assertions.assertThrowsExactly( + QuotedStringException.class, + () -> StringUtil.parseQuotedStrings("Hello World \"Hi", QUOTE, ESCAPE, DELIMITER) + ).getMessage() + ); + Assertions.assertEquals( + QuotedStringException.UNCLOSED_QUOTE, + Assertions.assertThrowsExactly( + QuotedStringException.class, + () -> StringUtil.parseQuotedStrings("Hello World \"Hi there, nice\" \"string", QUOTE, ESCAPE, DELIMITER) + ).getMessage() ); - assertThrows( - QuotedStringException.class, - () -> StringUtil.parseQuotedStrings("Hello World \"Hi", QUOTE, ESCAPE, DELIMITER), - QuotedStringException.UNCLOSED_QUOTE + } + + @Test + void testEscapeInTheEndException() { + Assertions.assertEquals( + QuotedStringException.INVALID_ESCAPE, + Assertions.assertThrowsExactly( + QuotedStringException.class, + () -> StringUtil.parseQuotedStrings("Hello World \\", QUOTE, ESCAPE, DELIMITER) + ).getMessage() ); - assertThrows( - QuotedStringException.class, - () -> StringUtil.parseQuotedStrings("Hello World \"Hi there, nice\" \"string", QUOTE, ESCAPE, DELIMITER), - QuotedStringException.UNCLOSED_QUOTE + } + + @Test + void testQuotedStringAfterNonQuotedStringSuccess() { + Assertions.assertEquals( + List.of("Hello", "WorldHi"), + StringUtil.parseQuotedStrings("Hello World\"Hi\"", QUOTE, ESCAPE, DELIMITER) ); } @Test - void testEscapeInTheEndException() { - assertThrows( - QuotedStringException.class, - () -> StringUtil.parseQuotedStrings("Hello World \\", QUOTE, ESCAPE, DELIMITER), - QuotedStringException.INVALID_ESCAPE + void testNonQuotedStringAfterQuotedStringSuccess() { + Assertions.assertEquals( + List.of("HelloWorld"), + StringUtil.parseQuotedStrings("\"Hello\"World", QUOTE, ESCAPE, DELIMITER) ); } @Test - void testQuotedStringAfterNonQuotedStringException() { - assertThrows( - QuotedStringException.class, - () -> StringUtil.parseQuotedStrings("Hello World\"Hi\"", QUOTE, ESCAPE, DELIMITER), - QuotedStringException.QUOTED_WITHOUT_DELIMITER + void testEmpty() { + Assertions.assertEquals( + List.of(), + StringUtil.parseQuotedStrings("", QUOTE, ESCAPE, DELIMITER) ); } @Test - void testNonQuotedStringAfterQuotedStringException() { - assertThrows( - QuotedStringException.class, - () -> StringUtil.parseQuotedStrings("\"Hello\"World", QUOTE, ESCAPE, DELIMITER), - QuotedStringException.NON_QUOTED_AFTER_QUOTED + void testEndingWithSpace0() { + Assertions.assertEquals( + List.of(""), + StringUtil.parseQuotedStrings(" ", QUOTE, ESCAPE, DELIMITER) + ); + } + + @Test + void testEndingWithSpace1() { + Assertions.assertEquals( + List.of("Hello", "World"), + StringUtil.parseQuotedStrings("Hello World ", QUOTE, ESCAPE, DELIMITER) + ); + } + + @Test + void testStringInsideString() { + Assertions.assertEquals( + List.of("Hello There \"Hi There\" Cool"), + StringUtil.parseQuotedStrings("\"Hello There \\\"Hi There\\\" Cool\"") + ); + } + + @Test + void testProperlyQuotedTrue() { + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello World", QUOTE, ESCAPE)); + + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello \"World\"", QUOTE, ESCAPE)); + Assertions.assertTrue(StringUtil.isProperlyQuoted("\"Hello World\"", QUOTE, ESCAPE)); + + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello \"World \"", QUOTE, ESCAPE)); + + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello \"World\" Hi There", QUOTE, ESCAPE)); + + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello \"World\"Hi There", QUOTE, ESCAPE)); + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello\"World\" Hi There", QUOTE, ESCAPE)); + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello\"World\"Hi There", QUOTE, ESCAPE)); + + Assertions.assertTrue(StringUtil.isProperlyQuoted("", QUOTE, ESCAPE)); + Assertions.assertTrue(StringUtil.isProperlyQuoted("\"\"", QUOTE, ESCAPE)); + + Assertions.assertTrue(StringUtil.isProperlyQuoted("Hello\\ World", QUOTE, ESCAPE)); + + Assertions.assertTrue(StringUtil.isProperlyQuoted("\"Hello There \\\"Hi There\\\" Cool\"")); + } + + @Test + void testProperlyQuotedFalse() { + Assertions.assertFalse(StringUtil.isProperlyQuoted("Hello \"World", QUOTE, ESCAPE)); + Assertions.assertFalse(StringUtil.isProperlyQuoted("Hello \"World\" Hi \"There", QUOTE, ESCAPE)); + + Assertions.assertFalse(StringUtil.isProperlyQuoted("\"", QUOTE, ESCAPE)); + + Assertions.assertFalse(StringUtil.isProperlyQuoted("Hello \"World\" \\", QUOTE, ESCAPE)); + } + + @Test + void testEscape() { + Assertions.assertEquals( + List.of("Hello World"), + StringUtil.parseQuotedStrings("\"Hello World\"", QUOTE, ESCAPE, DELIMITER) + ); + Assertions.assertEquals( + List.of("Hello World"), + StringUtil.parseQuotedStrings("Hello\\ World", QUOTE, ESCAPE, DELIMITER) + ); + + Assertions.assertEquals( + List.of("\\Hello", "World"), + StringUtil.parseQuotedStrings("\\\\Hello World", QUOTE, ESCAPE, DELIMITER) + ); + + Assertions.assertEquals( + List.of("\\"), + StringUtil.parseQuotedStrings("\\\\", QUOTE, ESCAPE, DELIMITER) + ); + Assertions.assertEquals( + List.of("\\\\"), + StringUtil.parseQuotedStrings("\\\\\\\\", QUOTE, ESCAPE, DELIMITER) + ); + } + + @Test + void testAddEscape() { + Assertions.assertEquals( + "\"Hello World\"", //"Hello World" + StringUtil.escapeString("Hello World", QUOTE, ESCAPE) + ); + + Assertions.assertEquals( + "\"Hello World \\\\\"", //"Hello World \\" + StringUtil.escapeString("Hello World \\", QUOTE, ESCAPE) + ); + + Assertions.assertEquals( + "\" \"", //" " + StringUtil.escapeString(" ", QUOTE, ESCAPE) + ); + + Assertions.assertEquals( + "\"\\\"\\\"\\\"\"", //"\"\"\"" + StringUtil.escapeString("\"\"\"", QUOTE, ESCAPE) + ); + + Assertions.assertEquals( + "\"\"", + StringUtil.escapeString("", QUOTE, ESCAPE) + ); + + Assertions.assertEquals( + "\"\\\"\"", // "\"" + StringUtil.escapeString("\"", QUOTE, ESCAPE) ); } } diff --git a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/Adapter.java b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/Adapter.java new file mode 100644 index 00000000..637b85e1 --- /dev/null +++ b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/Adapter.java @@ -0,0 +1,18 @@ +package com.wizardlybump17.wlib.adapter; + +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface Adapter { + + @NotNull Set getMinecraftVersions(); + + default void checkIsOnRightVersion() { + String current = Bukkit.getMinecraftVersion(); + Set expected = getMinecraftVersions(); + if (!expected.contains(current)) + throw new IllegalStateException("Expected Minecraft version(s) " + expected + ", but currently running " + current); + } +} diff --git a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/AttributeAdapter.java b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/AttributeAdapter.java index 184f178e..fcf80d9b 100644 --- a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/AttributeAdapter.java +++ b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/AttributeAdapter.java @@ -7,7 +7,7 @@ import java.util.Map; -public abstract class AttributeAdapter { +public abstract class AttributeAdapter implements Adapter { private static AttributeAdapter instance; @@ -24,6 +24,7 @@ public static AttributeAdapter getInstance() { public static void setInstance(@NotNull AttributeAdapter instance) { if (AttributeAdapter.instance != null) throw new IllegalStateException("The AttributeAdapter is already set"); + instance.checkIsOnRightVersion(); AttributeAdapter.instance = instance; } } diff --git a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/ItemAdapter.java b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/ItemAdapter.java index 4a8543f8..ae806240 100644 --- a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/ItemAdapter.java +++ b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/ItemAdapter.java @@ -11,7 +11,7 @@ import java.util.Map; -public abstract class ItemAdapter { +public abstract class ItemAdapter implements Adapter { public static final PersistentDataAdapterContext PERSISTENT_DATA_ADAPTER_CONTEXT = new ItemStack(Material.BOW).getItemMeta().getPersistentDataContainer().getAdapterContext(); private static ItemAdapter instance; @@ -21,6 +21,7 @@ public static ItemAdapter getInstance() { } public static void setInstance(ItemAdapter instance) { + instance.checkIsOnRightVersion(); if (ItemAdapter.instance == null) ItemAdapter.instance = instance; } diff --git a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/command/CommandMapAdapter.java b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/command/CommandMapAdapter.java new file mode 100644 index 00000000..d0948ef1 --- /dev/null +++ b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/command/CommandMapAdapter.java @@ -0,0 +1,30 @@ +package com.wizardlybump17.wlib.adapter.command; + +import com.wizardlybump17.wlib.adapter.Adapter; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public abstract class CommandMapAdapter implements Adapter { + + private static CommandMapAdapter instance; + + public abstract @NotNull CommandMap getCommandMap(); + + public abstract void unregisterCommand(@NotNull String command); + + public abstract @NotNull Map getCommands(); + + public static CommandMapAdapter getInstance() { + return instance; + } + + public static void setInstance(@NotNull CommandMapAdapter instance) { + if (CommandMapAdapter.instance != null) + throw new IllegalStateException("The CommandAdapter instance is already set."); + instance.checkIsOnRightVersion(); + CommandMapAdapter.instance = instance; + } +} diff --git a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/player/PlayerAdapter.java b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/player/PlayerAdapter.java index b072bcd4..dc1ea6c0 100644 --- a/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/player/PlayerAdapter.java +++ b/versions/adapter/src/main/java/com/wizardlybump17/wlib/adapter/player/PlayerAdapter.java @@ -1,5 +1,6 @@ package com.wizardlybump17.wlib.adapter.player; +import com.wizardlybump17.wlib.adapter.Adapter; import lombok.Getter; import lombok.NonNull; import org.bukkit.conversations.Conversation; @@ -9,7 +10,7 @@ import java.util.List; import java.util.function.Predicate; -public abstract class PlayerAdapter { +public abstract class PlayerAdapter implements Adapter { @Getter private static PlayerAdapter instance; @@ -23,6 +24,7 @@ public abstract class PlayerAdapter { public static void setInstance(PlayerAdapter instance) { if (PlayerAdapter.instance != null) throw new IllegalStateException("The PlayerAdapter instance is already set"); + instance.checkIsOnRightVersion(); PlayerAdapter.instance = instance; } } diff --git a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/AttributeAdapter.java b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/AttributeAdapter.java index 5f5d0355..7d9b36b2 100644 --- a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/AttributeAdapter.java +++ b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/AttributeAdapter.java @@ -10,7 +10,7 @@ import java.util.*; -public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter { +public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter implements BaseAdapter { @Override public Attribute getAttribute(@NotNull String name) { diff --git a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/BaseAdapter.java b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/BaseAdapter.java new file mode 100644 index 00000000..7ea70e18 --- /dev/null +++ b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/BaseAdapter.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.adapter.v1_20_R4; + +import com.wizardlybump17.wlib.adapter.Adapter; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface BaseAdapter extends Adapter { + + @Override + default @NotNull Set getMinecraftVersions() { + return Set.of("1.20.5", "1.20.6"); + } +} diff --git a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/ItemAdapter.java b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/ItemAdapter.java index 279537a1..97300bcf 100644 --- a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/ItemAdapter.java +++ b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/ItemAdapter.java @@ -13,7 +13,7 @@ import java.lang.reflect.Field; import java.util.*; -public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter { +public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter implements BaseAdapter { public static final @NotNull Class CRAFT_META_ITEM = ReflectionUtil.getClass("org.bukkit.craftbukkit.inventory.CraftMetaItem"); public static final @NotNull Field CUSTOM_TAG = ReflectionUtil.getField("customTag", CRAFT_META_ITEM); diff --git a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/command/CommandMapAdapter.java b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/command/CommandMapAdapter.java new file mode 100644 index 00000000..94ac9b3a --- /dev/null +++ b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/command/CommandMapAdapter.java @@ -0,0 +1,32 @@ +package com.wizardlybump17.wlib.adapter.v1_20_R4.command; + +import com.wizardlybump17.wlib.adapter.v1_20_R4.BaseAdapter; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class CommandMapAdapter extends com.wizardlybump17.wlib.adapter.command.CommandMapAdapter implements BaseAdapter { + + @Override + public @NotNull CommandMap getCommandMap() { + return Bukkit.getCommandMap(); + } + + @Override + public void unregisterCommand(@NotNull String command) { + Map commands = getCommands(); + CommandMap commandMap = getCommandMap(); + + Command removed = commands.remove(command); + if (removed != null) + removed.unregister(commandMap); + } + + @Override + public @NotNull Map getCommands() { + return getCommandMap().getKnownCommands(); + } +} diff --git a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/player/PlayerAdapter.java b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/player/PlayerAdapter.java index 11318aa5..71fe32ee 100644 --- a/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/player/PlayerAdapter.java +++ b/versions/v1_20_R4/src/main/java/com/wizardlybump17/wlib/adapter/v1_20_R4/player/PlayerAdapter.java @@ -1,5 +1,6 @@ package com.wizardlybump17.wlib.adapter.v1_20_R4.player; +import com.wizardlybump17.wlib.adapter.v1_20_R4.BaseAdapter; import com.wizardlybump17.wlib.util.ReflectionUtil; import lombok.NonNull; import org.bukkit.conversations.Conversation; @@ -13,7 +14,7 @@ import java.util.List; import java.util.function.Predicate; -public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter { +public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter implements BaseAdapter { public static final @NonNull Field CONVERSATION_TRACKER = ReflectionUtil.getField("conversationTracker", CraftPlayer.class); public static final @NonNull Field CONVERSATION_QUEUE = ReflectionUtil.getField("conversationQueue", ConversationTracker.class); diff --git a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/AttributeAdapter.java b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/AttributeAdapter.java index 6f0a7eae..fe6590ce 100644 --- a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/AttributeAdapter.java +++ b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/AttributeAdapter.java @@ -10,7 +10,7 @@ import java.util.*; -public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter { +public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter implements BaseAdapter { @Override public Attribute getAttribute(@NotNull String name) { diff --git a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/BaseAdapter.java b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/BaseAdapter.java new file mode 100644 index 00000000..63cac7d5 --- /dev/null +++ b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/BaseAdapter.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.adapter.v1_21_R1; + +import com.wizardlybump17.wlib.adapter.Adapter; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface BaseAdapter extends Adapter { + + @Override + default @NotNull Set getMinecraftVersions() { + return Set.of("1.21", "1.21.1"); + } +} diff --git a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/ItemAdapter.java b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/ItemAdapter.java index f03ab120..8645b097 100644 --- a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/ItemAdapter.java +++ b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/ItemAdapter.java @@ -13,7 +13,7 @@ import java.lang.reflect.Field; import java.util.*; -public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter { +public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter implements BaseAdapter { public static final @NotNull Class CRAFT_META_ITEM = ReflectionUtil.getClass("org.bukkit.craftbukkit.inventory.CraftMetaItem"); public static final @NotNull Field CUSTOM_TAG = ReflectionUtil.getField("customTag", CRAFT_META_ITEM); diff --git a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/command/CommandMapAdapter.java b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/command/CommandMapAdapter.java new file mode 100644 index 00000000..733af580 --- /dev/null +++ b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/command/CommandMapAdapter.java @@ -0,0 +1,32 @@ +package com.wizardlybump17.wlib.adapter.v1_21_R1.command; + +import com.wizardlybump17.wlib.adapter.v1_21_R1.BaseAdapter; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class CommandMapAdapter extends com.wizardlybump17.wlib.adapter.command.CommandMapAdapter implements BaseAdapter { + + @Override + public @NotNull CommandMap getCommandMap() { + return Bukkit.getCommandMap(); + } + + @Override + public void unregisterCommand(@NotNull String command) { + Map commands = getCommands(); + CommandMap commandMap = getCommandMap(); + + Command removed = commands.remove(command); + if (removed != null) + removed.unregister(commandMap); + } + + @Override + public @NotNull Map getCommands() { + return getCommandMap().getKnownCommands(); + } +} diff --git a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/player/PlayerAdapter.java b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/player/PlayerAdapter.java index d0177d11..555178f6 100644 --- a/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/player/PlayerAdapter.java +++ b/versions/v1_21_R1/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R1/player/PlayerAdapter.java @@ -1,5 +1,6 @@ package com.wizardlybump17.wlib.adapter.v1_21_R1.player; +import com.wizardlybump17.wlib.adapter.v1_21_R1.BaseAdapter; import com.wizardlybump17.wlib.util.ReflectionUtil; import lombok.NonNull; import org.bukkit.conversations.Conversation; @@ -13,7 +14,7 @@ import java.util.List; import java.util.function.Predicate; -public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter { +public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter implements BaseAdapter { public static final @NonNull Field CONVERSATION_TRACKER = ReflectionUtil.getField("conversationTracker", CraftPlayer.class); public static final @NonNull Field CONVERSATION_QUEUE = ReflectionUtil.getField("conversationQueue", ConversationTracker.class); diff --git a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/AttributeAdapter.java b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/AttributeAdapter.java index 61f449eb..6395ce15 100644 --- a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/AttributeAdapter.java +++ b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/AttributeAdapter.java @@ -10,7 +10,7 @@ import java.util.*; -public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter { +public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter implements BaseAdapter { public static final @NotNull Map ATTRIBUTES; diff --git a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/BaseAdapter.java b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/BaseAdapter.java new file mode 100644 index 00000000..5aff8ce7 --- /dev/null +++ b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/BaseAdapter.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.adapter.v1_21_R3; + +import com.wizardlybump17.wlib.adapter.Adapter; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface BaseAdapter extends Adapter { + + @Override + default @NotNull Set getMinecraftVersions() { + return Set.of("1.21.4"); + } +} diff --git a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/ItemAdapter.java b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/ItemAdapter.java index 742ad62e..60f773dd 100644 --- a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/ItemAdapter.java +++ b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/ItemAdapter.java @@ -13,7 +13,7 @@ import java.lang.reflect.Field; import java.util.*; -public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter { +public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter implements BaseAdapter { public static final @NotNull Class CRAFT_META_ITEM = ReflectionUtil.getClass("org.bukkit.craftbukkit.inventory.CraftMetaItem"); public static final @NotNull Field CUSTOM_TAG = ReflectionUtil.getField("customTag", CRAFT_META_ITEM); diff --git a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/command/CommandMapAdapter.java b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/command/CommandMapAdapter.java new file mode 100644 index 00000000..cb84658e --- /dev/null +++ b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/command/CommandMapAdapter.java @@ -0,0 +1,38 @@ +package com.wizardlybump17.wlib.adapter.v1_21_R3.command; + +import com.wizardlybump17.wlib.adapter.v1_21_R3.BaseAdapter; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Set; + +public class CommandMapAdapter extends com.wizardlybump17.wlib.adapter.command.CommandMapAdapter implements BaseAdapter { + + @Override + public @NotNull CommandMap getCommandMap() { + return Bukkit.getCommandMap(); + } + + @Override + public void unregisterCommand(@NotNull String command) { + Map commands = getCommands(); + CommandMap commandMap = getCommandMap(); + + Command removed = commands.remove(command); + if (removed != null) + removed.unregister(commandMap); + } + + @Override + public @NotNull Map getCommands() { + return getCommandMap().getKnownCommands(); + } + + @Override + public @NotNull Set getMinecraftVersions() { + return Set.of("1.20.6"); + } +} diff --git a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/player/PlayerAdapter.java b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/player/PlayerAdapter.java index bade240a..b0c67d71 100644 --- a/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/player/PlayerAdapter.java +++ b/versions/v1_21_R3/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R3/player/PlayerAdapter.java @@ -1,5 +1,6 @@ package com.wizardlybump17.wlib.adapter.v1_21_R3.player; +import com.wizardlybump17.wlib.adapter.v1_21_R3.BaseAdapter; import com.wizardlybump17.wlib.util.ReflectionUtil; import lombok.NonNull; import org.bukkit.conversations.Conversation; @@ -13,7 +14,7 @@ import java.util.List; import java.util.function.Predicate; -public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter { +public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter implements BaseAdapter { public static final @NonNull Field CONVERSATION_TRACKER = ReflectionUtil.getField("conversationTracker", CraftPlayer.class); public static final @NonNull Field CONVERSATION_QUEUE = ReflectionUtil.getField("conversationQueue", ConversationTracker.class); diff --git a/versions/v1_21_R5/build.gradle.kts b/versions/v1_21_R5/build.gradle.kts index 6961f45c..b082471f 100644 --- a/versions/v1_21_R5/build.gradle.kts +++ b/versions/v1_21_R5/build.gradle.kts @@ -4,7 +4,7 @@ plugins { apply(plugin = "io.papermc.paperweight.userdev") -val paper = "1.21.7-R0.1-SNAPSHOT" +val paper = "1.21.8-R0.1-SNAPSHOT" val jetbrainsAnnotations = "26.0.2" dependencies { diff --git a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/AttributeAdapter.java b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/AttributeAdapter.java index 634e10bd..5dc3fefa 100644 --- a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/AttributeAdapter.java +++ b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/AttributeAdapter.java @@ -10,7 +10,7 @@ import java.util.*; -public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter { +public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter implements BaseAdapter { public static final @NotNull Map ATTRIBUTES; diff --git a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/BaseAdapter.java b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/BaseAdapter.java new file mode 100644 index 00000000..0fac3342 --- /dev/null +++ b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/BaseAdapter.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.adapter.v1_21_R5; + +import com.wizardlybump17.wlib.adapter.Adapter; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface BaseAdapter extends Adapter { + + @Override + default @NotNull Set getMinecraftVersions() { + return Set.of("1.21.6", "1.21.7"); + } +} diff --git a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/ItemAdapter.java b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/ItemAdapter.java index 2e6d606d..f5f9bf24 100644 --- a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/ItemAdapter.java +++ b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/ItemAdapter.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; import java.util.*; -public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter { +public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter implements BaseAdapter { public static final @NotNull Class CRAFT_META_ITEM = ReflectionUtil.getClass("org.bukkit.craftbukkit.inventory.CraftMetaItem"); public static final @NotNull Field CUSTOM_TAG = ReflectionUtil.getField("customTag", CRAFT_META_ITEM); diff --git a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/command/CommandMapAdapter.java b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/command/CommandMapAdapter.java new file mode 100644 index 00000000..6a575088 --- /dev/null +++ b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/command/CommandMapAdapter.java @@ -0,0 +1,32 @@ +package com.wizardlybump17.wlib.adapter.v1_21_R5.command; + +import com.wizardlybump17.wlib.adapter.v1_21_R5.BaseAdapter; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class CommandMapAdapter extends com.wizardlybump17.wlib.adapter.command.CommandMapAdapter implements BaseAdapter { + + @Override + public @NotNull CommandMap getCommandMap() { + return Bukkit.getCommandMap(); + } + + @Override + public void unregisterCommand(@NotNull String command) { + Map commands = getCommands(); + CommandMap commandMap = getCommandMap(); + + Command removed = commands.remove(command); + if (removed != null) + removed.unregister(commandMap); + } + + @Override + public @NotNull Map getCommands() { + return getCommandMap().getKnownCommands(); + } +} diff --git a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/player/PlayerAdapter.java b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/player/PlayerAdapter.java index 38cb57f7..6943fb3b 100644 --- a/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/player/PlayerAdapter.java +++ b/versions/v1_21_R5/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_R5/player/PlayerAdapter.java @@ -1,5 +1,6 @@ package com.wizardlybump17.wlib.adapter.v1_21_R5.player; +import com.wizardlybump17.wlib.adapter.v1_21_R5.BaseAdapter; import com.wizardlybump17.wlib.util.ReflectionUtil; import org.bukkit.conversations.Conversation; import org.bukkit.craftbukkit.conversations.ConversationTracker; @@ -13,7 +14,7 @@ import java.util.List; import java.util.function.Predicate; -public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter { +public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter implements BaseAdapter { public static final @NotNull Field CONVERSATION_TRACKER = ReflectionUtil.getField("conversationTracker", CraftPlayer.class); public static final @NotNull Field CONVERSATION_QUEUE = ReflectionUtil.getField("conversationQueue", ConversationTracker.class); diff --git a/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/AttributeAdapter.java b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/AttributeAdapter.java index cab4d976..3d45bb2c 100644 --- a/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/AttributeAdapter.java +++ b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/AttributeAdapter.java @@ -10,7 +10,7 @@ import java.util.*; -public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter { +public class AttributeAdapter extends com.wizardlybump17.wlib.adapter.AttributeAdapter implements BaseAdapter { public static final @NotNull Map ATTRIBUTES; diff --git a/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/BaseAdapter.java b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/BaseAdapter.java new file mode 100644 index 00000000..8f70db96 --- /dev/null +++ b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/BaseAdapter.java @@ -0,0 +1,14 @@ +package com.wizardlybump17.wlib.adapter.v1_21_r7; + +import com.wizardlybump17.wlib.adapter.Adapter; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface BaseAdapter extends Adapter { + + @Override + default @NotNull Set getMinecraftVersions() { + return Set.of("1.21.6", "1.21.7"); + } +} diff --git a/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/ItemAdapter.java b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/ItemAdapter.java index 2b9eb1b9..c07678ff 100644 --- a/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/ItemAdapter.java +++ b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/ItemAdapter.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; import java.util.*; -public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter { +public class ItemAdapter extends com.wizardlybump17.wlib.adapter.ItemAdapter implements BaseAdapter { public static final @NotNull Class CRAFT_META_ITEM = ReflectionUtil.getClass("org.bukkit.craftbukkit.inventory.CraftMetaItem"); public static final @NotNull Field CUSTOM_TAG = ReflectionUtil.getField("customTag", CRAFT_META_ITEM); diff --git a/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/player/PlayerAdapter.java b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/player/PlayerAdapter.java index d76adb40..16b8e88b 100644 --- a/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/player/PlayerAdapter.java +++ b/versions/v1_21_r7/src/main/java/com/wizardlybump17/wlib/adapter/v1_21_r7/player/PlayerAdapter.java @@ -1,5 +1,6 @@ package com.wizardlybump17.wlib.adapter.v1_21_r7.player; +import com.wizardlybump17.wlib.adapter.v1_21_r7.BaseAdapter; import com.wizardlybump17.wlib.util.ReflectionUtil; import org.bukkit.conversations.Conversation; import org.bukkit.craftbukkit.conversations.ConversationTracker; @@ -13,7 +14,7 @@ import java.util.List; import java.util.function.Predicate; -public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter { +public class PlayerAdapter extends com.wizardlybump17.wlib.adapter.player.PlayerAdapter implements BaseAdapter { public static final @NotNull Field CONVERSATION_TRACKER = ReflectionUtil.getField("conversationTracker", CraftPlayer.class); public static final @NotNull Field CONVERSATION_QUEUE = ReflectionUtil.getField("conversationQueue", ConversationTracker.class);