/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* 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 org.lanternpowered.server.command.element;
import static org.lanternpowered.server.text.translation.TranslationHelper.t;
import com.flowpowered.math.vector.Vector3d;
import com.flowpowered.math.vector.Vector3i;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;
import org.lanternpowered.server.game.Lantern;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.args.CommandElement;
import org.spongepowered.api.command.args.GenericArguments;
import org.spongepowered.api.command.args.PatternMatchingCommandElement;
import org.spongepowered.api.entity.Entity;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.util.Color;
import org.spongepowered.api.util.GuavaCollectors;
import org.spongepowered.api.util.StartsWithPredicate;
import org.spongepowered.api.util.blockray.BlockRay;
import org.spongepowered.api.util.blockray.BlockRayHit;
import org.spongepowered.api.world.Locatable;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public final class GenericArguments2 {
/**
* Gives a {@link Color}, the color can be parsed
* in 3 different formats: literal which will match
* some inbuilt mappings, rgb which will use 3 components
* to parse the color and hex which will parse the
* hex format of a color.
*
* @param key The key to store the color under
* @return The element to match the input
*/
public static CommandElement color(Text key) {
return new ColorElement(key, null);
}
/**
* Gives a {@link Color}, the color can be parsed
* in 3 different formats: literal which will match
* some inbuilt mappings, rgb which will use 3 components
* to parse the color and hex which will parse the
* hex format of a color.
*
* <p>The default color will be used in the tab completation,
* it will be the first entry when completing a empty color
* element or the only entry when completing hex or rgb.</p>
*
* @param key The key to store the color under
* @param defaultColor The default color used in tab completation
* @return The element to match the input
*/
public static CommandElement color(Text key, @Nullable Color defaultColor) {
return new ColorElement(key, defaultColor);
}
private static class ColorElement extends CommandElement {
private static final Pattern RGB_PATTERN = Pattern.compile("^[0-9,]+$");
private static final Map<String, Color> INBUILT_COLORS = ImmutableMap.<String, Color>builder()
.put("black", Color.BLACK)
.put("blue", Color.BLUE)
.put("cyan", Color.CYAN)
.put("darkcyan", Color.DARK_CYAN)
.put("darkgreen", Color.DARK_GREEN)
.put("darkmagenta", Color.DARK_MAGENTA)
.put("gray", Color.GRAY)
.put("green", Color.GREEN)
.put("lime", Color.LIME)
.put("magenta", Color.MAGENTA)
.put("navy", Color.NAVY)
.put("pink", Color.PINK)
.put("purple", Color.PURPLE)
.put("red", Color.RED)
.put("white", Color.WHITE)
.put("yellow", Color.YELLOW)
.build();
private static final List<String> INBUILT_COLOR_NAMES = ImmutableList.copyOf(INBUILT_COLORS.keySet());
public static String findClosestColorName(String target) {
int distance = Integer.MAX_VALUE;
String closest = null;
for (String name : INBUILT_COLORS.keySet()) {
int currentDistance = StringUtils.getLevenshteinDistance(name, target);
if (currentDistance < distance) {
distance = currentDistance;
closest = name;
}
}
return closest;
}
@Nullable private final Color defaultColor;
@Nullable private final ImmutableList<String> sortedColorNames;
ColorElement(Text key, @Nullable Color defaultColor) {
super(key);
this.defaultColor = defaultColor;
if (defaultColor != null) {
for (Map.Entry<String, Color> entry : INBUILT_COLORS.entrySet()) {
if (entry.getValue().equals(defaultColor)) {
final ImmutableList.Builder<String> entries = ImmutableList.builder();
entries.add(entry.getKey());
// Add all the other colors except the first element
INBUILT_COLORS.keySet().stream()
.filter(colorName -> !colorName.equals(entry.getKey()))
.forEach(entries::add);
this.sortedColorNames = entries.build();
return;
}
}
}
this.sortedColorNames = null;
}
@Override
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
String rStr = args.next();
// Check for hex format if allowed
if (rStr.startsWith("0x") || rStr.startsWith("#")) {
// Get the hex value without the prefix
String value = rStr.substring(rStr.startsWith("0x") ? 2 : 1);
int hex;
try {
hex = Integer.parseInt(value, 16);
} catch (NumberFormatException e) {
throw args.createError(t("Expected input %s to be hexadecimal, but it was not"));
}
return Color.ofRgb(hex);
}
// Check whether the format matches
if (RGB_PATTERN.matcher(rStr).matches()) {
String gStr;
String bStr;
// Try for the comma-separated format
if (rStr.contains(",")) {
String[] split = rStr.split(",");
if (split.length != 3) {
throw args.createError(t("Comma-separated color must have 3 elements, not %s", split.length));
}
rStr = split[0];
gStr = split[1];
bStr = split[2];
} else {
gStr = args.next();
bStr = args.next();
}
int r = parseComponent(args, rStr, "r");
int g = parseComponent(args, gStr, "g");
int b = parseComponent(args, bStr, "b");
return Color.of(new Vector3i(r, g, b));
}
Color color = INBUILT_COLORS.get(rStr.toLowerCase());
if (color == null) {
throw args.createError(t("Unknown inbuilt color: %s Did you mean: %s ?", rStr, findClosestColorName(rStr)));
}
return color;
}
private static int parseComponent(CommandArgs args, String arg, String name) throws ArgumentParseException {
try {
int value = Integer.parseInt(arg);
if (value < 0) {
throw args.createError(t("Number %s for %s component is too small, it must be at least %s", value, name, 0));
}
if (value > 255) {
throw args.createError(t("Number %s for %s component is too big, it must be at most %s", value, name, 255));
}
return value;
} catch (NumberFormatException e) {
throw args.createError(t("Expected input %s for %s component to be a number, but it was not", arg, name));
}
}
@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
Optional<String> arg = args.nextIfPresent();
if (!arg.isPresent()) {
return Collections.emptyList();
}
final String rStr = arg.get();
if (args.nextIfPresent().isPresent()) {
if (rStr.startsWith("0x") || rStr.startsWith("#") ||
!RGB_PATTERN.matcher(rStr).matches() || rStr.contains(",")) {
return Collections.emptyList();
}
if (args.nextIfPresent().isPresent()) {
// Store the current state
Object state = args.getState();
if (args.nextIfPresent().isPresent()) {
// We finished the vector3d, reset before the last arg
args.setState(state);
} else {
// The blue is being completed
if (this.defaultColor != null) {
return Collections.singletonList(Integer.toString(this.defaultColor.getBlue()));
} else {
return Collections.emptyList();
}
}
} else {
// The green is being completed
if (this.defaultColor != null) {
return Collections.singletonList(Integer.toString(this.defaultColor.getGreen()));
} else {
return Collections.emptyList();
}
}
} else {
if (rStr.isEmpty()) {
if (this.sortedColorNames == null) {
return INBUILT_COLOR_NAMES;
}
return this.sortedColorNames;
}
if (rStr.startsWith("0x") || rStr.startsWith("#")) {
if (this.defaultColor == null) {
return Collections.emptyList();
}
final String prefix = rStr.charAt(0) == '#' ? "#" : "0x";
return Collections.singletonList(prefix + Integer.toHexString(this.defaultColor.getRgb()));
}
if (RGB_PATTERN.matcher(rStr).matches()) {
if (this.defaultColor == null) {
return Collections.emptyList();
}
if (rStr.contains(",")) {
int partCount = rStr.split(",").length;
int index = rStr.lastIndexOf(',');
if (partCount > 3) {
return Collections.emptyList();
}
String begin = index == -1 ? "" : rStr.substring(0, index + 1);
int value = partCount == 0 ? this.defaultColor.getRed() :
partCount == 1 ? this.defaultColor.getGreen() : this.defaultColor.getBlue();
return Collections.singletonList(begin + Integer.toString(value));
}
return Collections.singletonList(Integer.toString(this.defaultColor.getRed()));
} else {
return INBUILT_COLOR_NAMES.stream().filter(new StartsWithPredicate(rStr.toLowerCase()))
.collect(GuavaCollectors.toImmutableList());
}
}
return Collections.emptyList();
}
}
/**
* Gives a string array which contains all the
* remaining arguments.
*
* @param key The key to store the string array under
* @return The element to match the input
*/
public static CommandElement remainingStringArray(Text key) {
return new StringArrayElement(key);
}
private static class StringArrayElement extends CommandElement {
private StringArrayElement(Text key) {
super(key);
}
@Override
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
List<String> values = new ArrayList<>();
// Move the position to the end
while (args.hasNext()) {
String arg = args.next();
if (!arg.isEmpty()) {
values.add(arg);
}
}
return values.toArray(new String[values.size()]);
}
@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
return Collections.emptyList();
}
}
/**
* Gives a string that includes all the remaining
* arguments. This will include the original
* spacing.
*
* @param key The key to store the string under
* @return The element to match the input
*/
public static CommandElement remainingString(Text key) {
return new RemainingStringElement(key);
}
private static class RemainingStringElement extends CommandElement {
private RemainingStringElement(Text key) {
super(key);
}
@Override
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
args.next();
String text = args.getRaw().substring(args.getRawPosition());
// Move the position to the end
while (args.hasNext()) {
args.next();
}
return text;
}
@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
return Collections.emptyList();
}
}
/**
* Require the argument to be a key under the provided
* enum. Gives values of type T.
*
* Unlike the {@link GenericArguments#enumValue(Text, Class)} command element
* are the enum values case insensitive and will the default names of the enum
* be mapped to {@link Object#toString()}.
*
* @param key The key to store the matched enum value under
* @param type The enum class to get enum constants from
* @param <T> The type of enum
* @return The element to match the input
*/
public static <T extends Enum<T>> CommandElement enumValue(Text key, Class<T> type) {
return new EnumValueElement<>(key, type);
}
private static class EnumValueElement<T extends Enum<T>> extends PatternMatchingCommandElement {
private final Map<String, T> mappings;
EnumValueElement(Text key, Class<T> type) {
super(key);
final ImmutableMap.Builder<String, T> builder = ImmutableMap.builder();
for (T enumValue : type.getEnumConstants()) {
builder.put(enumValue.toString().toLowerCase(), enumValue);
}
this.mappings = builder.build();
}
@Override
protected Iterable<String> getChoices(CommandSource source) {
return this.mappings.values().stream().map(Object::toString).collect(Collectors.toList());
}
@Override
protected Object getValue(String choice) throws IllegalArgumentException {
return this.mappings.get(choice.toLowerCase());
}
}
public static CommandElement relativeDoubleNum(Text key) {
return new RelativeDoubleElement(key);
}
public static CommandElement relativeDoubleNum(Text key, @Nullable Double defaultValue) {
return defaultValue == null ? relativeDoubleNum(key) : delegateCompleter(relativeDoubleNum(key),
(src, args, context) -> Collections.singletonList(defaultValue.toString()));
}
public static CommandElement relativeDoubleNum(Text key, @Nullable RelativeDouble defaultValue) {
return defaultValue == null ? relativeDoubleNum(key) : delegateCompleter(relativeDoubleNum(key),
(src, args, context) -> Collections.singletonList(relativeDoubleToString(defaultValue)));
}
private static class RelativeDoubleElement extends CommandElement {
protected RelativeDoubleElement(Text key) {
super(key);
}
@Override
public Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
return parseRelativeDouble(args, args.next());
}
@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
return Collections.emptyList();
}
}
public static CommandElement integer(Text key) {
return GenericArguments.integer(key);
}
public static CommandElement integer(Text key, @Nullable Integer defaultValue) {
return defaultValue == null ? integer(key) : delegateCompleter(integer(key),
(src, args, context) -> Collections.singletonList(Integer.toString(defaultValue)));
}
public static CommandElement doubleNum(Text key) {
return GenericArguments.doubleNum(key);
}
public static CommandElement doubleNum(Text key, @Nullable Double defaultValue) {
return defaultValue == null ? doubleNum(key) : delegateCompleter(doubleNum(key),
(src, args, context) -> Collections.singletonList(Double.toString(defaultValue)));
}
public static CommandElement targetedVector3d(Text key) {
return targetedVector3d(key, null);
}
public static CommandElement targetedVector3d(Text key, @Nullable Vector3d defaultValue) {
return delegateCompleter(vector3d(key), new Vector3dElementCompleter() {
private List<String> complete(CommandContext context, Function<Vector3d, Double> function) {
final Optional<Location<World>> location = context.<Location<World>>getOne(CommandContext.TARGET_BLOCK_ARG);
if (location.isPresent() || defaultValue != null) {
final Vector3d pos = location.map(Location::getPosition).orElse(defaultValue);
return Collections.singletonList(Double.toString(function.apply(pos)));
}
return Collections.emptyList();
}
@Override
protected List<String> completeX(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getX);
}
@Override
protected List<String> completeY(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getY);
}
@Override
protected List<String> completeZ(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getZ);
}
});
}
public static CommandElement targetedRelativeVector3d(Text key, @Nullable Vector3d defaultValue) {
return delegateCompleter(relativeVector3d(key), new Vector3dElementCompleter() {
private List<String> complete(CommandContext context, Function<Vector3d, Double> function) {
final Optional<Location<World>> location = context.<Location<World>>getOne(CommandContext.TARGET_BLOCK_ARG);
if (location.isPresent() || defaultValue != null) {
final Vector3d pos = location.map(Location::getPosition).orElse(defaultValue);
return Collections.singletonList(Double.toString(function.apply(pos)));
}
return Collections.emptyList();
}
@Override
protected List<String> completeX(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getX);
}
@Override
protected List<String> completeY(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getY);
}
@Override
protected List<String> completeZ(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getZ);
}
});
}
public static CommandElement targetedRelativeVector3d(Text key) {
return targetedRelativeVector3d(key, (Vector3d) null);
}
public static CommandElement targetedRelativeVector3d(Text key, @Nullable RelativeVector3d defaultValue) {
return delegateCompleter(relativeVector3d(key), new Vector3dElementCompleter() {
private List<String> complete(CommandContext context, Function<Vector3d, Double> function,
@Nullable RelativeDouble defaultValue) {
final Optional<Location<World>> location = context.<Location<World>>getOne(CommandContext.TARGET_BLOCK_ARG);
if (location.isPresent() || defaultValue != null) {
return Collections.singletonList(location.isPresent() ? Double.toString(
function.apply(location.get().getPosition())) : relativeDoubleToString(defaultValue));
}
return Collections.emptyList();
}
@Override
protected List<String> completeX(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getX, defaultValue == null ? null : defaultValue.getX());
}
@Override
protected List<String> completeY(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getY, defaultValue == null ? null : defaultValue.getY());
}
@Override
protected List<String> completeZ(CommandSource src, CommandContext context) {
return this.complete(context, Vector3d::getZ, defaultValue == null ? null : defaultValue.getZ());
}
});
}
public static CommandElement relativeVector3d(Text key) {
return new RelativeVector3dCommandElement(key);
}
public static CommandElement relativeVector3d(Text key, @Nullable RelativeVector3d defaultValue) {
return defaultValue == null ? relativeVector3d(key) : delegateCompleter(relativeVector3d(key),
new Vector3dElementCompleter() {
@Override
protected List<String> completeX(CommandSource src, CommandContext context) {
return Collections.singletonList(relativeDoubleToString(defaultValue.getX()));
}
@Override
protected List<String> completeY(CommandSource src, CommandContext context) {
return Collections.singletonList(relativeDoubleToString(defaultValue.getY()));
}
@Override
protected List<String> completeZ(CommandSource src, CommandContext context) {
return Collections.singletonList(relativeDoubleToString(defaultValue.getZ()));
}
}
);
}
private static String relativeDoubleToString(RelativeDouble relativeDouble) {
final double value = relativeDouble.getValue();
if (relativeDouble.isRelative()) {
return value == 0 ? "~" : "~" + Double.toString(value);
}
return Double.toString(value);
}
private static class RelativeVector3dCommandElement extends CommandElement {
private static final ImmutableSet<String> SPECIAL_TOKENS = ImmutableSet.of("#target", "#me");
protected RelativeVector3dCommandElement(@Nullable Text key) {
super(key);
}
@Override
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
String xStr;
String yStr;
String zStr;
xStr = args.next();
if (xStr.contains(",")) {
String[] split = xStr.split(",");
if (split.length != 3) {
throw args.createError(t("Comma-separated location must have 3 elements, not %s", split.length));
}
xStr = split[0];
yStr = split[1];
zStr = split[2];
} else if (xStr.equals("#target") && source instanceof Entity) {
Optional<BlockRayHit<World>> hit = BlockRay.from(((Entity) source))
.stopFilter(BlockRay.onlyAirFilter()).build().end();
if (!hit.isPresent()) {
throw args.createError(t("No target block is available! Stop stargazing!"));
}
return hit.get().getPosition();
} else if (xStr.equalsIgnoreCase("#me") && source instanceof Locatable) {
return ((Locatable) source).getLocation().getPosition();
} else {
yStr = args.next();
zStr = args.next();
}
final RelativeDouble x = parseRelativeDouble(args, xStr);
final RelativeDouble y = parseRelativeDouble(args, yStr);
final RelativeDouble z = parseRelativeDouble(args, zStr);
return new RelativeVector3d(x, y, z);
}
@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
Optional<String> arg = args.nextIfPresent();
// Traverse through the possible arguments. We can't really complete arbitrary integers
if (arg.isPresent()) {
if (arg.get().startsWith("#")) {
return SPECIAL_TOKENS.stream().filter(new StartsWithPredicate(arg.get()))
.collect(GuavaCollectors.toImmutableList());
} else if (arg.get().contains(",") || !args.hasNext()) {
return ImmutableList.of(arg.get());
} else {
arg = args.nextIfPresent();
if (args.hasNext()) {
return ImmutableList.of(args.nextIfPresent().get());
} else {
return ImmutableList.of(arg.get());
}
}
} else {
return ImmutableList.of();
}
}
}
private static RelativeDouble parseRelativeDouble(CommandArgs args, String arg) throws ArgumentParseException {
boolean relative = arg.startsWith("~");
double value;
if (relative) {
arg = arg.substring(1);
if (arg.isEmpty()) {
return RelativeDouble.ZERO_RELATIVE;
}
}
try {
value = Double.parseDouble(arg);
} catch (NumberFormatException e) {
throw args.createError(t("Expected input %s to be a double, but was not", arg));
}
return new RelativeDouble(value, relative);
}
public static CommandElement vector3d(Text key) {
return GenericArguments.vector3d(key);
}
public static CommandElement vector3d(Text key, @Nullable Vector3d defaultValue) {
return defaultValue == null ? vector3d(key) : delegateCompleter(vector3d(key), new Vector3dElementCompleter() {
@Override
protected List<String> completeX(CommandSource src, CommandContext context) {
return Collections.singletonList(Double.toString(defaultValue.getX()));
}
@Override
protected List<String> completeY(CommandSource src, CommandContext context) {
return Collections.singletonList(Double.toString(defaultValue.getY()));
}
@Override
protected List<String> completeZ(CommandSource src, CommandContext context) {
return Collections.singletonList(Double.toString(defaultValue.getZ()));
}
});
}
private static abstract class Vector3dElementCompleter implements DelegateCompleter {
protected abstract List<String> completeX(CommandSource src, CommandContext context);
protected abstract List<String> completeY(CommandSource src, CommandContext context);
protected abstract List<String> completeZ(CommandSource src, CommandContext context);
@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context, Completer original) {
Object state = args.getState();
final List<String> completions = original.complete(src, args, context);
// Why are there empty entries in the list?
if (!completions.isEmpty() && completions.size() != 1 && !completions.get(0).isEmpty()) {
return completions;
}
args.setState(state);
if (!args.nextIfPresent().isPresent()) {
return Collections.emptyList();
}
if (args.nextIfPresent().isPresent()) {
if (args.nextIfPresent().isPresent()) {
// Store the current state
state = args.getState();
if (args.nextIfPresent().isPresent()) {
// We finished the vector3d, reset before the last arg
args.setState(state);
Lantern.getLogger().warn("Attempted to complete to many args, vector3d has only 3 components.");
} else {
// The z is being completed
return this.completeZ(src, context);
}
} else {
// The y is being completed
return this.completeY(src, context);
}
} else {
// The x is being completed
return this.completeX(src, context);
}
return Collections.emptyList();
}
}
public static CommandElement delegateCompleter(CommandElement originalElement, Completer delegateCompleter) {
return new DelegateCompleterElement(originalElement,
(src, args, context, original) -> delegateCompleter.complete(src, args, context));
}
public static CommandElement delegateCompleter(CommandElement originalElement, DelegateCompleter delegateCompleter) {
return new DelegateCompleterElement(originalElement, delegateCompleter);
}
private static class DelegateCompleterElement extends CommandElement {
private final CommandElement originalElement;
private final DelegateCompleter delegateCompleter;
protected DelegateCompleterElement(CommandElement originalElement, DelegateCompleter delegateCompleter) {
super(originalElement.getKey());
this.delegateCompleter = delegateCompleter;
this.originalElement = originalElement;
}
@Override
public void parse(CommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException {
this.originalElement.parse(source, args, context);
}
@Nullable
@Override
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
throw new UnsupportedOperationException();
}
@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
return this.delegateCompleter.complete(src, args, context, this.originalElement::complete);
}
}
private GenericArguments2() {
}
}