All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.guizhanss.guizhanlib.minecraft.commands.AbstractCommand Maven / Gradle / Ivy

package net.guizhanss.guizhanlib.minecraft.commands;

import com.google.common.base.Preconditions;
import lombok.Getter;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * This is a node in a command tree.
 * A node can have 0-1 parent node and 0-n child nodes.
 *
 * @author ybw0014
 */
@SuppressWarnings("ConstantConditions")
@Getter
public abstract class AbstractCommand {
    private final Set subCommands = new HashSet<>();
    private final AbstractCommand parent;
    private final String name;
    private final BiFunction description;
    private final Usage usage;

    /**
     * Creates a new {@link AbstractCommand} instance.
     *
     * @param parent      the parent node.
     * @param name        the name of this node.
     * @param description the description of this node.
     * @param usage       the usage of this node.
     * @param subCommands the sub-commands of this node.
     */
    protected AbstractCommand(@Nullable AbstractCommand parent, @Nonnull String name,
                              @Nonnull BiFunction description,
                              @Nonnull String usage, @Nonnull SubCommand... subCommands) {
        Preconditions.checkArgument(name != null && !name.isEmpty(), "name cannot be null or empty");
        Preconditions.checkArgument(description != null, "description cannot be null");

        this.parent = parent;
        this.name = name;
        this.description = description;
        this.usage = new Usage(usage);
        this.subCommands.addAll(Set.of(subCommands));
    }

    /**
     * Creates a new {@link AbstractCommand} instance, without a parent node.
     *
     * @param name        the name of this node.
     * @param description the description of this node.
     * @param usage       the usage of this node.
     * @param subCommands the sub-commands of this node.
     */
    @ParametersAreNonnullByDefault
    protected AbstractCommand(String name, BiFunction description,
                              String usage, SubCommand... subCommands) {
        this(null, name, description, usage, subCommands);
    }

    /**
     * Adds several {@link SubCommand} to this node.
     *
     * @param subCommands the array of {@link SubCommand} to add.
     * @return This {@link AbstractCommand} instance, for chaining.
     */
    @Nonnull
    public AbstractCommand addSubCommand(@Nonnull SubCommand... subCommands) {
        this.subCommands.addAll(Set.of(subCommands));
        return this;
    }

    /**
     * Check if this node has a parent node.
     *
     * @return Whether this node has a parent node.
     */
    public boolean hasParent() {
        return parent != null;
    }

    /**
     * Check if this node has child nodes.
     *
     * @return Whether this node has child nodes.
     */
    public boolean hasSubCommands() {
        return !subCommands.isEmpty();
    }

    /**
     * The general logic of command execution. This does not contain the real logic of the command.
     *
     * @param sender The {@link CommandSender} of the command.
     * @param args   The arguments of the command.
     */
    @ParametersAreNonnullByDefault
    protected final void onCommandExecute(CommandSender sender, Command command, String label, String[] args) {
        if (!hasSubCommands()) {
            if (getUsage().isValid(args)) {
                onExecute(sender, args);
            } else {
                sendHelp(sender, label);
            }
        } else {
            if (args.length == 0) {
                sendHelp(sender, label);
            } else {
                for (var subCommand : getSubCommands()) {
                    if (subCommand.getName().equalsIgnoreCase(args[0])) {
                        subCommand.onCommandExecute(sender, command, label, Arrays.copyOfRange(args, 1, args.length));
                        return;
                    }
                }
                sendHelp(sender, label);
            }
        }
    }

    /**
     * The general logic of tab complete execution. This does not contain the real logic of the command.
     *
     * @param sender The {@link CommandSender} of the command.
     * @param args   The arguments of the command.
     */
    @Nullable
    @ParametersAreNonnullByDefault
    protected final List onTabCompleteExecute(CommandSender sender, String[] args) {
        if (hasSubCommands()) {
            if (args.length == 1) {
                return getSubCommands().stream().map(AbstractCommand::getName).toList();
            } else {
                for (var subCommand : getSubCommands()) {
                    if (subCommand.getName().equalsIgnoreCase(args[0])) {
                        return subCommand.onTabCompleteExecute(sender, Arrays.copyOfRange(args, 1,
                            args.length));
                    }
                }
                return List.of();
            }
        } else {
            return onTab(sender, args);
        }
    }

    /**
     * Get the full usage of this node.
     * 

* Example: /command subcommand <arg1> [arg2] * * @param label The label of the command. * @return The full usage of this node. */ @Nonnull public String getFullUsage(@Nonnull String label) { final Deque layers = new ArrayDeque<>(); AbstractCommand current = this; while (current != null) { layers.push(current); current = current.getParent(); } String cmd = layers.stream() .map(command -> command instanceof BaseCommand ? label : command.getName()) .collect(Collectors.joining(" ", "/", "")); cmd += " " + getUsage().get(); return cmd; } /** * Send the help message of this node to the {@link CommandSender}. * * @param sender The {@link CommandSender} to send the help message to. * @param label The label of the command. */ @ParametersAreNonnullByDefault protected void sendHelp(CommandSender sender, String label) { if (hasSubCommands()) { for (var subCommand : getSubCommands()) { subCommand.sendHelp(sender, label); } } else { sender.sendMessage(ChatColor.YELLOW + getFullUsage(label) + ChatColor.WHITE + " - " + getDescription().apply(this, sender)); } } // The actual command logic. @ParametersAreNonnullByDefault public abstract void onExecute(CommandSender sender, String[] args); // The actual tab complete logic. @ParametersAreNonnullByDefault public List onTab(CommandSender sender, String[] args) { return List.of(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy