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

cloud.commandframework.CommandHelpHandler Maven / Gradle / Ivy

There is a newer version: 1.8.4
Show newest version
//
// MIT License
//
// Copyright (c) 2020 Alexander Söderberg & Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
package cloud.commandframework;

import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.StaticArgument;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;

public final class CommandHelpHandler {

    private final CommandManager commandManager;

    CommandHelpHandler(final @NonNull CommandManager commandManager) {
        this.commandManager = commandManager;
    }

    /**
     * Get exact syntax hints for all commands
     *
     * @return Syntax hints for all registered commands, order in lexicographical order
     */
    public @NonNull List<@NonNull VerboseHelpEntry> getAllCommands() {
        final List> syntaxHints = new ArrayList<>();
        for (final Command command : this.commandManager.getCommands()) {
            final List> arguments = command.getArguments();
            final String description = command.getCommandMeta().getOrDefault("description", "");
            syntaxHints.add(new VerboseHelpEntry<>(
                    command,
                    this.commandManager.getCommandSyntaxFormatter()
                            .apply(arguments, null),
                    description
            ));
        }
        syntaxHints.sort(Comparator.comparing(VerboseHelpEntry::getSyntaxString));
        return syntaxHints;
    }

    /**
     * Get a list of the longest shared command chains of all commands.
     * If there are two commands "foo bar 1" and "foo bar 2", this would
     * then return "foo bar 1|2"
     *
     * @return Longest shared command chains
     */
    public @NonNull List<@NonNull String> getLongestSharedChains() {
        final List chains = new ArrayList<>();
        this.commandManager.getCommandTree().getRootNodes().forEach(node ->
                chains.add(Objects.requireNonNull(node.getValue())
                        .getName() + this.commandManager
                        .getCommandSyntaxFormatter()
                        .apply(
                                Collections
                                        .emptyList(),
                                node
                        )));
        chains.sort(String::compareTo);
        return chains;
    }

    /**
     * Query for help
     *
     * @param query Query string
     * @return Help topic, will return an empty {@link IndexHelpTopic} if no results were found
     */
    public @NonNull HelpTopic queryHelp(final @NonNull String query) {
        return this.queryHelp(null, query);
    }

    /**
     * Query for help
     *
     * @param recipient The recipient of this help query to check permissions against (if Non-Null)
     * @param query     Query string
     * @return Help topic, will return an empty {@link IndexHelpTopic} if no results were found
     */
    public @NonNull HelpTopic queryHelp(
            final @Nullable C recipient,
            final @NonNull String query
    ) {
        final List> commands = this.getAllCommands();
        commands.removeIf(command -> recipient != null && !this.commandManager.hasPermission(
                recipient,
                command.getCommand().getCommandPermission()
        ));
        if (query.replace(" ", "").isEmpty()) {
            return new IndexHelpTopic<>(commands);
        }

        final String[] queryFragments = query.split(" ");
        final String rootFragment = queryFragments[0];

        /* Determine which command we are querying for */
        final List> availableCommands = new LinkedList<>();
        final Set availableCommandLabels = new HashSet<>();

        boolean exactMatch = false;

        for (final VerboseHelpEntry entry : commands) {
            final Command command = entry.getCommand();
            @SuppressWarnings("unchecked") final StaticArgument staticArgument = (StaticArgument) command.getArguments()
                    .get(0);
            for (final String alias : staticArgument.getAliases()) {
                if (alias.toLowerCase(Locale.ENGLISH).startsWith(rootFragment.toLowerCase(Locale.ENGLISH))) {
                    availableCommands.add(command);
                    availableCommandLabels.add(staticArgument.getName());
                    break;
                }
            }

            for (final String alias : staticArgument.getAliases()) {
                if (alias.equalsIgnoreCase(rootFragment)) {
                    exactMatch = true;
                    break;
                }
            }

            if (rootFragment.equalsIgnoreCase(staticArgument.getName())) {
                availableCommandLabels.clear();
                availableCommands.clear();
                availableCommandLabels.add(staticArgument.getName());
                availableCommands.add(command);
                break;
            }
        }

        /* No command found, return all possible commands */
        if (availableCommands.isEmpty()) {
            return new IndexHelpTopic<>(Collections.emptyList());
        } else if (!exactMatch || availableCommandLabels.size() > 1) {
            final List> syntaxHints = new ArrayList<>();
            for (final Command command : availableCommands) {
                final List> arguments = command.getArguments();
                final String description = command.getCommandMeta().getOrDefault("description", "");
                syntaxHints.add(new VerboseHelpEntry<>(
                        command,
                        this.commandManager.getCommandSyntaxFormatter()
                                .apply(arguments, null),
                        description
                ));
            }
            syntaxHints.sort(Comparator.comparing(VerboseHelpEntry::getSyntaxString));
            syntaxHints.removeIf(command -> recipient != null
                    && !this.commandManager.hasPermission(recipient, command.getCommand().getCommandPermission()));
            return new IndexHelpTopic<>(syntaxHints);
        }

        /* Traverse command to find the most specific help topic */
        final CommandTree.Node> node = this.commandManager.getCommandTree()
                .getNamedNode(availableCommandLabels.iterator()
                        .next());

        final List> traversedNodes = new LinkedList<>();
        CommandTree.Node> head = node;
        int index = 0;

        outer:
        while (head != null) {
            ++index;
            traversedNodes.add(head.getValue());

            if (head.getValue() != null && head.getValue().getOwningCommand() != null) {
                if (head.isLeaf() || index == queryFragments.length) {
                    if (recipient == null || this.commandManager.hasPermission(recipient, head.getValue()
                            .getOwningCommand()
                            .getCommandPermission())) {
                        return new VerboseHelpTopic<>(head.getValue().getOwningCommand());
                    }
                }
            }

            if (head.getChildren().size() == 1) {
                head = head.getChildren().get(0);
            } else {
                if (index < queryFragments.length) {
                    /* We might still be able to match an argument */
                    for (final CommandTree.Node> child : head.getChildren()) {
                        @SuppressWarnings("unchecked") final StaticArgument childArgument = (StaticArgument) child
                                .getValue();
                        if (childArgument == null) {
                            continue;
                        }
                        for (final String childAlias : childArgument.getAliases()) {
                            if (childAlias.equalsIgnoreCase(queryFragments[index])) {
                                head = child;
                                continue outer;
                            }
                        }
                    }
                }
                final String currentDescription = this.commandManager.getCommandSyntaxFormatter().apply(traversedNodes, null);
                /* Attempt to parse the longest possible description for the children */
                final List childSuggestions = new LinkedList<>();
                for (final CommandTree.Node> child : head.getChildren()) {
                    final List> traversedNodesSub = new LinkedList<>(traversedNodes);
                    if (recipient == null
                            || child.getValue() == null
                            || child.getValue().getOwningCommand() == null
                            || this.commandManager.hasPermission(
                            recipient,
                            child.getValue().getOwningCommand().getCommandPermission()
                    )) {
                        traversedNodesSub.add(child.getValue());
                        childSuggestions.add(this.commandManager.getCommandSyntaxFormatter().apply(traversedNodesSub, child));
                    }
                }
                return new MultiHelpTopic<>(currentDescription, childSuggestions);
            }
        }

        return new IndexHelpTopic<>(Collections.emptyList());
    }

