/** * PermissionsEx * Copyright (C) zml and PermissionsEx contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ninja.leaping.permissionsex.util.command; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import ninja.leaping.permissionsex.util.GuavaStartsWithPredicate; import ninja.leaping.permissionsex.util.command.args.ArgumentParseException; import ninja.leaping.permissionsex.util.command.args.CommandArgs; import ninja.leaping.permissionsex.util.command.args.CommandElement; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import static ninja.leaping.permissionsex.util.Translations.t; import static ninja.leaping.permissionsex.util.Translations.untr; /** * Utility methods for handling child commands */ public class ChildCommands { private ChildCommands() {} public static CommandElement args(CommandSpec... children) { Map<String, CommandSpec> mapping = new HashMap<>(); for (CommandSpec child : children) { List<String> aliases = child.getAliases(); if (aliases.isEmpty()) { continue; // Unnamable command -- TODO maybe warn? } final String primaryName = aliases.get(0); if (mapping.containsKey(primaryName)) { continue; // oh well, we're presented with an ordered collection so hopefully whoever is calling us knows what they're doing } mapping.put(primaryName, child); } for (CommandSpec child : children) { List<String> aliases = child.getAliases(); for (int i = 1; i < aliases.size(); ++i) { if (!mapping.containsKey(aliases.get(i))) { mapping.put(aliases.get(i), child); } } } return new ChildCommandElement(mapping); } private static class ChildCommandElement extends CommandElement { private static final AtomicInteger COUNTER = new AtomicInteger(); private final Map<String, CommandSpec> children; private ChildCommandElement(Map<String, CommandSpec> children) { super(untr("child" + COUNTER.getAndIncrement())); this.children = ImmutableMap.copyOf(children); } @Override public void parse(CommandArgs args, CommandContext context) throws ArgumentParseException { super.parse(args, context); CommandSpec spec = context.getOne(getKey().getUntranslated()); spec.parse(args, context); } @Override protected Object parseValue(CommandArgs args) throws ArgumentParseException { final String key = args.next(); if (!children.containsKey(key.toLowerCase())) { throw args.createError(t("Input command %s was not a valid subcommand!", key)); } return children.get(key.toLowerCase()); } @Override public <TextType> List<String> tabComplete(final Commander<TextType> src, CommandArgs args, CommandContext context) { final Optional<String> commandComponent = args.nextIfPresent(); if (commandComponent.isPresent()) { if (args.hasNext()) { CommandSpec child = children.get(commandComponent.get()); if (child != null) { try { child.checkPermission(src); return child.tabComplete(src, args, context); // todo make this more correct } catch (CommandException e) { } } return ImmutableList.of(); } else { return ImmutableList.copyOf(Iterables.filter(filterCommands(src), new GuavaStartsWithPredicate(commandComponent.get()))); } } else { return ImmutableList.copyOf(children.keySet()); } } @Override public <TextType> TextType getUsage(Commander<TextType> context) { List<Object> args = new ArrayList<>(Math.max(0, children.size() * 2 - 1)); Iterable<String> filteredCommands = Iterables.filter(filterCommands(context), input -> { return children.get(input).getAliases().get(0).equals(input); // Restrict to primary aliases in usage }); for (Iterator<String> it = filteredCommands.iterator(); it.hasNext();) { args.add(it.next()); if (it.hasNext()) { args.add("|"); } } return context.fmt().combined(args.toArray()); } private Iterable<String> filterCommands(final Commander<?> src) { return Iterables.filter(children.keySet(), input -> { CommandSpec child = children.get(input); try { child.checkPermission(src); return true; } catch (CommandException ex) { return false; } }); } } public static CommandExecutor executor(CommandElement arg) { return new ChildCommandExecutor(arg.getKey().getUntranslated(), null); } public static CommandExecutor optionalExecutor(CommandElement arg, CommandExecutor fallbackExecutor) { return new ChildCommandExecutor(arg.getKey().getUntranslated(), fallbackExecutor); } private static class ChildCommandExecutor implements CommandExecutor { private final String key; private final CommandExecutor fallbackExecutor; private ChildCommandExecutor(String key, CommandExecutor fallbackExecutor) { this.key = key; this.fallbackExecutor = fallbackExecutor; } @Override public <TextType> void execute(Commander<TextType> src, CommandContext args) throws CommandException { CommandSpec spec = args.getOne(key); if (spec == null) { if (fallbackExecutor != null) { fallbackExecutor.execute(src, args); return; } else { throw new CommandException(t("Invalid subcommand state -- only one command spec must be provided for child arg %s", key)); } } spec.checkPermission(src); spec.getExecutor().execute(src, args); } } }