diff --git a/pom.xml b/pom.xml
index b9d717b8b..89e1d0efc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -355,24 +355,6 @@
-
-
- maven-assembly-plugin
- 2.2-beta-2
-
- ${basedir}/src/main/assembly/default.xml
-
-
-
- release
- package
-
- single
-
-
-
-
-
org.apache.maven.plugins
@@ -396,6 +378,24 @@
+
+
+ maven-assembly-plugin
+ 2.2-beta-2
+
+ ${basedir}/src/main/assembly/default.xml
+
+
+
+ release
+ package
+
+ single
+
+
+
+
+
org.apache.maven.plugins
diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java
index 1e57ffa54..74dfc2e41 100644
--- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java
+++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java
@@ -30,12 +30,12 @@ import java.io.File;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
-class BukkitCommandSender implements Actor {
+public class BukkitCommandSender implements Actor {
private CommandSender sender;
private WorldEditPlugin plugin;
- BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) {
+ public BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) {
checkNotNull(plugin);
checkNotNull(sender);
checkArgument(!(sender instanceof Player), "Cannot wrap a player");
diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
index 9f0beff64..0cdaf470d 100644
--- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
+++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
@@ -156,10 +156,12 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
*/
@Override
public void onDisable() {
- controller.clearSessions();
- controller.getPlatformManager().unregister(server);
- config.unload();
- server.unregisterCommands();
+ if (controller != null) {
+ controller.clearSessions();
+ controller.getPlatformManager().unregister(server);
+ config.unload();
+ server.unregisterCommands();
+ }
this.getServer().getScheduler().cancelTasks(this);
}
diff --git a/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java b/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java
index 31900a9d2..dd3fa00e2 100644
--- a/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java
+++ b/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java
@@ -145,7 +145,7 @@ public class CommandContext {
suggestionContext = SuggestionContext.hangingValue();
// Not a flag?
- if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z]+$")) {
+ if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z\\?]+$")) {
if (!isHanging) {
suggestionContext = SuggestionContext.lastValue();
}
diff --git a/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java b/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java
index eae94c75f..968c31e6a 100644
--- a/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java
+++ b/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java
@@ -19,10 +19,13 @@
package com.sk89q.minecraft.util.commands;
+import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
+import static com.google.common.base.Preconditions.checkNotNull;
+
public class CommandException extends Exception {
private static final long serialVersionUID = 870638193072101739L;
@@ -48,7 +51,16 @@ public class CommandException extends Exception {
commandStack.add(name);
}
- public String toStackString(String prefix, String spacedSuffix) {
+ /**
+ * Gets the command that was called, which will include the sub-command
+ * (i.e. "/br sphere").
+ *
+ * @param prefix the command shebang character (such as "/") -- may be empty
+ * @param spacedSuffix a suffix to put at the end (optional) -- may be null
+ * @return the command that was used
+ */
+ public String getCommandUsed(String prefix, @Nullable String spacedSuffix) {
+ checkNotNull(prefix);
StringBuilder builder = new StringBuilder();
if (prefix != null) {
builder.append(prefix);
@@ -66,7 +78,7 @@ public class CommandException extends Exception {
}
builder.append(spacedSuffix);
}
- return builder.toString();
+ return builder.toString().trim();
}
}
diff --git a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
index 5534df372..e8dd44277 100644
--- a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
+++ b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
@@ -131,7 +131,7 @@ public class RegionCommands {
desc = "Draws a spline through selected points",
help =
"Draws a spline through selected points.\n" +
- "Can only be uesd with convex polyhedral selections.\n" +
+ "Can only be used with convex polyhedral selections.\n" +
"Flags:\n" +
" -h generates only a shell",
flags = "h",
diff --git a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java
index 6198ddb43..0b61d2ff9 100644
--- a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java
+++ b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java
@@ -60,8 +60,8 @@ public class SchematicCommands {
@Command(
aliases = { "load", "l" },
usage = "[format] ",
- desc = "Load a schematic into your clipboard",
- help = "Load a schematic into your clipboard\n" +
+ desc = "Load a file into your clipboard",
+ help = "Load a schematic file into your clipboard\n" +
"Format is a format from \"//schematic formats\"\n" +
"If the format is not provided, WorldEdit will\n" +
"attempt to automatically detect the format of the schematic",
@@ -78,8 +78,8 @@ public class SchematicCommands {
@Command(
aliases = { "save", "s" },
usage = "[format] ",
- desc = "Save a schematic into your clipboard",
- help = "Save a schematic into your clipboard\n" +
+ desc = "Save your clipboard to file",
+ help = "Save your clipboard to file\n" +
"Format is a format from \"//schematic formats\"\n",
min = 1,
max = 2
@@ -93,7 +93,7 @@ public class SchematicCommands {
@Command(
aliases = { "delete", "d" },
usage = "",
- desc = "Delete a schematic from the schematic list",
+ desc = "Delete a saved schematic",
help = "Delete a schematic from the schematic list",
min = 1,
max = 1
@@ -122,7 +122,7 @@ public class SchematicCommands {
@Command(
aliases = {"formats", "listformats", "f"},
- desc = "List available schematic formats",
+ desc = "List available formats",
max = 0
)
@CommandPermissions("worldedit.schematic.formats")
@@ -147,7 +147,7 @@ public class SchematicCommands {
@Command(
aliases = {"list", "all", "ls"},
- desc = "List available schematics",
+ desc = "List saved schematics",
max = 0,
flags = "dn",
help = "List all schematics in the schematics directory\n" +
diff --git a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java
index 7487cd52e..7f596c8e3 100644
--- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java
+++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java
@@ -19,26 +19,44 @@
package com.sk89q.worldedit.command;
-import com.sk89q.minecraft.util.commands.*;
-import com.sk89q.worldedit.*;
+import com.google.common.base.Joiner;
+import com.sk89q.minecraft.util.commands.Command;
+import com.sk89q.minecraft.util.commands.CommandContext;
+import com.sk89q.minecraft.util.commands.CommandPermissions;
+import com.sk89q.minecraft.util.commands.Logging;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.EntityType;
+import com.sk89q.worldedit.LocalConfiguration;
+import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.LocalWorld.KillFlags;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.patterns.Pattern;
import com.sk89q.worldedit.patterns.SingleBlockPattern;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.util.command.CommandCallable;
import com.sk89q.worldedit.util.command.CommandMapping;
-import com.sk89q.worldedit.util.command.Description;
import com.sk89q.worldedit.util.command.Dispatcher;
+import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
import com.sk89q.worldedit.util.command.parametric.Optional;
+import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
+import com.sk89q.worldedit.util.formatting.Style;
+import com.sk89q.worldedit.util.formatting.StyledFragment;
+import com.sk89q.worldedit.util.formatting.component.Code;
+import com.sk89q.worldedit.util.formatting.component.CommandListBox;
+import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
import com.sk89q.worldedit.world.World;
-import java.util.Comparator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT;
@@ -497,59 +515,137 @@ public class UtilityCommands {
help(args, we, actor);
}
- public static void help(CommandContext args, WorldEdit we, Actor actor) {
- final Dispatcher dispatcher = we.getPlatformManager().getCommandManager().getDispatcher();
+ private static CommandMapping detectCommand(Dispatcher dispatcher, String command, boolean isRootLevel) {
+ CommandMapping mapping;
- if (args.argsLength() == 0) {
- SortedSet commands = new TreeSet(new Comparator() {
- @Override
- public int compare(String o1, String o2) {
- final int ret = o1.replaceAll("/", "").compareToIgnoreCase(o2.replaceAll("/", ""));
- if (ret == 0) {
- return o1.compareToIgnoreCase(o2);
- }
- return ret;
- }
- });
- commands.addAll(dispatcher.getPrimaryAliases());
+ // First try the command as entered
+ mapping = dispatcher.get(command);
+ if (mapping != null) {
+ return mapping;
+ }
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (String command : commands) {
- if (!first) {
- sb.append(", ");
- }
-
- sb.append('/');
- sb.append(command);
- first = false;
+ // Then if we're looking at root commands and the user didn't use
+ // any slashes, let's try double slashes and then single slashes.
+ // However, be aware that there exists different single slash
+ // and double slash commands in WorldEdit
+ if (isRootLevel && !command.contains("/")) {
+ mapping = dispatcher.get("//" + command);
+ if (mapping != null) {
+ return mapping;
}
- actor.print(sb.toString());
-
- return;
+ mapping = dispatcher.get("/" + command);
+ if (mapping != null) {
+ return mapping;
+ }
}
- String command = args.getJoinedStrings(0).toLowerCase().replaceAll("^/", "");
- CommandMapping mapping = dispatcher.get(command);
+ return null;
+ }
- if (mapping == null) {
- actor.printError("Unknown command '" + command + "'.");
- return;
+ public static void help(CommandContext args, WorldEdit we, Actor actor) {
+ CommandCallable callable = we.getPlatformManager().getCommandManager().getDispatcher();
+
+ int page = 0;
+ final int perPage = actor instanceof Player ? 8 : 20; // More pages for console
+ int effectiveLength = args.argsLength();
+
+ // Detect page from args
+ try {
+ if (args.argsLength() > 0) {
+ page = args.getInteger(args.argsLength() - 1);
+ if (page <= 0) {
+ page = 1;
+ } else {
+ page--;
+ }
+
+ effectiveLength--;
+ }
+ } catch (NumberFormatException ignored) {
}
- Description description = mapping.getDescription();
+ boolean isRootLevel = true;
+ List visited = new ArrayList();
- if (description.getUsage() != null) {
- actor.printDebug("Usage: " + description.getUsage());
+ // Drill down to the command
+ for (int i = 0; i < effectiveLength; i++) {
+ String command = args.getString(i);
+
+ if (callable instanceof Dispatcher) {
+ // Chop off the beginning / if we're are the root level
+ if (isRootLevel && command.length() > 1 && command.charAt(0) == '/') {
+ command = command.substring(1);
+ }
+
+ CommandMapping mapping = detectCommand((Dispatcher) callable, command, isRootLevel);
+ if (mapping != null) {
+ callable = mapping.getCallable();
+ } else {
+ if (isRootLevel) {
+ actor.printError(String.format("The command '%s' could not be found.", args.getString(i)));
+ return;
+ } else {
+ actor.printError(String.format("The sub-command '%s' under '%s' could not be found.",
+ command, Joiner.on(" ").join(visited)));
+ return;
+ }
+ }
+
+ visited.add(args.getString(i));
+ isRootLevel = false;
+ } else {
+ actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)",
+ Joiner.on(" ").join(visited), command));
+ return;
+ }
}
- if (description.getHelp() != null) {
- actor.print(description.getHelp());
- } else if (description.getShortDescription() != null) {
- actor.print(description.getShortDescription());
+ // Create the message
+ if (callable instanceof Dispatcher) {
+ Dispatcher dispatcher = (Dispatcher) callable;
+
+ // Get a list of aliases
+ List aliases = new ArrayList(dispatcher.getCommands());
+ Collections.sort(aliases, new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN));
+
+ // Calculate pagination
+ int offset = perPage * page;
+ int pageTotal = (int) Math.ceil(aliases.size() / (double) perPage);
+
+ // Box
+ CommandListBox box = new CommandListBox(String.format("Help: page %d/%d ", page + 1, pageTotal));
+ StyledFragment contents = box.getContents();
+ StyledFragment tip = contents.createFragment(Style.GRAY);
+
+ if (offset >= aliases.size()) {
+ tip.createFragment(Style.RED).append(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal)).newLine();
+ } else {
+ List list = aliases.subList(offset, Math.min(offset + perPage, aliases.size()));
+
+ tip.append("Type ");
+ tip.append(new Code().append("//help ").append(" []"));
+ tip.append(" for more information.").newLine();
+
+ // Add each command
+ for (CommandMapping mapping : list) {
+ StringBuilder builder = new StringBuilder();
+ if (isRootLevel) {
+ builder.append("/");
+ }
+ if (!visited.isEmpty()) {
+ builder.append(Joiner.on(" ").join(visited));
+ builder.append(" ");
+ }
+ builder.append(mapping.getPrimaryAlias());
+ box.appendCommand(builder.toString(), mapping.getDescription().getShortDescription());
+ }
+ }
+
+ actor.printRaw(ColorCodeBuilder.asColorCodes(box));
} else {
- actor.print("No further help is available.");
+ CommandUsageBox box = new CommandUsageBox(callable, Joiner.on(" ").join(visited));
+ actor.printRaw(ColorCodeBuilder.asColorCodes(box));
}
}
}
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 cc9f26556..9c71cfe69 100644
--- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java
+++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java
@@ -42,6 +42,8 @@ import com.sk89q.worldedit.util.command.fluent.CommandGraph;
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import com.sk89q.worldedit.util.eventbus.Subscribe;
+import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
+import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
import com.sk89q.worldedit.util.logging.LogFormat;
@@ -50,6 +52,7 @@ import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -60,6 +63,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public final class CommandManager {
+ public static final Pattern COMMAND_CLEAN_PATTERN = Pattern.compile("^[/]+");
private static final Logger logger = Logger.getLogger(CommandManager.class.getCanonicalName());
private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$");
@@ -220,7 +224,17 @@ public final class CommandManager {
} catch (CommandPermissionsException e) {
actor.printError("You don't have permission to do this.");
} catch (InvalidUsageException e) {
- actor.printError(e.getMessage() + "\nUsage: " + e.getUsage("/"));
+ if (e.isFullHelpSuggested()) {
+ actor.printRaw(ColorCodeBuilder.asColorCodes(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals)));
+ String message = e.getMessage();
+ if (message != null) {
+ actor.printError(message);
+ }
+ } else {
+ String message = e.getMessage();
+ actor.printError(message != null ? message : "The command was not used properly (no more help available).");
+ actor.printError("Usage: " + e.getSimpleUsageString("/"));
+ }
} catch (WrappedCommandException e) {
Throwable t = e.getCause();
actor.printError("Please report this error: [See console]");
diff --git a/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java b/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java
index 86990f49c..84b1a474b 100644
--- a/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java
+++ b/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java
@@ -21,8 +21,10 @@ package com.sk89q.worldedit.math.transform;
import com.sk89q.worldedit.Vector;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -60,6 +62,15 @@ public class CombinedTransform implements Transform {
return vector;
}
+ @Override
+ public Transform inverse() {
+ List list = new ArrayList();
+ for (int i = transforms.length - 1; i >= 0; i--) {
+ list.add(transforms[i].inverse());
+ }
+ return new CombinedTransform(list);
+ }
+
@Override
public Transform combine(Transform other) {
checkNotNull(other);
diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Identity.java b/src/main/java/com/sk89q/worldedit/math/transform/Identity.java
index 3cad43c2c..a5f22038d 100644
--- a/src/main/java/com/sk89q/worldedit/math/transform/Identity.java
+++ b/src/main/java/com/sk89q/worldedit/math/transform/Identity.java
@@ -31,12 +31,17 @@ public class Identity implements Transform {
return vector;
}
+ @Override
+ public Transform inverse() {
+ return this;
+ }
+
@Override
public Transform combine(Transform other) {
if (other instanceof Identity) {
return this;
} else {
- return new CombinedTransform(this, other);
+ return other;
}
}
}
diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Transform.java b/src/main/java/com/sk89q/worldedit/math/transform/Transform.java
index 53f0d44ca..05a7d5196 100644
--- a/src/main/java/com/sk89q/worldedit/math/transform/Transform.java
+++ b/src/main/java/com/sk89q/worldedit/math/transform/Transform.java
@@ -27,6 +27,13 @@ import com.sk89q.worldedit.Vector;
*/
public interface Transform extends Function {
+ /**
+ * Create a new inverse transform.
+ *
+ * @return a new inverse transform
+ */
+ Transform inverse();
+
/**
* Create a new {@link Transform} that combines this transform with another.
*
diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Transforms.java b/src/main/java/com/sk89q/worldedit/math/transform/Transforms.java
new file mode 100644
index 000000000..35f8a3df8
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/math/transform/Transforms.java
@@ -0,0 +1,49 @@
+/*
+ * 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.math.transform;
+
+import com.sk89q.worldedit.util.Location;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Various utility methods related to {@link Transform}s.
+ */
+public final class Transforms {
+
+ private Transforms() {
+ }
+
+ /**
+ * Transform a location's position with a given transform.
+ *
+ * Direction is unaffected.
+ *
+ * @param location the location
+ * @param transform the transform
+ * @return the transformed location
+ */
+ public static Location transform(Location location, Transform transform) {
+ checkNotNull(location);
+ checkNotNull(transform);
+ return new Location(location.getWorld(), transform.apply(location.toVector()), location.getDirection());
+ }
+
+}
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 9a16a467c..3c75884c5 100644
--- a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java
+++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java
@@ -23,19 +23,11 @@ import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals;
import java.util.List;
-import java.util.Set;
/**
* A command that can be executed.
*/
public interface CommandCallable {
-
- /**
- * Get a list of value flags used by this command.
- *
- * @return a list of value flags
- */
- Set getValueFlags();
/**
* Execute the correct command based on the input.
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 6e44bff17..093850728 100644
--- a/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java
+++ b/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java
@@ -19,70 +19,37 @@
package com.sk89q.worldedit.util.command;
-import java.util.Arrays;
-
/**
- * Tracks a command registration.
+ * Provides information about a mapping between a command and its aliases.
*/
-public class CommandMapping {
-
- private final String[] aliases;
- private final CommandCallable callable;
-
- /**
- * Create a new instance.
- *
- * @param callable the command callable
- * @param alias a list of all aliases, where the first one is the primary one
- */
- public CommandMapping(CommandCallable callable, String... alias) {
- super();
- this.aliases = alias;
- this.callable = callable;
- }
+public interface CommandMapping {
/**
* Get the primary alias.
- *
+ *
* @return the primary alias
*/
- public String getPrimaryAlias() {
- return aliases[0];
- }
-
+ String getPrimaryAlias();
+
/**
* Get a list of all aliases.
- *
+ *
* @return aliases
*/
- public String[] getAllAliases() {
- return aliases;
- }
-
+ String[] getAllAliases();
+
/**
* Get the callable
- *
+ *
* @return the callable
*/
- public CommandCallable getCallable() {
- return callable;
- }
+ CommandCallable getCallable();
/**
* Get the {@link Description} form the callable.
- *
+ *
* @return the description
*/
- public Description getDescription() {
- return getCallable().getDescription();
- }
-
- @Override
- public String toString() {
- return "CommandMapping{" +
- "aliases=" + Arrays.toString(aliases) +
- ", callable=" + callable +
- '}';
- }
+ Description getDescription();
}
diff --git a/src/main/java/com/sk89q/worldedit/util/command/InvalidUsageException.java b/src/main/java/com/sk89q/worldedit/util/command/InvalidUsageException.java
index a97ce4d2f..9795aaaef 100644
--- a/src/main/java/com/sk89q/worldedit/util/command/InvalidUsageException.java
+++ b/src/main/java/com/sk89q/worldedit/util/command/InvalidUsageException.java
@@ -21,29 +21,87 @@ package com.sk89q.worldedit.util.command;
import com.sk89q.minecraft.util.commands.CommandException;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* Thrown when a command is not used properly.
+ *
+ * When handling this exception, print the error message if it is not null.
+ * Print a one line help instruction unless {@link #isFullHelpSuggested()}
+ * is true, which, in that case, the full help of the command should be shown.
+ *
+ * If no error message is set and full help is not to be shown, then a generic
+ * "you used this command incorrectly" message should be shown.
*/
public class InvalidUsageException extends CommandException {
private static final long serialVersionUID = -3222004168669490390L;
- private final Description description;
+ private final CommandCallable command;
+ private final boolean fullHelpSuggested;
- public InvalidUsageException(Description description) {
- this.description = description;
+ /**
+ * Create a new instance with no error message and with no suggestion
+ * that full and complete help for the command should be shown. This will
+ * result in a generic error message.
+ *
+ * @param command the command
+ */
+ public InvalidUsageException(CommandCallable command) {
+ this(null, command);
}
- public InvalidUsageException(String message, Description description) {
+ /**
+ * Create a new instance with a message and with no suggestion
+ * that full and complete help for the command should be shown.
+ *
+ * @param message the message
+ * @param command the command
+ */
+ public InvalidUsageException(@Nullable String message, CommandCallable command) {
+ this(message, command, false);
+ }
+
+ /**
+ * Create a new instance with a message.
+ *
+ * @param message the message
+ * @param command the command
+ * @param fullHelpSuggested true if the full help for the command should be shown
+ */
+ public InvalidUsageException(@Nullable String message, CommandCallable command, boolean fullHelpSuggested) {
super(message);
- this.description = description;
+ checkNotNull(command);
+ this.command = command;
+ this.fullHelpSuggested = fullHelpSuggested;
}
- public Description getDescription() {
- return description;
+ /**
+ * Get the command.
+ *
+ * @return the command
+ */
+ public CommandCallable getCommand() {
+ return command;
}
- public String getUsage(String prefix) {
- return toStackString(prefix, getDescription().getUsage());
+ /**
+ * Get a simple usage string.
+ *
+ * @param prefix the command shebang (such as "/") -- may be blank
+ * @return a usage string
+ */
+ public String getSimpleUsageString(String prefix) {
+ return getCommandUsed(prefix, command.getDescription().getUsage());
}
+ /**
+ * Return whether the full usage of the command should be shown.
+ *
+ * @return show full usage
+ */
+ public boolean isFullHelpSuggested() {
+ return fullHelpSuggested;
+ }
}
diff --git a/src/main/java/com/sk89q/worldedit/util/command/PrimaryAliasComparator.java b/src/main/java/com/sk89q/worldedit/util/command/PrimaryAliasComparator.java
new file mode 100644
index 000000000..b7b980acf
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/util/command/PrimaryAliasComparator.java
@@ -0,0 +1,59 @@
+/*
+ * 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 javax.annotation.Nullable;
+import java.util.Comparator;
+import java.util.regex.Pattern;
+
+/**
+ * Compares the primary aliases of two {@link CommandMapping} using
+ * {@link String#compareTo(String)}.
+ */
+public final class PrimaryAliasComparator implements Comparator {
+
+ /**
+ * An instance of this class.
+ */
+ public static final PrimaryAliasComparator INSTANCE = new PrimaryAliasComparator(null);
+ private final @Nullable Pattern removalPattern;
+
+ /**
+ * Create a new instance.
+ *
+ * @param removalPattern a regex to remove unwanted characters from the compared aliases
+ */
+ public PrimaryAliasComparator(@Nullable Pattern removalPattern) {
+ this.removalPattern = removalPattern;
+ }
+
+ private String clean(String alias) {
+ if (removalPattern != null) {
+ return removalPattern.matcher(alias).replaceAll("");
+ }
+ return alias;
+ }
+
+ @Override
+ public int compare(CommandMapping o1, CommandMapping o2) {
+ return clean(o1.getPrimaryAlias()).compareTo(clean(o2.getPrimaryAlias()));
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleCommandMapping.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleCommandMapping.java
new file mode 100644
index 000000000..982832c48
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleCommandMapping.java
@@ -0,0 +1,72 @@
+/*
+ * 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 java.util.Arrays;
+
+/**
+ * Tracks a command registration.
+ */
+public class SimpleCommandMapping implements CommandMapping {
+
+ private final String[] aliases;
+ private final CommandCallable callable;
+
+ /**
+ * Create a new instance.
+ *
+ * @param callable the command callable
+ * @param alias a list of all aliases, where the first one is the primary one
+ */
+ public SimpleCommandMapping(CommandCallable callable, String... alias) {
+ super();
+ this.aliases = alias;
+ this.callable = callable;
+ }
+
+ @Override
+ public String getPrimaryAlias() {
+ return aliases[0];
+ }
+
+ @Override
+ public String[] getAllAliases() {
+ return aliases;
+ }
+
+ @Override
+ public CommandCallable getCallable() {
+ return callable;
+ }
+
+ @Override
+ public Description getDescription() {
+ 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/SimpleDispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java
index a84c6b497..ea027ec55 100644
--- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java
+++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java
@@ -20,12 +20,20 @@
package com.sk89q.worldedit.util.command;
import com.google.common.base.Joiner;
-import com.sk89q.minecraft.util.commands.*;
-import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
-import com.sk89q.worldedit.util.formatting.CommandListBox;
-import com.sk89q.worldedit.util.formatting.StyledFragment;
+import com.sk89q.minecraft.util.commands.CommandContext;
+import com.sk89q.minecraft.util.commands.CommandException;
+import com.sk89q.minecraft.util.commands.CommandLocals;
+import com.sk89q.minecraft.util.commands.CommandPermissionsException;
+import com.sk89q.minecraft.util.commands.WrappedCommandException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* A simple implementation of {@link Dispatcher}.
@@ -47,7 +55,7 @@ public class SimpleDispatcher implements Dispatcher {
@Override
public void registerCommand(CommandCallable callable, String... alias) {
- CommandMapping mapping = new CommandMapping(callable, alias);
+ CommandMapping mapping = new SimpleCommandMapping(callable, alias);
// Check for replacements
for (String a : alias) {
@@ -93,11 +101,6 @@ public class SimpleDispatcher implements Dispatcher {
return commands.get(alias.toLowerCase());
}
- @Override
- public Set getValueFlags() {
- return Collections.emptySet();
- }
-
@Override
public boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException {
// We have permission for this command if we have permissions for subcommands
@@ -109,7 +112,7 @@ public class SimpleDispatcher implements Dispatcher {
Set aliases = getPrimaryAliases();
if (aliases.isEmpty()) {
- throw new InvalidUsageException("This command has no sub-commands.", getDescription());
+ throw new InvalidUsageException("This command has no sub-commands.", this);
} else if (split.length > 0) {
String subCommand = split[0];
String subArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length));
@@ -132,7 +135,7 @@ public class SimpleDispatcher implements Dispatcher {
}
- throw new InvalidUsageException(ColorCodeBuilder.asColorCodes(getSubcommandList(locals, parentCommands)), getDescription());
+ throw new InvalidUsageException("Please choose a sub-command.", this, true);
}
@Override
@@ -185,16 +188,4 @@ public class SimpleDispatcher implements Dispatcher {
return false;
}
- private StyledFragment getSubcommandList(CommandLocals locals, String[] parentCommands) {
- CommandListBox box = new CommandListBox("Subcommands");
- String prefix = parentCommands.length > 0 ? "/" + Joiner.on(" ").join(parentCommands) + " " : "";
- for (CommandMapping mapping : getCommands()) {
- if (mapping.getCallable().testPermission(locals)) {
- box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getShortDescription());
- }
- }
-
- return box;
- }
-
}
diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/Binding.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/Binding.java
index dcf931c81..108fc1bf8 100644
--- a/src/main/java/com/sk89q/worldedit/util/command/parametric/Binding.java
+++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/Binding.java
@@ -23,6 +23,7 @@ import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.worldedit.util.command.binding.PrimitiveBindings;
import com.sk89q.worldedit.util.command.binding.StandardBindings;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.List;
@@ -77,8 +78,8 @@ public interface Binding {
* @throws ParameterException thrown if the parameter could not be formulated
* @throws CommandException on a command exception
*/
- Object bind(ParameterData parameter, ArgumentStack scoped, boolean onlyConsume)
- throws ParameterException, CommandException;
+ Object bind(ParameterData parameter, ArgumentStack scoped, boolean onlyConsume)
+ throws ParameterException, CommandException, InvocationTargetException;
/**
* Get a list of suggestions for the given parameter and user arguments.
diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java
index b442da6a2..b56c76426 100644
--- a/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java
+++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java
@@ -143,7 +143,7 @@ public class BindingHelper implements Binding {
@Override
public Object bind(ParameterData parameter, ArgumentStack scoped,
- boolean onlyConsume) throws ParameterException, CommandException {
+ boolean onlyConsume) throws ParameterException, CommandException, InvocationTargetException {
BoundMethod binding = match(parameter);
List