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; 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.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.CommandLocals;
import com.sk89q.minecraft.util.commands.CommandPermissionsException; 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.WorldEdit;
import com.sk89q.worldedit.command.*; import com.sk89q.worldedit.command.*;
import com.sk89q.worldedit.event.platform.CommandEvent; 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.CommandLoggingHandler;
import com.sk89q.worldedit.internal.command.CommandPermissionsHandler; import com.sk89q.worldedit.internal.command.CommandPermissionsHandler;
import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.internal.command.WorldEditBinding;
import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter; 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.Dispatcher;
import com.sk89q.worldedit.util.command.InvalidUsageException; import com.sk89q.worldedit.util.command.InvalidUsageException;
import com.sk89q.worldedit.util.command.fluent.CommandGraph; import com.sk89q.worldedit.util.command.fluent.CommandGraph;
@ -215,7 +216,7 @@ public final class CommandManager {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
try { try {
dispatcher.call(split, locals); dispatcher.call(Joiner.on(" ").join(split), locals);
} catch (CommandPermissionsException e) { } catch (CommandPermissionsException e) {
actor.printError("You don't have permission to do this."); actor.printError("You don't have permission to do this.");
} catch (InvalidUsageException e) { } catch (InvalidUsageException e) {

View File

@ -19,8 +19,8 @@
package com.sk89q.worldedit.util.command; 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.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
@ -38,21 +38,23 @@ public interface CommandCallable {
Set<Character> getValueFlags(); Set<Character> getValueFlags();
/** /**
* Execute the command. * Execute the correct command based on the input.
* *
* @param context the user input * @param arguments the arguments
* @throws CommandException thrown on any sort of command exception * @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. * Get a list of suggestions based on input.
* *
* @param context the user input * @param arguments the arguments entered up to this point
* @return a list of suggestions * @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. * Get an object describing this command.

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.util.command; package com.sk89q.worldedit.util.command;
import java.util.Arrays;
/** /**
* Tracks a command registration. * Tracks a command registration.
@ -76,4 +77,12 @@ public class CommandMapping {
return getCallable().getDescription(); 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; package com.sk89q.worldedit.util.command;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals;
import java.util.Collection; import java.util.Collection;
/** /**
* Executes a command based on user input. * Executes a command based on user input.
*/ */
public interface Dispatcher { public interface Dispatcher extends CommandCallable {
/** /**
* Register a command with this dispatcher. * Register a command with this dispatcher.
@ -81,33 +78,4 @@ public interface Dispatcher {
*/ */
boolean contains(String alias); 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; 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.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.CommandLocals;
@ -32,6 +33,7 @@ import java.util.*;
public class SimpleDispatcher implements Dispatcher { public class SimpleDispatcher implements Dispatcher {
private final Map<String, CommandMapping> commands = new HashMap<String, CommandMapping>(); private final Map<String, CommandMapping> commands = new HashMap<String, CommandMapping>();
private final SimpleDescription description = new SimpleDescription();
@Override @Override
public void register(CommandCallable callable, String... alias) { public void register(CommandCallable callable, String... alias) {
@ -82,41 +84,93 @@ public class SimpleDispatcher implements Dispatcher {
} }
@Override @Override
public CommandMapping call(String arguments, CommandLocals locals) throws CommandException { public Set<Character> getValueFlags() {
return call(CommandContext.split(arguments), locals); return Collections.emptySet();
} }
@Override @Override
public CommandMapping call(String[] arguments, CommandLocals locals) throws CommandException { public boolean call(String arguments, CommandLocals locals) throws CommandException {
CommandContext dummyContext = new CommandContext(arguments); String[] split = CommandContext.split(arguments);
CommandMapping mapping = get(dummyContext.getCommand()); 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) { if (mapping != null) {
CommandCallable c = mapping.getCallable();
CommandContext context =
new CommandContext(arguments, c.getValueFlags(), false, locals);
try { try {
c.call(context); mapping.getCallable().call(passedArguments, locals);
} catch (CommandException e) { } catch (CommandException e) {
e.prependStack(context.getCommand()); e.prependStack(subCommand);
throw e; throw e;
} catch (Throwable t) { } catch (Throwable t) {
throw new WrappedCommandException(t); throw new WrappedCommandException(t);
} }
return true;
} }
return mapping;
}
throw new InvalidUsageException(getSubcommandList(), getDescription());
} }
@Override @Override
public Collection<String> getSuggestions(String arguments) throws CommandException { public Collection<String> getSuggestions(String arguments) throws CommandException {
CommandContext dummyContext = new CommandContext(arguments); String[] split = CommandContext.split(arguments);
CommandMapping mapping = get(dummyContext.getCommand());
if (mapping != null) { if (split.length == 0) {
CommandCallable c = mapping.getCallable(); return getAllAliases();
CommandContext context = } else if (split.length == 1) {
new CommandContext(arguments, c.getValueFlags(), true); String prefix = split[0];
return c.getSuggestions(context); List<String> suggestions = new ArrayList<String>();
for (String alias : getAllAliases()) {
if (alias.startsWith(prefix)) {
suggestions.add(alias);
} }
return new ArrayList<String>(); }
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();
}
}
}
@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.CommandCallable;
import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.SimpleDispatcher; import com.sk89q.worldedit.util.command.SimpleDispatcher;
import com.sk89q.worldedit.util.command.SimpleDispatcherCommand;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
/** /**
@ -58,9 +57,8 @@ public class DispatcherNode {
* @return this object * @return this object
*/ */
public DispatcherNode describe(String description) { public DispatcherNode describe(String description) {
if (dispatcher instanceof SimpleDispatcherCommand) { if (dispatcher instanceof SimpleDispatcher) {
((SimpleDispatcherCommand) dispatcher).getDescription() ((SimpleDispatcher) dispatcher).getDescription().setDescription(description);
.setDescription(description);
} }
return this; return this;
} }
@ -102,7 +100,7 @@ public class DispatcherNode {
* @return an object to place sub-commands * @return an object to place sub-commands
*/ */
public DispatcherNode group(String... alias) { public DispatcherNode group(String... alias) {
SimpleDispatcherCommand command = new SimpleDispatcherCommand(); SimpleDispatcher command = new SimpleDispatcher();
getDispatcher().register(command, alias); getDispatcher().register(command, alias);
return new DispatcherNode(graph, this, command); return new DispatcherNode(graph, this, command);
} }

View File

@ -162,7 +162,10 @@ class ParametricCallable implements CommandCallable {
} }
@Override @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]; Object[] args = new Object[parameters.length];
ContextArgumentStack arguments = new ContextArgumentStack(context); ContextArgumentStack arguments = new ContextArgumentStack(context);
ParameterData parameter = null; ParameterData parameter = null;
@ -244,19 +247,22 @@ class ParametricCallable implements CommandCallable {
} catch (Throwable e) { } catch (Throwable e) {
throw new WrappedCommandException(e); throw new WrappedCommandException(e);
} }
return true;
} }
@Override @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); ContextArgumentStack scoped = new ContextArgumentStack(context);
SuggestionContext suggestable = context.getSuggestionContext(); SuggestionContext suggestable = context.getSuggestionContext();
// For /command -f | // For /command -f |
// For /command -f flag| // For /command -f flag|
if (suggestable.forFlag()) { if (suggestable.forFlag()) {
for (int i = 0; i < parameters.length; i++) { for (ParameterData parameter : parameters) {
ParameterData parameter = parameters[i];
if (parameter.getFlag() == suggestable.getFlag()) { if (parameter.getFlag() == suggestable.getFlag()) {
String prefix = context.getFlag(parameter.getFlag()); String prefix = context.getFlag(parameter.getFlag());
if (prefix == null) { if (prefix == null) {