From ff67f6343f5dfad1290b1eaea9555c90cffce208 Mon Sep 17 00:00:00 2001
From: Jesse Boyd
Date: Mon, 1 Apr 2019 21:35:55 +1100
Subject: [PATCH] Binding improvements
WIP towards deprecating parsers and unifying the command bindings
Allow registering dynamic bindings
- Supports nesting bindings
---
.../com/boydti/fawe/command/FaweBinding.java | 5 +-
.../fawe/command/FawePrimitiveBinding.java | 2 +-
.../boydti/fawe/command/PatternBinding.java | 2 -
.../boydti/fawe/object/brush/CircleBrush.java | 2 +-
.../general/plot/FaweSchematicHandler.java | 1 -
.../sk89q/jnbt/CompressedSchematicTag.java | 1 -
.../internal/command/WorldEditBinding.java | 2 +-
.../command/binding/PrimitiveBindings.java | 89 +----
.../command/binding/StandardBindings.java | 51 ++-
.../command/parametric/BindingHelper.java | 121 +++++--
.../util/command/parametric/BindingMap.java | 304 ++++++++++++++++++
.../util/command/parametric/BindingMatch.java | 7 +-
.../FunctionParametricCallable.java | 6 +-
.../command/parametric/ParametricBuilder.java | 53 +--
.../parametric/ParametricCallable.java | 4 +-
15 files changed, 487 insertions(+), 163 deletions(-)
create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingMap.java
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/FaweBinding.java b/worldedit-core/src/main/java/com/boydti/fawe/command/FaweBinding.java
index 73b8c7a25..2973feb5e 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/command/FaweBinding.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/command/FaweBinding.java
@@ -1,13 +1,12 @@
package com.boydti.fawe.command;
import com.sk89q.worldedit.WorldEdit;
-import com.sk89q.worldedit.internal.command.WorldEditBinding;
+import com.sk89q.worldedit.util.command.parametric.BindingHelper;
-public class FaweBinding extends WorldEditBinding {
+public class FaweBinding extends BindingHelper {
private final WorldEdit worldEdit;
public FaweBinding(WorldEdit worldEdit) {
- super(worldEdit);
this.worldEdit = worldEdit;
}
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java b/worldedit-core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java
index eacaf53cf..0a2c76534 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java
@@ -42,7 +42,7 @@ import java.net.URI;
import java.net.URL;
import javax.annotation.Nullable;
-public class FawePrimitiveBinding extends BindingHelper {
+public class FawePrimitiveBinding {
@BindingMatch(type = {Long.class, long.class},
behavior = BindingBehavior.CONSUMES,
consumedCount = 1,
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java b/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java
index 078042d21..fb05ed40a 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java
@@ -4,8 +4,6 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.util.command.parametric.ParameterData;
import com.sk89q.worldedit.world.block.BlockTypes;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CircleBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CircleBrush.java
index cea101db8..b7160c710 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CircleBrush.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CircleBrush.java
@@ -35,4 +35,4 @@ public class CircleBrush implements Brush {
Vector3 random = affine.apply(normal);
return random.cross(normal).normalize();
}
-}
+}
\ No newline at end of file
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java
index 12426beea..67c103c37 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java
@@ -98,7 +98,6 @@ public class FaweSchematicHandler extends SchematicHandler {
if (tag instanceof CompressedCompoundTag) {
CompressedCompoundTag cTag = (CompressedCompoundTag) tag;
if (cTag instanceof CompressedSchematicTag) {
- System.out.println("Write directly");
Clipboard clipboard = (Clipboard) cTag.getSource();
try (OutputStream stream = new FileOutputStream(tmp); NBTOutputStream output = new NBTOutputStream(new BufferedOutputStream(new PGZIPOutputStream(stream)))) {
new SpongeSchematicWriter(output).write(clipboard);
diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java
index bfdeb68ec..c5f43e4d6 100644
--- a/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java
+++ b/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java
@@ -19,7 +19,6 @@ public class CompressedSchematicTag extends CompressedCompoundTag {
@Override
public DataInputStream adapt(Clipboard src) throws IOException {
- System.out.println("Decompress");
FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream();
try (LZ4BlockOutputStream lz4out = new LZ4BlockOutputStream(blocksOut)) {
NBTOutputStream nbtOut = new NBTOutputStream(lz4out);
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java
index c3a8fe906..ead5cd02b 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java
@@ -64,7 +64,7 @@ import java.util.List;
/**
* Binds standard WorldEdit classes such as {@link Player} and {@link LocalSession}.
*/
-public class WorldEditBinding extends BindingHelper {
+public class WorldEditBinding {
private final WorldEdit worldEdit;
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java
index 3a4d78edf..9085cf186 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java
@@ -32,12 +32,14 @@ import java.lang.annotation.Annotation;
import javax.annotation.Nullable;
+import static com.sk89q.worldedit.util.command.parametric.BindingHelper.validate;
+
/**
* Handles basic Java types such as {@link String}s, {@link Byte}s, etc.
*
*
Handles both the object and primitive types.
*/
-public final class PrimitiveBindings extends BindingHelper {
+public final class PrimitiveBindings {
/**
* Gets a type from a {@link ArgumentStack}.
@@ -206,89 +208,4 @@ public final class PrimitiveBindings extends BindingHelper {
}
return null;
}
-
- /**
- * Validate a number value using relevant modifiers.
- *
- * @param number the number
- * @param modifiers the list of modifiers to scan
- * @throws ParameterException on a validation error
- */
- private static void validate(double number, Annotation[] modifiers)
- throws ParameterException {
- for (Annotation modifier : modifiers) {
- if (modifier instanceof Range) {
- Range range = (Range) modifier;
- if (number < range.min()) {
- throw new ParameterException(
- String.format(
- "A valid value is greater than or equal to %s " +
- "(you entered %s)", range.min(), number));
- } else if (number > range.max()) {
- throw new ParameterException(
- String.format(
- "A valid value is less than or equal to %s " +
- "(you entered %s)", range.max(), number));
- }
- }
- }
- }
-
- /**
- * Validate a number value using relevant modifiers.
- *
- * @param number the number
- * @param modifiers the list of modifiers to scan
- * @throws ParameterException on a validation error
- */
- private static void validate(int number, Annotation[] modifiers)
- throws ParameterException {
- for (Annotation modifier : modifiers) {
- if (modifier instanceof Range) {
- Range range = (Range) modifier;
- if (number < range.min()) {
- throw new ParameterException(
- String.format(
- "A valid value is greater than or equal to %s " +
- "(you entered %s)", range.min(), number));
- } else if (number > range.max()) {
- throw new ParameterException(
- String.format(
- "A valid value is less than or equal to %s " +
- "(you entered %s)", range.max(), number));
- }
- }
- }
- }
-
- /**
- * Validate a string value using relevant modifiers.
- *
- * @param string the string
- * @param modifiers the list of modifiers to scan
- * @throws ParameterException on a validation error
- */
- private static void validate(String string, Annotation[] modifiers)
- throws ParameterException {
- if (string == null) {
- return;
- }
-
- for (Annotation modifier : modifiers) {
- if (modifier instanceof Validate) {
- Validate validate = (Validate) modifier;
-
- if (!validate.regex().isEmpty()) {
- if (!string.matches(validate.regex())) {
- throw new ParameterException(
- String.format(
- "The given text doesn't match the right " +
- "format (technically speaking, the 'format' is %s)",
- validate.regex()));
- }
- }
- }
- }
- }
-
}
\ No newline at end of file
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/StandardBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/StandardBindings.java
index a5a06e5fe..1945c54d2 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/StandardBindings.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/StandardBindings.java
@@ -24,11 +24,17 @@ import com.sk89q.worldedit.util.command.parametric.ArgumentStack;
import com.sk89q.worldedit.util.command.parametric.BindingBehavior;
import com.sk89q.worldedit.util.command.parametric.BindingHelper;
import com.sk89q.worldedit.util.command.parametric.BindingMatch;
+import com.sk89q.worldedit.util.command.parametric.ParameterException;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import static com.sk89q.worldedit.util.command.parametric.BindingHelper.validate;
/**
* Standard bindings that should be available to most configurations.
*/
-public final class StandardBindings extends BindingHelper {
+public final class StandardBindings {
/**
* Gets a {@link CommandContext} from a {@link ArgumentStack}.
@@ -42,5 +48,48 @@ public final class StandardBindings extends BindingHelper {
context.markConsumed(); // Consume entire stack
return context.getContext();
}
+
+ @BindingMatch(
+ type = Annotation[].class,
+ behavior = BindingBehavior.PROVIDES,
+ consumedCount = 0,
+ provideModifiers = true,
+ provideType = true)
+ public Annotation[] getModifiers(ArgumentStack context, Annotation[] modifiers, Type type) throws ParameterException {
+ return modifiers;
+ }
+
+ @BindingMatch(
+ type = Type.class,
+ behavior = BindingBehavior.PROVIDES,
+ consumedCount = 0,
+ provideModifiers = true,
+ provideType = true)
+ public Type getType(ArgumentStack context, Annotation[] modifiers, Type type) throws ParameterException {
+ return type;
+ }
+
+ @BindingMatch(
+ type = Enum.class,
+ behavior = BindingBehavior.CONSUMES,
+ consumedCount = 1,
+ provideModifiers = true,
+ provideType = true)
+ public Enum getEnum(ArgumentStack context, Annotation[] modifiers, Type type) throws ParameterException {
+ String input = context.next();
+ Enum value;
+ try {
+ value = Enum.valueOf((Class) type, input);
+ } catch (IllegalArgumentException ignore) {
+ try {
+ value = Enum.valueOf((Class) type, input.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ throw new ParameterException("Invalid input " + input + " for type " + type);
+ }
+ }
+ validate(value.ordinal(), modifiers);
+ validate(input, modifiers);
+ return value;
+ }
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java
index e5024f77b..1d45d0b32 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java
@@ -22,6 +22,7 @@ package com.sk89q.worldedit.util.command.parametric;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.worldedit.util.command.binding.Range;
+import com.sk89q.worldedit.util.command.binding.Validate;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
@@ -48,16 +49,17 @@ import java.util.List;
*
Methods may throw any exception. Exceptions may be converted using a
* {@link ExceptionConverter} registered with the {@link ParametricBuilder}.
*/
+@Deprecated
public class BindingHelper implements Binding {
- private final List bindings;
+ private final List bindings;
private final Type[] types;
/**
* Create a new instance.
*/
public BindingHelper() {
- List bindings = new ArrayList<>();
+ List bindings = new ArrayList<>();
List types = new ArrayList<>();
for (Method method : this.getClass().getMethods()) {
@@ -88,7 +90,7 @@ public class BindingHelper implements Binding {
"A @BindingMatch needs either a type or classifier set");
}
- BoundMethod handler = new BoundMethod(info, type, classifier, method);
+ BindingMap.BoundMethod handler = new BindingMap.BoundMethod(info, type, classifier, method, this);
bindings.add(handler);
}
}
@@ -110,8 +112,8 @@ public class BindingHelper implements Binding {
* @param parameter the parameter
* @return a binding
*/
- private BoundMethod match(ParameterData parameter) {
- for (BoundMethod binding : bindings) {
+ private BindingMap.BoundMethod match(ParameterData parameter) {
+ for (BindingMap.BoundMethod binding : bindings) {
Annotation classifer = parameter.getClassifier();
Type type = parameter.getType();
@@ -147,7 +149,7 @@ public class BindingHelper implements Binding {
@Override
public Object bind(ParameterData parameter, ArgumentStack scoped,
boolean onlyConsume) throws ParameterException, CommandException, InvocationTargetException {
- BoundMethod binding = match(parameter);
+ BindingMap.BoundMethod binding = match(parameter);
List
*/
public ParametricBuilder() {
- addBinding(new FawePrimitiveBinding());
- addBinding(new StandardBindings());
+ this.bindings = new BindingMap(this);
+ this.bindings.add(new FawePrimitiveBinding());
+ this.bindings.add(new StandardBindings());
}
/**
@@ -103,14 +104,17 @@ public class ParametricBuilder {
* @param binding the binding
* @param type a list of types (if specified) to override the binding's types
*/
+ @Deprecated
public void addBinding(Binding binding, Type... type) {
- if (type == null || type.length == 0) {
- type = binding.getTypes();
- }
+ this.bindings.add(binding);
+ }
- for (Type t : type) {
- bindings.put(t, binding);
- }
+ /**
+ * Add a binding (accepts @Command or @BindingMatch methods)
+ * @param binding
+ */
+ public void addBinding(Object binding) {
+ this.bindings.add(binding);
}
/**
@@ -175,21 +179,22 @@ public class ParametricBuilder {
*/
public void registerMethodsAsCommands(Dispatcher dispatcher, Object object, CallableProcessor processor) throws ParametricException {
for (Method method : object.getClass().getDeclaredMethods()) {
- Command definition = method.getAnnotation(Command.class);
- if (definition != null) {
- definition = Commands.translate(method.getDeclaringClass(), definition);
- CommandCallable callable = build(object, method, definition);
- if (processor != null) {
- callable = new ProcessedCallable(callable, processor);
- }
- else if (object instanceof CallableProcessor) {
- callable = new ProcessedCallable(callable, (CallableProcessor) object);
- }
- if (object instanceof MethodCommands) {
- ((MethodCommands) object).register(method, callable, dispatcher);
- }
- dispatcher.registerCommand(callable, definition.aliases());
+ registerMethodAsCommands(method, dispatcher, object, processor);
+ }
+ }
+
+ public void registerMethodAsCommands(Method method, Dispatcher dispatcher, Object object, CallableProcessor processor) throws ParametricException {
+ Command definition = method.getAnnotation(Command.class);
+ if (definition != null) {
+ definition = Commands.translate(method.getDeclaringClass(), definition);
+ CommandCallable callable = build(object, method, definition);
+ if (processor != null) {
+ callable = new ProcessedCallable(callable, processor);
}
+ else if (object instanceof CallableProcessor) {
+ callable = new ProcessedCallable(callable, (CallableProcessor) object);
+ }
+ dispatcher.registerCommand(callable, definition.aliases());
}
}
@@ -230,7 +235,7 @@ public class ParametricBuilder {
*
* @return the map of bindings
*/
- public Map getBindings() {
+ public BindingMap getBindings() {
return bindings;
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java
index e2b90f6c0..9c66999db 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java
@@ -113,7 +113,7 @@ public class ParametricCallable extends AParametricCallable {
}
// Special annotation bindings
} else if (parameter.getBinding() == null) {
- parameter.setBinding(builder.getBindings().get(annotation.annotationType()));
+ parameter.setBinding(builder.getBindings());
parameter.setClassifier(annotation);
}
}
@@ -127,7 +127,7 @@ public class ParametricCallable extends AParametricCallable {
// No special @annotation binding... let's check for the type
if (parameter.getBinding() == null) {
- parameter.setBinding(builder.getBindings().get(type));
+ parameter.setBinding(builder.getBindings());
// Don't know how to parse for this type of value
if (parameter.getBinding() == null) {