From 225851310421a2130f5cd16ca52f994257da476c Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 01:42:59 -0700 Subject: [PATCH] Made Dispatcher implement CommandCallable. This simplifies things a bit. --- .../extension/platform/CommandManager.java | 5 +- .../util/command/CommandCallable.java | 24 ++-- .../util/command/CommandMapping.java | 9 ++ .../worldedit/util/command/Dispatcher.java | 34 +----- .../util/command/SimpleDispatcher.java | 104 +++++++++++++----- .../util/command/SimpleDispatcherCommand.java | 96 ---------------- .../util/command/fluent/DispatcherNode.java | 8 +- .../parametric/ParametricCallable.java | 30 +++-- 8 files changed, 126 insertions(+), 184 deletions(-) delete mode 100644 src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index 0d068e687..a3e506bdb 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extension.platform; +import com.google.common.base.Joiner; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.CommandPermissionsException; @@ -29,11 +30,11 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.*; import com.sk89q.worldedit.event.platform.CommandEvent; -import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.internal.command.CommandLoggingHandler; import com.sk89q.worldedit.internal.command.CommandPermissionsHandler; import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter; +import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.InvalidUsageException; import com.sk89q.worldedit.util.command.fluent.CommandGraph; @@ -215,7 +216,7 @@ public final class CommandManager { long start = System.currentTimeMillis(); try { - dispatcher.call(split, locals); + dispatcher.call(Joiner.on(" ").join(split), locals); } catch (CommandPermissionsException e) { actor.printError("You don't have permission to do this."); } catch (InvalidUsageException e) { diff --git a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java index 4c0046ba0..0fd96670d 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java @@ -19,8 +19,8 @@ package com.sk89q.worldedit.util.command; -import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; import java.util.Collection; import java.util.Set; @@ -38,21 +38,23 @@ public interface CommandCallable { Set getValueFlags(); /** - * Execute the command. - * - * @param context the user input - * @throws CommandException thrown on any sort of command exception + * Execute the correct command based on the input. + * + * @param arguments the arguments + * @param locals the locals + * @return the called command, or null if there was no command found + * @throws CommandException thrown on a command error */ - void call(CommandContext context) throws CommandException; + boolean call(String arguments, CommandLocals locals) throws CommandException; /** - * Get a list of suggestions. - * - * @param context the user input + * Get a list of suggestions based on input. + * + * @param arguments the arguments entered up to this point * @return a list of suggestions - * @throws CommandException + * @throws CommandException thrown if there was a parsing error */ - Collection getSuggestions(CommandContext context) throws CommandException; + Collection getSuggestions(String arguments) throws CommandException; /** * Get an object describing this command. diff --git a/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java b/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java index a6f973cb1..6e44bff17 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.util.command; +import java.util.Arrays; /** * Tracks a command registration. @@ -76,4 +77,12 @@ public class CommandMapping { return getCallable().getDescription(); } + @Override + public String toString() { + return "CommandMapping{" + + "aliases=" + Arrays.toString(aliases) + + ", callable=" + callable + + '}'; + } + } diff --git a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java index 90beed347..00aa399e6 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java @@ -19,15 +19,12 @@ package com.sk89q.worldedit.util.command; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandLocals; - import java.util.Collection; /** * Executes a command based on user input. */ -public interface Dispatcher { +public interface Dispatcher extends CommandCallable { /** * Register a command with this dispatcher. @@ -81,33 +78,4 @@ public interface Dispatcher { */ boolean contains(String alias); - /** - * Execute the correct command based on the input. - * - * @param arguments the arguments - * @param locals the locals - * @return the called command, or null if there was no command found - * @throws CommandException thrown on a command error - */ - CommandMapping call(String arguments, CommandLocals locals) throws CommandException; - - /** - * Execute the correct command based on the input. - * - * @param arguments the arguments - * @param locals the locals - * @return the called command, or null if there was no command found - * @throws CommandException thrown on a command error - */ - CommandMapping call(String[] arguments, CommandLocals locals) throws CommandException; - - /** - * Get a list of suggestions based on input. - * - * @param arguments the arguments entered up to this point - * @return a list of suggestions - * @throws CommandException thrown if there was a parsing error - */ - Collection getSuggestions(String arguments) throws CommandException; - } \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java index af379e7b4..888445017 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.util.command; +import com.google.common.base.Joiner; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; @@ -32,6 +33,7 @@ import java.util.*; public class SimpleDispatcher implements Dispatcher { private final Map commands = new HashMap(); + private final SimpleDescription description = new SimpleDescription(); @Override public void register(CommandCallable callable, String... alias) { @@ -82,41 +84,93 @@ public class SimpleDispatcher implements Dispatcher { } @Override - public CommandMapping call(String arguments, CommandLocals locals) throws CommandException { - return call(CommandContext.split(arguments), locals); + public Set getValueFlags() { + return Collections.emptySet(); } @Override - public CommandMapping call(String[] arguments, CommandLocals locals) throws CommandException { - CommandContext dummyContext = new CommandContext(arguments); - CommandMapping mapping = get(dummyContext.getCommand()); - if (mapping != null) { - CommandCallable c = mapping.getCallable(); - CommandContext context = - new CommandContext(arguments, c.getValueFlags(), false, locals); - try { - c.call(context); - } catch (CommandException e) { - e.prependStack(context.getCommand()); - throw e; - } catch (Throwable t) { - throw new WrappedCommandException(t); + public boolean call(String arguments, CommandLocals locals) throws CommandException { + String[] split = CommandContext.split(arguments); + Set aliases = getPrimaryAliases(); + + if (aliases.isEmpty()) { + throw new InvalidUsageException("This command has no sub-commands.", getDescription()); + } else if (split.length != 0) { + String subCommand = split[0]; + CommandMapping mapping = get(subCommand); + String passedArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); + + if (mapping != null) { + try { + mapping.getCallable().call(passedArguments, locals); + } catch (CommandException e) { + e.prependStack(subCommand); + throw e; + } catch (Throwable t) { + throw new WrappedCommandException(t); + } + + return true; } + } - return mapping; + + throw new InvalidUsageException(getSubcommandList(), getDescription()); } @Override public Collection getSuggestions(String arguments) throws CommandException { - CommandContext dummyContext = new CommandContext(arguments); - CommandMapping mapping = get(dummyContext.getCommand()); - if (mapping != null) { - CommandCallable c = mapping.getCallable(); - CommandContext context = - new CommandContext(arguments, c.getValueFlags(), true); - return c.getSuggestions(context); + String[] split = CommandContext.split(arguments); + + if (split.length == 0) { + return getAllAliases(); + } else if (split.length == 1) { + String prefix = split[0]; + List suggestions = new ArrayList(); + + for (String alias : getAllAliases()) { + if (alias.startsWith(prefix)) { + suggestions.add(alias); + } + } + + return suggestions; + } else { + String subCommand = split[0]; + CommandMapping mapping = get(subCommand); + String passedArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); + + if (mapping != null) { + return mapping.getCallable().getSuggestions(passedArguments); + } else { + return Collections.emptyList(); + } } - return new ArrayList(); + } + + @Override + public SimpleDescription getDescription() { + return description; + } + + /** + * Get a list of subcommands for display. + * + * @return a string + */ + private String getSubcommandList() { + Set aliases = getPrimaryAliases(); + + StringBuilder builder = new StringBuilder("Subcommands: "); + for (String alias : getPrimaryAliases()) { + builder.append("\n- ").append(alias); + } + + if (aliases.size() == 1) { + builder.append(" (there is only one)"); + } + + return builder.toString(); } } diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java deleted file mode 100644 index 79b30f064..000000000 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ - -package com.sk89q.worldedit.util.command; - -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; - -import java.util.*; - -/** - * A combination {@link Dispatcher} and {@link CommandCallable} that is backed by - * a {@link SimpleDispatcher}. - * - *

The primary use of this is to make nested commands.

- */ -public class SimpleDispatcherCommand extends SimpleDispatcher implements CommandCallable { - - private final SimpleDescription description = new SimpleDescription(); - - @Override - public Set getValueFlags() { - return Collections.emptySet(); - } - - @Override - public void call(CommandContext context) throws CommandException { - boolean success = context.argsLength() >= 1 && super.call(context.getRemainingString(0), context.getLocals()) != null; - - if (!success) { - Set aliases = getPrimaryAliases(); - - if (aliases.size() == 0) { - throw new InvalidUsageException( - "This command is supposed to have sub-commands, " + - "but it has no sub-commands.", - getDescription()); - } - - StringBuilder builder = new StringBuilder(); - for (String alias : getPrimaryAliases()) { - builder.append("\n- ").append(alias); - } - - if (aliases.size() == 1) { - builder.append(" (there is only one)"); - } - - throw new InvalidUsageException( - "Select one of these subcommand(s):" + builder.toString(), - getDescription()); - } - } - - @Override - public SimpleDescription getDescription() { - return description; - } - - @Override - public Collection getSuggestions(CommandContext context) throws CommandException { - if (context.argsLength() == 0) { - return super.getAllAliases(); - } else if (context.argsLength() == 1 && - context.getSuggestionContext().forLastValue()) { - String prefix = context.getString(0).toLowerCase(); - List suggestions = new ArrayList(); - for (String alias : super.getAllAliases()) { - if (alias.startsWith(prefix)) { - suggestions.add(alias); - } - } - return suggestions; - } - - return super.getSuggestions( - context.argsLength() > 1 ? context.getRemainingString(1) : ""); - } - -} diff --git a/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java b/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java index cf0d93d74..8a4f71b6c 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java +++ b/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java @@ -22,7 +22,6 @@ package com.sk89q.worldedit.util.command.fluent; import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.SimpleDispatcher; -import com.sk89q.worldedit.util.command.SimpleDispatcherCommand; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; /** @@ -58,9 +57,8 @@ public class DispatcherNode { * @return this object */ public DispatcherNode describe(String description) { - if (dispatcher instanceof SimpleDispatcherCommand) { - ((SimpleDispatcherCommand) dispatcher).getDescription() - .setDescription(description); + if (dispatcher instanceof SimpleDispatcher) { + ((SimpleDispatcher) dispatcher).getDescription().setDescription(description); } return this; } @@ -102,7 +100,7 @@ public class DispatcherNode { * @return an object to place sub-commands */ public DispatcherNode group(String... alias) { - SimpleDispatcherCommand command = new SimpleDispatcherCommand(); + SimpleDispatcher command = new SimpleDispatcher(); getDispatcher().register(command, alias); return new DispatcherNode(graph, this, command); } diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java index f97f9b8b1..23e2d1b22 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java @@ -162,7 +162,10 @@ class ParametricCallable implements CommandCallable { } @Override - public void call(CommandContext context) throws CommandException { + public boolean call(String stringArguments, CommandLocals locals) throws CommandException { + String[] split = CommandContext.split(stringArguments); + CommandContext context = new CommandContext(split, getValueFlags(), false, locals); + Object[] args = new Object[parameters.length]; ContextArgumentStack arguments = new ContextArgumentStack(context); ParameterData parameter = null; @@ -175,15 +178,15 @@ class ParametricCallable implements CommandCallable { handlers.add(handler); handler.preProcess(object, method, parameters, context); } - + // Collect parameters for (int i = 0; i < parameters.length; i++) { parameter = parameters[i]; - + if (mayConsumeArguments(i, arguments)) { // Parse the user input into a method argument ArgumentStack usedArguments = getScopedContext(parameter, arguments); - + try { args[i] = parameter.getBinding().bind(parameter, usedArguments, false); } catch (MissingParameterException e) { @@ -198,7 +201,7 @@ class ParametricCallable implements CommandCallable { args[i] = getDefaultValue(i, arguments); } } - + // Check for unused arguments checkUnconsumed(arguments); @@ -206,7 +209,7 @@ class ParametricCallable implements CommandCallable { for (InvokeHandler handler : handlers) { handler.preInvoke(object, method, parameters, args, context); } - + // Execute! method.invoke(object, args); @@ -227,7 +230,7 @@ class ParametricCallable implements CommandCallable { converter.convert(e.getCause()); } } - + String name = parameter.getName(); throw new InvalidUsageException("For parameter '" + name + "': " @@ -244,25 +247,28 @@ class ParametricCallable implements CommandCallable { } catch (Throwable e) { throw new WrappedCommandException(e); } + + return true; } @Override - public List getSuggestions(CommandContext context) throws CommandException { + public Collection getSuggestions(String stringArguments) throws CommandException { + String[] split = CommandContext.split(stringArguments); + CommandContext context = new CommandContext(split, getValueFlags()); + ContextArgumentStack scoped = new ContextArgumentStack(context); SuggestionContext suggestable = context.getSuggestionContext(); // For /command -f | // For /command -f flag| if (suggestable.forFlag()) { - for (int i = 0; i < parameters.length; i++) { - ParameterData parameter = parameters[i]; - + for (ParameterData parameter : parameters) { if (parameter.getFlag() == suggestable.getFlag()) { String prefix = context.getFlag(parameter.getFlag()); if (prefix == null) { prefix = ""; } - + return parameter.getBinding().getSuggestions(parameter, prefix); } }