diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/BooleanConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/BooleanConverter.java index a024cb792..5d4ad5891 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/BooleanConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/BooleanConverter.java @@ -19,49 +19,27 @@ package com.sk89q.worldedit.command.argument; -import com.google.common.collect.ImmutableSortedSet; -import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.google.common.collect.ImmutableSetMultimap; import org.enginehub.piston.CommandManager; -import org.enginehub.piston.converter.ArgumentConverter; -import org.enginehub.piston.converter.ConversionResult; -import org.enginehub.piston.converter.FailedConversion; -import org.enginehub.piston.converter.SuccessfulConversion; -import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.converter.MultiKeyConverter; import org.enginehub.piston.inject.Key; -public class BooleanConverter implements ArgumentConverter { +public class BooleanConverter { public static void register(CommandManager commandManager) { - commandManager.registerConverter(Key.of(Boolean.class), new BooleanConverter()); + commandManager.registerConverter(Key.of(Boolean.class), + MultiKeyConverter.builder( + ImmutableSetMultimap.builder() + .putAll(false, "off", "f", "false", "n", "no") + .putAll(true, "on", "t", "true", "y", "yes") + .build() + ) + .errorMessage(arg -> "Not a boolean value" + arg) + .build() + ); } - private static final ImmutableSortedSet TRUE = ImmutableSortedSet - .orderedBy(String.CASE_INSENSITIVE_ORDER) - .add("on", "t", "true", "y", "yes") - .build(); - - private static final ImmutableSortedSet FALSE = ImmutableSortedSet - .orderedBy(String.CASE_INSENSITIVE_ORDER) - .add("off", "f", "false", "n", "no") - .build(); - private BooleanConverter() { } - @Override - public Component describeAcceptableArguments() { - return TextComponent.of("on|off|true|false"); - } - - @Override - public ConversionResult convert(String argument, InjectedValueAccess context) { - if (TRUE.contains(argument)) { - return SuccessfulConversion.fromSingle(true); - } - if (FALSE.contains(argument)) { - return SuccessfulConversion.fromSingle(false); - } - return FailedConversion.from(new IllegalArgumentException("Not a strictly boolean value: " + argument)); - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java index ba35fa6e4..7479ff375 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java @@ -71,7 +71,7 @@ public class CommaSeparatedValuesConverter implements ArgumentConverter { @Override public List getSuggestions(String input) { - String lastInput = Iterables.getLast(COMMA.split(input)); + String lastInput = Iterables.getLast(COMMA.split(input), ""); return delegate.getSuggestions(lastInput); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java index f9e1c4f1f..aacfe4bf5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java @@ -41,6 +41,7 @@ import org.enginehub.piston.inject.Key; import java.util.List; import static java.util.stream.Collectors.toList; +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; public class DirectionConverter implements ArgumentConverter { @@ -113,8 +114,6 @@ public class DirectionConverter implements ArgumentConverter { @Override public List getSuggestions(String input) { - return suggestions.stream() - .filter(s -> s.startsWith(input)) - .collect(toList()); + return limitByPrefix(suggestions.stream(), input); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java index a408a2793..14a49b38c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java @@ -19,31 +19,20 @@ package com.sk89q.worldedit.command.argument; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedMap; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.util.TreeGenerator; -import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.formatting.text.TextComponent; import org.enginehub.piston.CommandManager; import org.enginehub.piston.converter.ArgumentConverter; -import org.enginehub.piston.converter.ConversionResult; -import org.enginehub.piston.converter.FailedConversion; -import org.enginehub.piston.converter.SuccessfulConversion; -import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.converter.MultiKeyConverter; import org.enginehub.piston.inject.Key; import javax.annotation.Nullable; import java.util.EnumSet; import java.util.Set; import java.util.function.Function; -import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkState; -import static java.util.stream.Collectors.joining; - -public class EnumConverter> implements ArgumentConverter { +public class EnumConverter { public static void register(CommandManager commandManager) { commandManager.registerConverter(Key.of(SelectorChoice.class), @@ -58,59 +47,25 @@ public class EnumConverter> implements ArgumentConverter { null)); } - private static > EnumConverter basic(Class enumClass) { + private static > ArgumentConverter basic(Class enumClass) { return full(enumClass, e -> ImmutableSet.of(e.name()), null); } - private static > EnumConverter basic(Class enumClass, E unknownValue) { + private static > ArgumentConverter basic(Class enumClass, E unknownValue) { return full(enumClass, e -> ImmutableSet.of(e.name()), unknownValue); } - private static > EnumConverter full(Class enumClass, - Function> lookupKeys, - @Nullable E unknownValue) { - return new EnumConverter<>(enumClass, lookupKeys, unknownValue); + private static > ArgumentConverter full(Class enumClass, + Function> lookupKeys, + @Nullable E unknownValue) { + return MultiKeyConverter.from( + EnumSet.allOf(enumClass), + lookupKeys, + unknownValue + ); } - private final Component choices; - private final ImmutableMap map; - @Nullable - private final E unknownValue; - - private EnumConverter(Class enumClass, - Function> lookupKeys, - @Nullable E unknownValue) { - ImmutableSortedMap.Builder map = ImmutableSortedMap.orderedBy(String.CASE_INSENSITIVE_ORDER); - Stream.Builder> choices = Stream.builder(); - EnumSet validValues = EnumSet.allOf(enumClass); - if (unknownValue != null) { - validValues.remove(unknownValue); - } - for (E e : validValues) { - Set keys = lookupKeys.apply(e); - checkState(keys.size() > 0, "No lookup keys for enum value %s", e); - choices.add(keys); - for (String key : keys) { - map.put(key, e); - } - } - this.choices = TextComponent.of(choices.build() - .map(choice -> choice.stream().collect(joining("|", "[", "]"))) - .collect(joining("|"))); - this.map = map.build(); - this.unknownValue = unknownValue; + private EnumConverter() { } - @Override - public Component describeAcceptableArguments() { - return choices; - } - - @Override - public ConversionResult convert(String argument, InjectedValueAccess context) { - E result = map.getOrDefault(argument, unknownValue); - return result == null - ? FailedConversion.from(new IllegalArgumentException("Not a valid choice: " + argument)) - : SuccessfulConversion.fromSingle(result); - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmountConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmountConverter.java index c7a4b5bdd..4d431ebca 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmountConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmountConverter.java @@ -31,9 +31,10 @@ import org.enginehub.piston.inject.InjectedValueAccess; import org.enginehub.piston.inject.Key; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; + public class ExpandAmountConverter implements ArgumentConverter { public static void register(CommandManager commandManager) { @@ -53,9 +54,9 @@ public class ExpandAmountConverter implements ArgumentConverter { @Override public List getSuggestions(String input) { - return Stream.concat(Stream.of("vert"), integerConverter.getSuggestions(input).stream()) - .filter(x -> x.startsWith(input)) - .collect(Collectors.toList()); + return limitByPrefix(Stream.concat( + Stream.of("vert"), integerConverter.getSuggestions(input).stream() + ), input); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegionFactoryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegionFactoryConverter.java index ed54327b1..15624d31b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegionFactoryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegionFactoryConverter.java @@ -19,54 +19,31 @@ package com.sk89q.worldedit.command.argument; +import com.google.common.collect.ImmutableSetMultimap; import com.sk89q.worldedit.regions.factory.CuboidRegionFactory; import com.sk89q.worldedit.regions.factory.CylinderRegionFactory; import com.sk89q.worldedit.regions.factory.RegionFactory; import com.sk89q.worldedit.regions.factory.SphereRegionFactory; -import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.formatting.text.TextComponent; import org.enginehub.piston.CommandManager; -import org.enginehub.piston.converter.ArgumentConverter; -import org.enginehub.piston.converter.ConversionResult; -import org.enginehub.piston.converter.FailedConversion; -import org.enginehub.piston.converter.SuccessfulConversion; -import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.converter.MultiKeyConverter; import org.enginehub.piston.inject.Key; -public class RegionFactoryConverter implements ArgumentConverter { +public class RegionFactoryConverter { public static void register(CommandManager commandManager) { - commandManager.registerConverter(Key.of(RegionFactory.class), new RegionFactoryConverter()); + commandManager.registerConverter(Key.of(RegionFactory.class), + MultiKeyConverter.builder( + ImmutableSetMultimap.builder() + .put(new CuboidRegionFactory(), "cuboid") + .put(new SphereRegionFactory(), "sphere") + .putAll(new CylinderRegionFactory(1), "cyl", "cylinder") + .build() + ) + .errorMessage(arg -> "Not a known region type: " + arg) + .build() + ); } private RegionFactoryConverter() { } - - @Override - public Component describeAcceptableArguments() { - return TextComponent.of("cuboid|sphere|cyl"); - } - - @Override - public ConversionResult convert(String argument, InjectedValueAccess context) { - try { - return SuccessfulConversion.fromSingle(parse(argument)); - } catch (Exception e) { - return FailedConversion.from(e); - } - } - - private RegionFactory parse(String argument) { - switch (argument) { - case "cuboid": - return new CuboidRegionFactory(); - case "sphere": - return new SphereRegionFactory(); - case "cyl": - case "cylinder": - return new CylinderRegionFactory(1); // TODO: Adjustable height - default: - throw new IllegalArgumentException("Not a known region type: " + argument); - } - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java index 5e9d2ed66..7bafb86e9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.command.argument; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.registry.Registry; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -40,40 +41,52 @@ import org.enginehub.piston.converter.SuccessfulConversion; import org.enginehub.piston.inject.InjectedValueAccess; import org.enginehub.piston.inject.Key; +import java.lang.reflect.Field; import java.util.List; -import java.util.stream.Collectors; + +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; public class RegistryConverter implements ArgumentConverter { + @SuppressWarnings("unchecked") public static void register(CommandManager commandManager) { - commandManager.registerConverter(Key.of(BlockType.class), - new RegistryConverter<>(BlockType.class, BlockType.REGISTRY)); - commandManager.registerConverter(Key.of(BlockCategory.class), - new RegistryConverter<>(BlockCategory.class, BlockCategory.REGISTRY)); - commandManager.registerConverter(Key.of(ItemType.class), - new RegistryConverter<>(ItemType.class, ItemType.REGISTRY)); - commandManager.registerConverter(Key.of(ItemCategory.class), - new RegistryConverter<>(ItemCategory.class, ItemCategory.REGISTRY)); - commandManager.registerConverter(Key.of(BiomeType.class), - new RegistryConverter<>(BiomeType.class, BiomeType.REGISTRY)); - commandManager.registerConverter(Key.of(EntityType.class), - new RegistryConverter<>(EntityType.class, EntityType.REGISTRY)); - commandManager.registerConverter(Key.of(FluidType.class), - new RegistryConverter<>(FluidType.class, FluidType.REGISTRY)); - commandManager.registerConverter(Key.of(FluidCategory.class), - new RegistryConverter<>(FluidCategory.class, FluidCategory.REGISTRY)); - commandManager.registerConverter(Key.of(GameMode.class), - new RegistryConverter<>(GameMode.class, GameMode.REGISTRY)); - commandManager.registerConverter(Key.of(WeatherType.class), - new RegistryConverter<>(WeatherType.class, WeatherType.REGISTRY)); + ImmutableList.of( + BlockType.class, + BlockCategory.class, + ItemType.class, + ItemCategory.class, + BiomeType.class, + EntityType.class, + FluidType.class, + FluidCategory.class, + GameMode.class, + WeatherType.class + ).stream() + .map(c -> (Class) c) + .forEach(registryType -> + commandManager.registerConverter(Key.of(registryType), from(registryType)) + ); + } + + @SuppressWarnings("unchecked") + private static RegistryConverter from(Class registryType) { + try { + Field registryField = registryType.getDeclaredField("REGISTRY"); + Registry registry = (Registry) registryField.get(null); + return new RegistryConverter<>(registryType, registry); + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException("Not a registry-backed type: " + registryType.getName()); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Registry field inaccessible on " + registryType.getName()); + } } private final Registry registry; private final TextComponent choices; - public RegistryConverter(Class clazz, Registry registry) { + private RegistryConverter(Class clazz, Registry registry) { this.registry = registry; - this.choices = TextComponent.of("any " + clazz.getSimpleName()); + this.choices = TextComponent.of("any " + registry.getName()); } @Override @@ -85,14 +98,13 @@ public class RegistryConverter implements ArgumentConverter { public ConversionResult convert(String argument, InjectedValueAccess injectedValueAccess) { V result = registry.get(argument); return result == null - ? FailedConversion.from(new IllegalArgumentException("Not a valid choice: " + argument)) + ? FailedConversion.from(new IllegalArgumentException( + "Not a valid " + registry.getName() + ": " + argument)) : SuccessfulConversion.fromSingle(result); } @Override public List getSuggestions(String input) { - return registry.keySet().stream() - .filter(string -> string.startsWith(input)) - .collect(Collectors.toList()); + return limitByPrefix(registry.keySet().stream(), input); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java index 7545068de..e7d7e9e58 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java @@ -39,6 +39,10 @@ public class Registry implements Iterable { this.name = name; } + public String getName() { + return name; + } + public @Nullable V get(final String key) { checkState(key.equals(key.toLowerCase()), "key must be lowercase"); return this.map.get(key);