diff --git a/checkstyle.xml b/checkstyle.xml
index dede079b..54a3ed89 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -146,7 +146,6 @@
-
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java b/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java
index af0fac9d..28c3dcdd 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java
@@ -89,6 +89,25 @@ public abstract class CommandManager());
}
+ /**
+ * Tokenize an input string
+ *
+ * @param input Input string
+ * @return List of tokens
+ */
+ @Nonnull
+ public static LinkedList tokenize(@Nonnull final String input) {
+ final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
+ final LinkedList tokens = new LinkedList<>();
+ while (stringTokenizer.hasMoreElements()) {
+ tokens.add(stringTokenizer.nextToken());
+ }
+ if (input.endsWith(" ")) {
+ tokens.add("");
+ }
+ return tokens;
+ }
+
/**
* Execute a command and get a future that completes with the result
*
@@ -134,19 +153,6 @@ public abstract class CommandManager tokenize(@Nonnull final String input) {
- final StringTokenizer stringTokenizer = new StringTokenizer(input, " ");
- final LinkedList tokens = new LinkedList<>();
- while (stringTokenizer.hasMoreElements()) {
- tokens.add(stringTokenizer.nextToken());
- }
- if (input.endsWith(" ")) {
- tokens.add("");
- }
- return tokens;
- }
-
/**
* Register a new command
*
@@ -235,7 +241,7 @@ public abstract class CommandManager getCommandTree() {
+ public CommandTree getCommandTree() {
return this.commandTree;
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java b/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java
index 7f93679c..17a2daec 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java
@@ -39,6 +39,7 @@ import com.intellectualsites.commands.sender.CommandSender;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -491,13 +492,35 @@ public final class CommandTree {
return casted;
}
+ /**
+ * Get an immutable collection containing all of the root nodes
+ * in the tree
+ *
+ * @return Root nodes
+ */
+ @Nonnull
+ public Collection>> getRootNodes() {
+ return this.internalTree.getChildren();
+ }
+
+ /**
+ * Get a named root node, if it exists
+ *
+ * @param name Root node name
+ * @return Root node, or {@code null}
+ */
+ @Nullable
+ public Node> getNamedNode(@Nullable final String name) {
+ return this.getRootNodes().stream().filter(node -> node.getValue() != null
+ && node.getValue().getName().equalsIgnoreCase(name)).findAny().orElse(null);
+ }
/**
* Very simple tree structure
*
* @param Node value type
*/
- private static final class Node {
+ public static final class Node {
private final Map nodeMeta = new HashMap<>();
private final List> children = new LinkedList<>();
@@ -508,7 +531,13 @@ public final class CommandTree {
this.value = value;
}
- private List> getChildren() {
+ /**
+ * Get an immutable copy of the node's child list
+ *
+ * @return Children
+ */
+ @Nonnull
+ public List> getChildren() {
return Collections.unmodifiableList(this.children);
}
@@ -529,10 +558,30 @@ public final class CommandTree {
return null;
}
- private boolean isLeaf() {
+ /**
+ * Check if the node is a leaf node
+ *
+ * @return {@code true} if the node is a leaf node, else {@code false}
+ */
+ public boolean isLeaf() {
return this.children.isEmpty();
}
+ /**
+ * Get the node meta instance
+ *
+ * @return Node meta
+ */
+ @Nonnull
+ public Map getNodeMeta() {
+ return this.nodeMeta;
+ }
+
+ /**
+ * Get the node value
+ *
+ * @return Node value
+ */
@Nullable
public T getValue() {
return this.value;
@@ -555,11 +604,21 @@ public final class CommandTree {
return Objects.hash(getValue());
}
+ /**
+ * Get the parent node
+ *
+ * @return Parent node
+ */
@Nullable
public Node getParent() {
return this.parent;
}
+ /**
+ * Set the parent node
+ *
+ * @param parent new parent node
+ */
public void setParent(@Nullable final Node parent) {
this.parent = parent;
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/ComponentParser.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/ComponentParser.java
index ef8c4458..de4eba20 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/ComponentParser.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/ComponentParser.java
@@ -62,4 +62,14 @@ public interface ComponentParser {
return Collections.emptyList();
}
+ /**
+ * Check whether or not this component parser is context free. A context free
+ * parser will not use the provided command context, and so supports impromptu parsing
+ *
+ * @return {@code true} if the parser is context free, else {@code false}
+ */
+ default boolean isContextFree() {
+ return false;
+ }
+
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/StandardParserRegistry.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/StandardParserRegistry.java
index cbb2997a..e6b31e86 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/StandardParserRegistry.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/parser/StandardParserRegistry.java
@@ -26,12 +26,21 @@ package com.intellectualsites.commands.components.parser;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.intellectualsites.commands.annotations.specifier.Range;
+import com.intellectualsites.commands.components.standard.BooleanComponent;
+import com.intellectualsites.commands.components.standard.ByteComponent;
+import com.intellectualsites.commands.components.standard.CharComponent;
+import com.intellectualsites.commands.components.standard.DoubleComponent;
+import com.intellectualsites.commands.components.standard.EnumComponent;
+import com.intellectualsites.commands.components.standard.FloatComponent;
import com.intellectualsites.commands.components.standard.IntegerComponent;
+import com.intellectualsites.commands.components.standard.ShortComponent;
+import com.intellectualsites.commands.components.standard.StringComponent;
import com.intellectualsites.commands.sender.CommandSender;
import javax.annotation.Nonnull;
import java.lang.annotation.Annotation;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@@ -69,9 +78,27 @@ public final class StandardParserRegistry implements Pa
this.registerAnnotationMapper(Range.class, new RangeMapper<>());
/* Register standard types */
+ this.registerParserSupplier(TypeToken.of(Byte.class), options ->
+ new ByteComponent.ByteParser((byte) options.get(StandardParameters.RANGE_MIN, Byte.MIN_VALUE),
+ (byte) options.get(StandardParameters.RANGE_MAX, Byte.MAX_VALUE)));
+ this.registerParserSupplier(TypeToken.of(Short.class), options ->
+ new ShortComponent.ShortParser((short) options.get(StandardParameters.RANGE_MIN, Short.MIN_VALUE),
+ (short) options.get(StandardParameters.RANGE_MAX, Short.MAX_VALUE)));
this.registerParserSupplier(TypeToken.of(Integer.class), options ->
new IntegerComponent.IntegerParser((int) options.get(StandardParameters.RANGE_MIN, Integer.MIN_VALUE),
(int) options.get(StandardParameters.RANGE_MAX, Integer.MAX_VALUE)));
+ this.registerParserSupplier(TypeToken.of(Float.class), options ->
+ new FloatComponent.FloatParser((float) options.get(StandardParameters.RANGE_MIN, Float.MIN_VALUE),
+ (float) options.get(StandardParameters.RANGE_MAX, Float.MAX_VALUE)));
+ this.registerParserSupplier(TypeToken.of(Double.class), options ->
+ new DoubleComponent.DoubleParser((double) options.get(StandardParameters.RANGE_MIN, Double.MIN_VALUE),
+ (double) options.get(StandardParameters.RANGE_MAX, Double.MAX_VALUE)));
+ this.registerParserSupplier(TypeToken.of(Character.class), options -> new CharComponent.CharacterParser());
+ /* Make this one less awful */
+ this.registerParserSupplier(TypeToken.of(String.class), options -> new StringComponent.StringParser(
+ StringComponent.StringMode.SINGLE, (context, s) -> Collections.emptyList()));
+ /* Add options to this */
+ this.registerParserSupplier(TypeToken.of(Boolean.class), options -> new BooleanComponent.BooleanParser<>(false));
}
@Override
@@ -98,8 +125,8 @@ public final class StandardParserRegistry implements Pa
if (mapper == null) {
return;
}
- @SuppressWarnings("unchecked")
- final ParserParameters parserParametersCasted = (ParserParameters) mapper.apply(annotation, parsingType);
+ @SuppressWarnings("unchecked") final ParserParameters parserParametersCasted = (ParserParameters) mapper.apply(
+ annotation, parsingType);
parserParameters.merge(parserParametersCasted);
});
return parserParameters;
@@ -117,10 +144,18 @@ public final class StandardParserRegistry implements Pa
}
final Function> producer = this.parserSuppliers.get(actualType);
if (producer == null) {
+ /* Give enums special treatment */
+ if (actualType.isSubtypeOf(Enum.class)) {
+ @SuppressWarnings("all")
+ final EnumComponent.EnumParser enumComponent = new EnumComponent.EnumParser((Class)
+ actualType.getRawType());
+ // noinspection all
+ return Optional.of(enumComponent);
+ }
return Optional.empty();
}
- @SuppressWarnings("unchecked")
- final ComponentParser parser = (ComponentParser) producer.apply(parserParameters);
+ @SuppressWarnings("unchecked") final ComponentParser parser = (ComponentParser) producer.apply(
+ parserParameters);
return Optional.of(parser);
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java
index 7fa83807..14ad5ee6 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java
@@ -146,7 +146,12 @@ public final class BooleanComponent extends CommandComp
private final boolean liberal;
- private BooleanParser(final boolean liberal) {
+ /**
+ * Construct a new boolean parser
+ *
+ * @param liberal Whether or not it'll accept boolean-esque strings, or just booleans
+ */
+ public BooleanParser(final boolean liberal) {
this.liberal = liberal;
}
@@ -195,6 +200,11 @@ public final class BooleanComponent extends CommandComp
return LIBERAL;
}
+
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java
index 6afbd514..f01f0674 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java
@@ -167,7 +167,13 @@ public final class ByteComponent extends CommandCompone
private final byte min;
private final byte max;
- private ByteParser(final byte min, final byte max) {
+ /**
+ * Construct a new byte parser
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ */
+ public ByteParser(final byte min, final byte max) {
this.min = min;
this.max = max;
}
@@ -198,6 +204,10 @@ public final class ByteComponent extends CommandCompone
}
}
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java
index 20b3c6f8..0713a42b 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java
@@ -111,7 +111,7 @@ public final class CharComponent extends CommandCompone
}
- private static final class CharacterParser implements ComponentParser {
+ public static final class CharacterParser implements ComponentParser {
@Nonnull
@Override
@@ -128,6 +128,11 @@ public final class CharComponent extends CommandCompone
return ComponentParseResult.success(input.charAt(0));
}
+
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java
index 32f8a1d4..cc474d89 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java
@@ -166,12 +166,18 @@ public final class DoubleComponent extends CommandCompo
}
- private static final class DoubleParser implements ComponentParser {
+ public static final class DoubleParser implements ComponentParser {
private final double min;
private final double max;
- private DoubleParser(final double min, final double max) {
+ /**
+ * Construct a new double parser
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ */
+ public DoubleParser(final double min, final double max) {
this.min = min;
this.max = max;
}
@@ -197,6 +203,10 @@ public final class DoubleComponent extends CommandCompo
}
}
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java
index 2cbdcec4..3f131edf 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java
@@ -130,12 +130,17 @@ public class EnumComponent> extends C
}
- private static final class EnumParser> implements ComponentParser {
+ public static final class EnumParser> implements ComponentParser {
private final Class enumClass;
private final EnumSet allowedValues;
- private EnumParser(@Nonnull final Class enumClass) {
+ /**
+ * Construct a new enum parser
+ *
+ * @param enumClass Enum class
+ */
+ public EnumParser(@Nonnull final Class enumClass) {
this.enumClass = enumClass;
this.allowedValues = EnumSet.allOf(enumClass);
}
@@ -164,6 +169,11 @@ public class EnumComponent> extends C
public List suggestions(@Nonnull final CommandContext commandContext, @Nonnull final String input) {
return EnumSet.allOf(this.enumClass).stream().map(e -> e.name().toLowerCase()).collect(Collectors.toList());
}
+
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java
index 6936b7f8..2856a4f2 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java
@@ -166,12 +166,18 @@ public final class FloatComponent extends CommandCompon
}
- private static final class FloatParser implements ComponentParser {
+ public static final class FloatParser implements ComponentParser {
private final float min;
private final float max;
- private FloatParser(final float min, final float max) {
+ /**
+ * Construct a new float parser
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ */
+ public FloatParser(final float min, final float max) {
this.min = min;
this.max = max;
}
@@ -197,6 +203,10 @@ public final class FloatComponent extends CommandCompon
}
}
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java
index cc637289..1c5e31f5 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java
@@ -221,6 +221,10 @@ public final class IntegerComponent extends CommandComp
return this.max;
}
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java
index 76325cae..1fb6aaf1 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java
@@ -197,6 +197,10 @@ public final class LongComponent extends CommandCompone
}
}
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java
index 5e825262..1b48aeb2 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java
@@ -166,12 +166,18 @@ public final class ShortComponent extends CommandCompon
}
- private static final class ShortParser implements ComponentParser {
+ public static final class ShortParser implements ComponentParser {
private final short min;
private final short max;
- private ShortParser(final short min, final short max) {
+ /**
+ * Construct a new short parser
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ */
+ public ShortParser(final short min, final short max) {
this.min = min;
this.max = max;
}
@@ -197,6 +203,10 @@ public final class ShortComponent extends CommandCompon
}
}
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java
index f65855e6..6a8b091f 100644
--- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java
+++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java
@@ -45,7 +45,7 @@ public final class StringComponent extends CommandCompo
@Nonnull final String name,
@Nonnull final StringMode stringMode,
@Nonnull final String defaultValue,
- @Nonnull final BiFunction, String, List> suggestionsProvider) {
+ @Nonnull final BiFunction, String, List> suggestionsProvider) {
super(required, name, new StringParser<>(stringMode, suggestionsProvider), defaultValue);
this.stringMode = stringMode;
}
@@ -121,7 +121,7 @@ public final class StringComponent extends CommandCompo
public static final class Builder extends CommandComponent.Builder {
private StringMode stringMode = StringMode.SINGLE;
- private BiFunction, String, List> suggestionsProvider = (v1, v2) -> Collections.emptyList();
+ private BiFunction, String, List> suggestionsProvider = (v1, v2) -> Collections.emptyList();
protected Builder(@Nonnull final String name) {
super(name);
@@ -188,13 +188,19 @@ public final class StringComponent extends CommandCompo
}
- private static final class StringParser implements ComponentParser {
+ public static final class StringParser implements ComponentParser {
private final StringMode stringMode;
private final BiFunction, String, List> suggestionsProvider;
- private StringParser(@Nonnull final StringMode stringMode,
- @Nonnull final BiFunction, String, List> suggestionsProvider) {
+ /**
+ * Construct a new string parser
+ *
+ * @param stringMode String parsing mode
+ * @param suggestionsProvider Suggestions provider
+ */
+ public StringParser(@Nonnull final StringMode stringMode,
+ @Nonnull final BiFunction, String, List> suggestionsProvider) {
this.stringMode = stringMode;
this.suggestionsProvider = suggestionsProvider;
}
@@ -258,6 +264,11 @@ public final class StringComponent extends CommandCompo
public List suggestions(@Nonnull final CommandContext commandContext, @Nonnull final String input) {
return this.suggestionsProvider.apply(commandContext, input);
}
+
+ @Override
+ public boolean isContextFree() {
+ return true;
+ }
}
diff --git a/cloud-minecraft/cloud-brigadier/src/main/java/com/intellectualsites/commands/brigadier/CloudBrigadierManager.java b/cloud-minecraft/cloud-brigadier/src/main/java/com/intellectualsites/commands/brigadier/CloudBrigadierManager.java
new file mode 100644
index 00000000..92b610b4
--- /dev/null
+++ b/cloud-minecraft/cloud-brigadier/src/main/java/com/intellectualsites/commands/brigadier/CloudBrigadierManager.java
@@ -0,0 +1,263 @@
+//
+// MIT License
+//
+// Copyright (c) 2020 Alexander Söderberg
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package com.intellectualsites.commands.brigadier;
+
+import com.google.common.collect.Maps;
+import com.google.common.reflect.TypeToken;
+import com.intellectualsites.commands.CommandTree;
+import com.intellectualsites.commands.components.CommandComponent;
+import com.intellectualsites.commands.components.StaticComponent;
+import com.intellectualsites.commands.components.standard.BooleanComponent;
+import com.intellectualsites.commands.components.standard.ByteComponent;
+import com.intellectualsites.commands.components.standard.DoubleComponent;
+import com.intellectualsites.commands.components.standard.FloatComponent;
+import com.intellectualsites.commands.components.standard.IntegerComponent;
+import com.intellectualsites.commands.components.standard.ShortComponent;
+import com.intellectualsites.commands.components.standard.StringComponent;
+import com.intellectualsites.commands.sender.CommandSender;
+import com.mojang.brigadier.arguments.ArgumentType;
+import com.mojang.brigadier.arguments.BoolArgumentType;
+import com.mojang.brigadier.arguments.DoubleArgumentType;
+import com.mojang.brigadier.arguments.FloatArgumentType;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
+import com.mojang.brigadier.arguments.StringArgumentType;
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
+import com.mojang.brigadier.builder.RequiredArgumentBuilder;
+import com.mojang.brigadier.suggestion.SuggestionProvider;
+import com.mojang.brigadier.tree.CommandNode;
+import com.mojang.brigadier.tree.LiteralCommandNode;
+
+import javax.annotation.Nonnull;
+import java.util.Map;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+
+/**
+ * Manager used to map cloud {@link com.intellectualsites.commands.Command}
+ *
+ * The structure of this class is largely inspired by
+ *
+ * ACFBrigadiermanager in the ACF project, which was originally written by MiniDigger and licensed under the MIT license.
+ *
+ * @param Command sender type
+ * @param Brigadier sender type
+ */
+public final class CloudBrigadierManager {
+
+ private final Map, Function extends CommandComponent,
+ ? extends ArgumentType>>> mappers;
+
+ /**
+ * Create a new cloud brigadier manager
+ */
+ public CloudBrigadierManager() {
+ this.mappers = Maps.newHashMap();
+ this.registerInternalMappings();
+ }
+
+ private void registerInternalMappings() {
+ /* Map byte, short and int to IntegerArgumentType */
+ this.registerMapping(new TypeToken>() {
+ }, component -> {
+ final boolean hasMin = component.getMin() != Byte.MIN_VALUE;
+ final boolean hasMax = component.getMax() != Byte.MAX_VALUE;
+ if (hasMin) {
+ return IntegerArgumentType.integer(component.getMin(), component.getMax());
+ } else if (hasMax) {
+ return IntegerArgumentType.integer(Byte.MIN_VALUE, component.getMax());
+ } else {
+ return IntegerArgumentType.integer();
+ }
+ });
+ this.registerMapping(new TypeToken>() {
+ }, component -> {
+ final boolean hasMin = component.getMin() != Short.MIN_VALUE;
+ final boolean hasMax = component.getMax() != Short.MAX_VALUE;
+ if (hasMin) {
+ return IntegerArgumentType.integer(component.getMin(), component.getMax());
+ } else if (hasMax) {
+ return IntegerArgumentType.integer(Short.MIN_VALUE, component.getMax());
+ } else {
+ return IntegerArgumentType.integer();
+ }
+ });
+ this.registerMapping(new TypeToken>() {
+ }, component -> {
+ final boolean hasMin = component.getMin() != Integer.MIN_VALUE;
+ final boolean hasMax = component.getMax() != Integer.MAX_VALUE;
+
+ System.out.println("Constructing new IntegerArgumentType with min " + hasMin + " | max " + hasMax);
+
+ if (hasMin) {
+ return IntegerArgumentType.integer(component.getMin(), component.getMax());
+ } else if (hasMax) {
+ return IntegerArgumentType.integer(Integer.MIN_VALUE, component.getMax());
+ } else {
+ return IntegerArgumentType.integer();
+ }
+ });
+ /* Map float to FloatArgumentType */
+ this.registerMapping(new TypeToken>() {
+ }, component -> {
+ final boolean hasMin = component.getMin() != Float.MIN_VALUE;
+ final boolean hasMax = component.getMax() != Float.MAX_VALUE;
+ if (hasMin) {
+ return FloatArgumentType.floatArg(component.getMin(), component.getMax());
+ } else if (hasMax) {
+ return FloatArgumentType.floatArg(Float.MIN_VALUE, component.getMax());
+ } else {
+ return FloatArgumentType.floatArg();
+ }
+ });
+ /* Map double to DoubleArgumentType */
+ this.registerMapping(new TypeToken>() {
+ }, component -> {
+ final boolean hasMin = component.getMin() != Double.MIN_VALUE;
+ final boolean hasMax = component.getMax() != Double.MAX_VALUE;
+ if (hasMin) {
+ return DoubleArgumentType.doubleArg(component.getMin(), component.getMax());
+ } else if (hasMax) {
+ return DoubleArgumentType.doubleArg(Double.MIN_VALUE, component.getMax());
+ } else {
+ return DoubleArgumentType.doubleArg();
+ }
+ });
+ /* Map boolean to BoolArgumentType */
+ this.registerMapping(new TypeToken>() {
+ }, component -> BoolArgumentType.bool());
+ /* Map String properly to StringArgumentType */
+ this.registerMapping(new TypeToken>() {
+ }, component -> {
+ switch (component.getStringMode()) {
+ case SINGLE:
+ return StringArgumentType.word();
+ case QUOTED:
+ return StringArgumentType.string();
+ case GREEDY:
+ return StringArgumentType.greedyString();
+ default:
+ return StringArgumentType.word();
+ }
+ });
+ }
+
+ /**
+ * Register a cloud-Brigadier mapping
+ *
+ * @param componentType cloud component type
+ * @param mapper mapper function
+ * @param cloud component value type
+ * @param cloud component type
+ * @param Brigadier argument type value
+ */
+ public , O> void registerMapping(@Nonnull final TypeToken componentType,
+ @Nonnull final Function extends K,
+ ? extends ArgumentType> mapper) {
+ this.mappers.put(componentType.getRawType(), mapper);
+ }
+
+ /**
+ * Get a Brigadier {@link ArgumentType} from a cloud {@link CommandComponent}
+ *
+ * @param componentType cloud component type
+ * @param component cloud component
+ * @param cloud component value type (generic)
+ * @param cloud component type (generic)
+ * @return Brigadier argument type
+ */
+ @Nonnull
+ @SuppressWarnings("all")
+ public > ArgumentType> getArgument(@Nonnull final TypeToken componentType,
+ @Nonnull final K component) {
+ final CommandComponent commandComponent = (CommandComponent) component;
+ final Function function = this.mappers.getOrDefault(componentType.getRawType(), t ->
+ createDefaultMapper((CommandComponent) component));
+ return (ArgumentType>) function.apply(commandComponent);
+ }
+
+ @Nonnull
+ private > ArgumentType> createDefaultMapper(@Nonnull final CommandComponent
+ component) {
+ return StringArgumentType.string();
+ }
+
+ /**
+ * Create a literal command from Brigadier command info, and a cloud command instance
+ *
+ * @param cloudCommand Cloud root command
+ * @param root Brigadier root command
+ * @param suggestionProvider Brigadier suggestions provider
+ * @param executor Brigadier command executor
+ * @param permissionChecker Permission checker
+ * @return Constructed literal command node
+ */
+ @Nonnull
+ public LiteralCommandNode createLiteralCommandNode(@Nonnull final CommandTree.Node> cloudCommand,
+ @Nonnull final LiteralCommandNode root,
+ @Nonnull final SuggestionProvider suggestionProvider,
+ @Nonnull final com.mojang.brigadier.Command executor,
+ @Nonnull final BiPredicate permissionChecker) {
+ final LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(root.getLiteral())
+ .requires(sender -> permissionChecker.test(sender, cloudCommand.getNodeMeta().getOrDefault("permission", "")));
+ if (cloudCommand.isLeaf() && cloudCommand.getValue() != null && cloudCommand.getValue().getOwningCommand() != null) {
+ literalArgumentBuilder.executes(executor);
+ }
+ final LiteralCommandNode constructedRoot = literalArgumentBuilder.build();
+ for (final CommandTree.Node> child : cloudCommand.getChildren()) {
+ constructedRoot.addChild(this.constructCommandNode(child, permissionChecker, executor, suggestionProvider));
+ }
+ return constructedRoot;
+ }
+
+ private CommandNode constructCommandNode(@Nonnull final CommandTree.Node> root,
+ @Nonnull final BiPredicate permissionChecker,
+ @Nonnull final com.mojang.brigadier.Command executor,
+ @Nonnull final SuggestionProvider suggestionProvider) {
+ CommandNode commandNode;
+ if (root.getValue() instanceof StaticComponent) {
+ final LiteralArgumentBuilder argumentBuilder = LiteralArgumentBuilder.literal(root.getValue().getName())
+ .requires(sender -> permissionChecker.test(sender, root.getNodeMeta().getOrDefault("permission", "")));
+ if (root.isLeaf()) {
+ argumentBuilder.executes(executor);
+ }
+ commandNode = argumentBuilder.build();
+ } else {
+ @SuppressWarnings("unchecked") final RequiredArgumentBuilder builder = RequiredArgumentBuilder
+ .argument(root.getValue().getName(),
+ (ArgumentType