    /**
     * Something that can be returned as the result of a help query
     * 

* Implementations: *

    *
  • {@link IndexHelpTopic}
  • *
  • {@link VerboseHelpTopic}
  • *
  • {@link MultiHelpTopic}
  • *
* * @param Command sender type */ public interface HelpTopic { } public static final class VerboseHelpEntry { private final Command command; private final String syntaxString; private final String description; private VerboseHelpEntry( final @NonNull Command command, final @NonNull String syntaxString, final @NonNull String description ) { this.command = command; this.syntaxString = syntaxString; this.description = description; } /** * Get the command * * @return Command */ public @NonNull Command getCommand() { return this.command; } /** * Get the syntax string * * @return Syntax string */ public @NonNull String getSyntaxString() { return this.syntaxString; } /** * Get the command description * * @return Command description */ public @NonNull String getDescription() { return this.description; } } /** * Index of available commands * * @param Command sender type */ public static final class IndexHelpTopic implements HelpTopic { private final List> entries; private IndexHelpTopic(final @NonNull List<@NonNull VerboseHelpEntry> entries) { this.entries = entries; } /** * Get help entries * * @return Entries */ public @NonNull List<@NonNull VerboseHelpEntry> getEntries() { return this.entries; } /** * Check if the help topic is entry * * @return {@code true} if the topic is entry, else {@code false} */ public boolean isEmpty() { return this.getEntries().isEmpty(); } } /** * Verbose information about a specific {@link Command} * * @param Command sender type */ public static final class VerboseHelpTopic implements HelpTopic { private final Command command; private final String description; private VerboseHelpTopic(final @NonNull Command command) { this.command = command; final String shortDescription = command.getCommandMeta().getOrDefault("description", "No description"); this.description = command.getCommandMeta().getOrDefault("long-description", shortDescription); } /** * Get the command * * @return Command */ public @NonNull Command getCommand() { return this.command; } /** * Get the command description * * @return Command description */ public @NonNull String getDescription() { return this.description; } } /** * Help topic with multiple semi-verbose command descriptions * * @param Command sender type */ public static final class MultiHelpTopic implements HelpTopic { private final String longestPath; private final List childSuggestions; private MultiHelpTopic( final @NonNull String longestPath, final @NonNull List<@NonNull String> childSuggestions ) { this.longestPath = longestPath; this.childSuggestions = childSuggestions; } /** * Get the longest shared path * * @return Longest path */ public @NonNull String getLongestPath() { return this.longestPath; } /** * Get syntax hints for the node's children * * @return Child suggestions */ public @NonNull List<@NonNull String> getChildSuggestions() { return this.childSuggestions; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy