CommandWithSubcommands

This commit is contained in:
Risto Lahtela 2020-05-08 17:54:48 +03:00
parent ba96fc3d90
commit a3015db57c
6 changed files with 286 additions and 4 deletions

View File

@ -27,6 +27,19 @@ public interface CMDSender {
boolean hasPermission(String permission);
default boolean hasAllPermissionsFor(Subcommand subcommand) {
return !isMissingPermissionsFor(subcommand);
}
default boolean isMissingPermissionsFor(Subcommand subcommand) {
for (String permission : subcommand.getRequiredPermissions()) {
if (!hasPermission(permission)) {
return true;
}
}
return false;
}
Optional<UUID> getUUID();
void send(String message);

View File

@ -0,0 +1,147 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.commands.use;
import com.djrapitops.plan.commands.Arguments;
import com.djrapitops.plugin.command.ColorScheme;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
public class CommandWithSubcommands extends Subcommand {
private final List<Subcommand> subcommands;
private BiConsumer<CMDSender, Arguments> fallback;
private CommandWithSubcommands() {
subcommands = new ArrayList<>();
}
public static Builder builder() {
return new Builder();
}
public void onHelp(CMDSender sender, Arguments arguments) {
MessageBuilder message = sender.buildMessage()
.addPart("Header").newLine().newLine();
new HelpFormatter(/* TODO */new ColorScheme("§2", "§7", "§f"), getPrimaryAlias(),
subcommands.stream().filter(sender::hasAllPermissionsFor).collect(Collectors.toList()))
.addSubcommands(message);
message.newLine().addPart("Footer")
.send();
}
public void onCommand(CMDSender sender, Arguments arguments) {
if (sender.isMissingPermissionsFor(this)) {
sender.send(/* TODO */"NO PERMISSION");
return;
}
Optional<String> gotAlias = arguments.get(0);
if (gotAlias.isPresent()) {
String alias = gotAlias.get();
if ("help".equals(alias)) {
onHelp(sender, arguments);
} else {
for (Subcommand subcommand : subcommands) {
if (subcommand.getAliases().contains(alias)) {
if (sender.isMissingPermissionsFor(subcommand)) {
sender.send(/* TODO */"NO PERMISSION");
continue;
}
subcommand.getExecutor().accept(sender, arguments.removeFirst());
break;
}
}
}
}
// Use fallback if no command is found.
fallback.accept(sender, arguments);
}
public List<String> onTabComplete(CMDSender sender, Arguments arguments) {
Optional<String> gotAlias = arguments.get(0);
List<String> options = new ArrayList<>();
if (gotAlias.isPresent()) {
for (Subcommand subcommand : subcommands) {
if (sender.isMissingPermissionsFor(subcommand)) {
continue;
}
for (String alias : subcommand.getAliases()) {
if (alias.startsWith(gotAlias.get())) {
options.add(alias);
}
}
}
} else {
for (Subcommand subcommand : subcommands) {
options.add(subcommand.getPrimaryAlias());
}
}
Collections.sort(options);
return options;
}
public static class Builder extends Subcommand.Builder {
private final CommandWithSubcommands command;
private Builder() {
this(new CommandWithSubcommands());
}
private Builder(CommandWithSubcommands command) {
super(command);
this.command = command;
}
public Builder subcommand(Subcommand subcommand) {
command.subcommands.add(subcommand);
return this;
}
public Builder fallback(BiConsumer<CMDSender, Arguments> executor) {
command.fallback = executor;
return this;
}
public Builder fallbackOrHelpFallback(
BiPredicate<CMDSender, Arguments> canBeUsed,
BiConsumer<CMDSender, Arguments> executor
) {
return fallback((sender, arguments) -> {
if (canBeUsed.test(sender, arguments)) {
executor.accept(sender, arguments);
} else {
command.onHelp(sender, arguments);
}
});
}
public CommandWithSubcommands build() {
super.build();
if (command.fallback == null) fallback(command::onHelp);
onCommand(command::onCommand);
onTabComplete(command::onTabComplete);
return command;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.commands.use;
import com.djrapitops.plugin.command.ColorScheme;
import org.apache.commons.text.TextStringBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class HelpFormatter {
private final ColorScheme colors;
private final String mainCommand;
private final List<Subcommand> subcommands;
public HelpFormatter(
ColorScheme colors, String mainCommand, List<Subcommand> subcommands
) {
this.colors = colors;
this.mainCommand = mainCommand;
this.subcommands = subcommands;
}
public MessageBuilder addSubcommands(MessageBuilder message) {
for (Subcommand subcommand : subcommands) {
String alias = subcommand.getPrimaryAlias();
message.addPart(colors.getMainColor() + mainCommand + " " + alias + "---")
.hover(argumentsAndAliases(subcommand.getArguments(), subcommand.getAliases()));
message.addPart(colors.getSecondaryColor() + subcommand.getDescription())
.hover(subcommand.getInDepthDescription());
message.tabular("---");
}
return message;
}
private List<String> argumentsAndAliases(List<Subcommand.ArgumentDescriptor> descriptors, Set<String> aliases) {
List<String> lines = new ArrayList<>();
lines.add(colors.getTertiaryColor() + "Arguments:");
for (Subcommand.ArgumentDescriptor descriptor : descriptors) {
if (descriptor.isRequired()) {
lines.add(" " + colors.getMainColor() + "§l<" + descriptor.getName() + ">§r " + colors.getSecondaryColor() + descriptor.getDescription());
} else {
lines.add(" " + colors.getMainColor() + "[" + descriptor.getName() + "] " + colors.getSecondaryColor() + descriptor.getDescription());
}
}
lines.add(colors.getTertiaryColor() + "Aliases:" + new TextStringBuilder().appendWithSeparators(aliases, " | ").toString());
return lines;
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.commands.use;
import java.util.Collection;
import java.util.UUID;
import java.util.function.Supplier;
@ -23,7 +24,9 @@ public interface MessageBuilder {
MessageBuilder defineColorCharacter(char colorChar);
MessageBuilder append(String msg);
MessageBuilder addPart(String msg);
MessageBuilder newLine();
MessageBuilder link(String address);
@ -33,6 +36,8 @@ public interface MessageBuilder {
MessageBuilder hover(String... text);
MessageBuilder hover(Collection<String> text);
MessageBuilder indent(int spaces);
MessageBuilder tabular(CharSequence columnSeparator);

View File

@ -27,15 +27,17 @@ public class Subcommand {
private final Set<String> aliases;
private final Set<String> requiredPermissions;
private final List<String> inDepthDescription;
private final List<ArgumentDescriptor> arguments;
private String primaryAlias;
private String description;
private BiConsumer<CMDSender, Arguments> executor;
private BiFunction<CMDSender, Arguments, List<String>> argumentResolver;
private Subcommand() {
Subcommand() {
aliases = new HashSet<>();
requiredPermissions = new HashSet<>();
inDepthDescription = new ArrayList<>();
arguments = new ArrayList<>();
}
public static SubcommandBuilder builder() {
@ -62,6 +64,10 @@ public class Subcommand {
return inDepthDescription;
}
public List<ArgumentDescriptor> getArguments() {
return arguments;
}
public BiConsumer<CMDSender, Arguments> getExecutor() {
return executor;
}
@ -71,9 +77,15 @@ public class Subcommand {
}
public static class Builder implements SubcommandBuilder {
private Subcommand subcommand;
private final Subcommand subcommand;
private Builder() {}
private Builder() {
this.subcommand = new Subcommand();
}
Builder(Subcommand subcommand) {
this.subcommand = subcommand;
}
@Override
public SubcommandBuilder alias(String alias) {
@ -108,6 +120,18 @@ public class Subcommand {
return this;
}
@Override
public SubcommandBuilder requiredArgument(String name, String description) {
subcommand.arguments.add(new ArgumentDescriptor(name, description, true));
return this;
}
@Override
public SubcommandBuilder optionalArgument(String name, String description) {
subcommand.arguments.add(new ArgumentDescriptor(name, description, false));
return this;
}
@Override
public SubcommandBuilder onCommand(BiConsumer<CMDSender, Arguments> executor) {
subcommand.executor = executor;
@ -130,4 +154,27 @@ public class Subcommand {
}
}
public static class ArgumentDescriptor {
private final String name;
private final String description;
private final boolean required;
public ArgumentDescriptor(String name, String description, boolean required) {
this.name = name;
this.description = description;
this.required = required;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean isRequired() {
return required;
}
}
}

View File

@ -39,6 +39,10 @@ public interface SubcommandBuilder {
SubcommandBuilder inDepthDescription(String... lines);
SubcommandBuilder requiredArgument(String name, String description);
SubcommandBuilder optionalArgument(String name, String description);
SubcommandBuilder onCommand(BiConsumer<CMDSender, Arguments> executor);
SubcommandBuilder onTabComplete(BiFunction<CMDSender, Arguments, List<String>> resolver);