Made Dispatcher implement CommandCallable.

This simplifies things a bit.
This commit is contained in:
sk89q 2014-06-28 01:42:59 -07:00
parent f64107c2c0
commit 2258513104
8 changed files with 126 additions and 184 deletions

View File

@ -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) {

View File

@ -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<Character> 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<String> getSuggestions(CommandContext context) throws CommandException;
Collection<String> getSuggestions(String arguments) throws CommandException;
/**
* Get an object describing this command.

View File

@ -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 +
'}';
}
}

View File

@ -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<String> getSuggestions(String arguments) throws CommandException;
}

View File

@ -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<String, CommandMapping> commands = new HashMap<String, CommandMapping>();
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<Character> 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<String> 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<String> 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<String> suggestions = new ArrayList<String>();
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<String>();
}
@Override
public SimpleDescription getDescription() {
return description;
}
/**
* Get a list of subcommands for display.
*
* @return a string
*/
private String getSubcommandList() {
Set<String> 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();
}
}

View File

@ -1,96 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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}.
*
* <p>The primary use of this is to make nested commands.</p>
*/
public class SimpleDispatcherCommand extends SimpleDispatcher implements CommandCallable {
private final SimpleDescription description = new SimpleDescription();
@Override
public Set<Character> 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<String> 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<String> 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<String> suggestions = new ArrayList<String>();
for (String alias : super.getAllAliases()) {
if (alias.startsWith(prefix)) {
suggestions.add(alias);
}
}
return suggestions;
}
return super.getSuggestions(
context.argsLength() > 1 ? context.getRemainingString(1) : "");
}
}

View File

@ -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);
}

View File

@ -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<String> getSuggestions(CommandContext context) throws CommandException {
public Collection<String> 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);
}
}