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 extends CommandSender>> getSenderType() {
- return (Class extends CommandSender>>) 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 extends ArgsReader>>) 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 extends ArgsReader>> 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