Re-implement many converters using Piston utility converters

This commit is contained in:
Kenzie Togami 2019-04-29 21:57:03 -07:00
parent 3c04a83852
commit d0f9a71d53
No known key found for this signature in database
GPG Key ID: 5D200B325E157A81
8 changed files with 91 additions and 165 deletions

View File

@ -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<Boolean> {
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.<Boolean, String>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<String> TRUE = ImmutableSortedSet
.orderedBy(String.CASE_INSENSITIVE_ORDER)
.add("on", "t", "true", "y", "yes")
.build();
private static final ImmutableSortedSet<String> 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<Boolean> 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));
}
}

View File

@ -71,7 +71,7 @@ public class CommaSeparatedValuesConverter<T> implements ArgumentConverter<T> {
@Override
public List<String> getSuggestions(String input) {
String lastInput = Iterables.getLast(COMMA.split(input));
String lastInput = Iterables.getLast(COMMA.split(input), "");
return delegate.getSuggestions(lastInput);
}

View File

@ -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<BlockVector3> {
@ -113,8 +114,6 @@ public class DirectionConverter implements ArgumentConverter<BlockVector3> {
@Override
public List<String> getSuggestions(String input) {
return suggestions.stream()
.filter(s -> s.startsWith(input))
.collect(toList());
return limitByPrefix(suggestions.stream(), input);
}
}

View File

@ -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<E extends Enum<E>> implements ArgumentConverter<E> {
public class EnumConverter {
public static void register(CommandManager commandManager) {
commandManager.registerConverter(Key.of(SelectorChoice.class),
@ -58,59 +47,25 @@ public class EnumConverter<E extends Enum<E>> implements ArgumentConverter<E> {
null));
}
private static <E extends Enum<E>> EnumConverter<E> basic(Class<E> enumClass) {
private static <E extends Enum<E>> ArgumentConverter<E> basic(Class<E> enumClass) {
return full(enumClass, e -> ImmutableSet.of(e.name()), null);
}
private static <E extends Enum<E>> EnumConverter<E> basic(Class<E> enumClass, E unknownValue) {
private static <E extends Enum<E>> ArgumentConverter<E> basic(Class<E> enumClass, E unknownValue) {
return full(enumClass, e -> ImmutableSet.of(e.name()), unknownValue);
}
private static <E extends Enum<E>> EnumConverter<E> full(Class<E> enumClass,
Function<E, Set<String>> lookupKeys,
@Nullable E unknownValue) {
return new EnumConverter<>(enumClass, lookupKeys, unknownValue);
private static <E extends Enum<E>> ArgumentConverter<E> full(Class<E> enumClass,
Function<E, Set<String>> lookupKeys,
@Nullable E unknownValue) {
return MultiKeyConverter.from(
EnumSet.allOf(enumClass),
lookupKeys,
unknownValue
);
}
private final Component choices;
private final ImmutableMap<String, E> map;
@Nullable
private final E unknownValue;
private EnumConverter(Class<E> enumClass,
Function<E, Set<String>> lookupKeys,
@Nullable E unknownValue) {
ImmutableSortedMap.Builder<String, E> map = ImmutableSortedMap.orderedBy(String.CASE_INSENSITIVE_ORDER);
Stream.Builder<Set<String>> choices = Stream.builder();
EnumSet<E> validValues = EnumSet.allOf(enumClass);
if (unknownValue != null) {
validValues.remove(unknownValue);
}
for (E e : validValues) {
Set<String> 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<E> 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);
}
}

View File

@ -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<ExpandAmount> {
public static void register(CommandManager commandManager) {
@ -53,9 +54,9 @@ public class ExpandAmountConverter implements ArgumentConverter<ExpandAmount> {
@Override
public List<String> 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

View File

@ -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<RegionFactory> {
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.<RegionFactory, String>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<RegionFactory> 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);
}
}
}

View File

@ -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<V> implements ArgumentConverter<V> {
@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<Object>) c)
.forEach(registryType ->
commandManager.registerConverter(Key.of(registryType), from(registryType))
);
}
@SuppressWarnings("unchecked")
private static <V> RegistryConverter<V> from(Class<V> registryType) {
try {
Field registryField = registryType.getDeclaredField("REGISTRY");
Registry<V> registry = (Registry<V>) 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<V> registry;
private final TextComponent choices;
public RegistryConverter(Class<V> clazz, Registry<V> registry) {
private RegistryConverter(Class<V> clazz, Registry<V> 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<V> implements ArgumentConverter<V> {
public ConversionResult<V> 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<String> getSuggestions(String input) {
return registry.keySet().stream()
.filter(string -> string.startsWith(input))
.collect(Collectors.toList());
return limitByPrefix(registry.keySet().stream(), input);
}
}

View File

@ -39,6 +39,10 @@ public class Registry<V> implements Iterable<V> {
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);