# Source code
source: package: co.aikar.commands, class: CommandHelp

    001/*
    002 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
    003 *
    004 *  Permission is hereby granted, free of charge, to any person obtaining
    005 *  a copy of this software and associated documentation files (the
    006 *  "Software"), to deal in the Software without restriction, including
    007 *  without limitation the rights to use, copy, modify, merge, publish,
    008 *  distribute, sublicense, and/or sell copies of the Software, and to
    009 *  permit persons to whom the Software is furnished to do so, subject to
    010 *  the following conditions:
    011 *
    012 *  The above copyright notice and this permission notice shall be
    013 *  included in all copies or substantial portions of the Software.
    014 *
    015 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    016 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    017 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    018 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    019 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    020 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    021 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    022 */
    023
    024package co.aikar.commands;
    025
    026import com.google.common.collect.SetMultimap;
    027
    028import java.util.ArrayList;
    029import java.util.Comparator;
    030import java.util.HashSet;
    031import java.util.Iterator;
    032import java.util.List;
    033import java.util.Set;
    034import java.util.regex.Pattern;
    035import java.util.stream.Collectors;
    036
    037@SuppressWarnings("WeakerAccess")
    038public class CommandHelp {
    039    private final CommandManager manager;
    040    private final CommandIssuer issuer;
    041    private final List<HelpEntry> helpEntries = new ArrayList<>();
    042    private final String commandName;
    043    final String commandPrefix;
    044    private int page = 1;
    045    private int perPage;
    046    List<String> search;
    047    private Set<HelpEntry> selectedEntry = new HashSet<>();
    048    private int totalResults;
    049    private int totalPages;
    050    private boolean lastPage;
    051
    052    public CommandHelp(CommandManager manager, RootCommand rootCommand, CommandIssuer issuer) {
    053        this.manager = manager;
    054        this.issuer = issuer;
    055        this.perPage = manager.defaultHelpPerPage;
    056        this.commandPrefix = manager.getCommandPrefix(issuer);
    057        this.commandName = rootCommand.getCommandName();
    058
    059
    060        SetMultimap<String, RegisteredCommand> subCommands = rootCommand.getSubCommands();
    061        Set<RegisteredCommand> seen = new HashSet<>();
    062
    063        if (!rootCommand.getDefCommand().hasHelpCommand) {
    064            RegisteredCommand defCommand = rootCommand.getDefaultRegisteredCommand();
    065            if (defCommand != null) {
    066                helpEntries.add(new HelpEntry(this, defCommand));
    067                seen.add(defCommand);
    068            }
    069        }
    070
    071        subCommands.entries().forEach(e -> {
    072            String key = e.getKey();
    073            if (key.equals(BaseCommand.DEFAULT) || key.equals(BaseCommand.CATCHUNKNOWN)) {
    074                return;
    075            }
    076
    077            RegisteredCommand regCommand = e.getValue();
    078
    079            if (!regCommand.isPrivate && regCommand.hasPermission(issuer) && !seen.contains(regCommand)) {
    080                this.helpEntries.add(new HelpEntry(this, regCommand));
    081                seen.add(regCommand);
    082            }
    083        });
    084    }
    085
    086    @UnstableAPI // Not sure on this one yet even when API becomes unstable
    087    protected void updateSearchScore(HelpEntry help) {
    088        if (this.search == null || this.search.isEmpty()) {
    089            help.setSearchScore(1);
    090            return;
    091        }
    092        final RegisteredCommand<?> cmd = help.getRegisteredCommand();
    093
    094        int searchScore = 0;
    095        for (String word : this.search) {
    096            Pattern pattern = Pattern.compile(".*" + Pattern.quote(word) + ".*", Pattern.CASE_INSENSITIVE);
    097            for (String subCmd : cmd.registeredSubcommands) {
    098                Pattern subCmdPattern = Pattern.compile(".*" + Pattern.quote(subCmd) + ".*", Pattern.CASE_INSENSITIVE);
    099                if (pattern.matcher(subCmd).matches()) {
    100                    searchScore += 3;
    101                } else if (subCmdPattern.matcher(word).matches()) {
    102                    searchScore++;
    103                }
    104            }
    105
    106
    107            if (pattern.matcher(help.getDescription()).matches()) {
    108                searchScore += 2;
    109            }
    110            if (pattern.matcher(help.getParameterSyntax(issuer)).matches()) {
    111                searchScore++;
    112            }
    113            if (help.getSearchTags() != null && pattern.matcher(help.getSearchTags()).matches()) {
    114                searchScore += 2;
    115            }
    116        }
    117        help.setSearchScore(searchScore);
    118    }
    119
    120    public CommandManager getManager() {
    121        return manager;
    122    }
    123
    124    public boolean testExactMatch(String command) {
    125        selectedEntry.clear();
    126        for (HelpEntry helpEntry : helpEntries) {
    127            if (helpEntry.getCommand().endsWith(" " + command)) {
    128                selectedEntry.add(helpEntry);
    129            }
    130        }
    131        return !selectedEntry.isEmpty();
    132    }
    133
    134    public void showHelp() {
    135        showHelp(issuer);
    136    }
    137
    138    public void showHelp(CommandIssuer issuer) {
    139        CommandHelpFormatter formatter = manager.getHelpFormatter();
    140        if (!selectedEntry.isEmpty()) {
    141            HelpEntry first = ACFUtil.getFirstElement(selectedEntry);
    142            formatter.printDetailedHelpHeader(this, issuer, first);
    143
    144            for (HelpEntry helpEntry : selectedEntry) {
    145                formatter.showDetailedHelp(this, helpEntry);
    146            }
    147
    148            formatter.printDetailedHelpFooter(this, issuer, first);
    149            return;
    150        }
    151
    152        List<HelpEntry> helpEntries = getHelpEntries().stream().filter(HelpEntry::shouldShow).collect(Collectors.toList());
    153        Iterator<HelpEntry> results = helpEntries.stream()
    154                .sorted(Comparator.comparingInt(helpEntry -> helpEntry.getSearchScore() * -1)).iterator();
    155        if (!results.hasNext()) {
    156            issuer.sendMessage(MessageType.ERROR, MessageKeys.NO_COMMAND_MATCHED_SEARCH, "{search}", ACFUtil.join(this.search, " "));
    157            helpEntries = getHelpEntries();
    158            results = helpEntries.iterator();
    159        }
    160        this.totalResults = helpEntries.size();
    161        int min = (this.page - 1) * this.perPage; // TODO: per page configurable?
    162        int max = min + this.perPage;
    163        this.totalPages = (int) Math.ceil((float) totalResults / (float) this.perPage);
    164        int i = 0;
    165        if (min >= totalResults) {
    166            issuer.sendMessage(MessageType.HELP, MessageKeys.HELP_NO_RESULTS);
    167            return;
    168        }
    169
    170        List<HelpEntry> printEntries = new ArrayList<>();
    171        while (results.hasNext()) {
    172            HelpEntry e = results.next();
    173            if (i >= max) {
    174                break;
    175            }
    176            if (i++ < min) {
    177                continue;
    178            }
    179            printEntries.add(e);
    180        }
    181        this.lastPage = max >= totalResults;
    182
    183        if (search == null) {
    184            formatter.showAllResults(this, printEntries);
    185        } else {
    186            formatter.showSearchResults(this, printEntries);
    187        }
    188
    189    }
    190
    191    public List<HelpEntry> getHelpEntries() {
    192        return helpEntries;
    193    }
    194
    195    public void setPerPage(int perPage) {
    196        this.perPage = perPage;
    197    }
    198
    199    public void setPage(int page) {
    200        this.page = page;
    201    }
    202
    203    public void setPage(int page, int perPage) {
    204        this.setPage(page);
    205        this.setPerPage(perPage);
    206    }
    207
    208    public void setSearch(List<String> search) {
    209        this.search = search;
    210        getHelpEntries().forEach(this::updateSearchScore);
    211    }
    212
    213    public CommandIssuer getIssuer() {
    214        return issuer;
    215    }
    216
    217    public String getCommandName() {
    218        return commandName;
    219    }
    220
    221    public String getCommandPrefix() {
    222        return commandPrefix;
    223    }
    224
    225    public int getPage() {
    226        return page;
    227    }
    228
    229    public int getPerPage() {
    230        return perPage;
    231    }
    232
    233    public List<String> getSearch() {
    234        return search;
    235    }
    236
    237    public Set<HelpEntry> getSelectedEntry() {
    238        return selectedEntry;
    239    }
    240
    241    public int getTotalResults() {
    242        return totalResults;
    243    }
    244
    245    public int getTotalPages() {
    246        return totalPages;
    247    }
    248
    249    public boolean isOnlyPage() {
    250        return this.page == 1 && lastPage;
    251    }
    252
    253    public boolean isLastPage() {
    254        return lastPage;
    255    }
    256}