geforkt von Mirrors/FastAsyncWorldEdit
Re-implement many converters using Piston utility converters
Dieser Commit ist enthalten in:
Ursprung
3c04a83852
Commit
d0f9a71d53
@ -19,49 +19,27 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.command.argument;
|
package com.sk89q.worldedit.command.argument;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
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.CommandManager;
|
||||||
import org.enginehub.piston.converter.ArgumentConverter;
|
import org.enginehub.piston.converter.MultiKeyConverter;
|
||||||
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.inject.Key;
|
import org.enginehub.piston.inject.Key;
|
||||||
|
|
||||||
public class BooleanConverter implements ArgumentConverter<Boolean> {
|
public class BooleanConverter {
|
||||||
|
|
||||||
public static void register(CommandManager commandManager) {
|
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() {
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ public class CommaSeparatedValuesConverter<T> implements ArgumentConverter<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getSuggestions(String input) {
|
public List<String> getSuggestions(String input) {
|
||||||
String lastInput = Iterables.getLast(COMMA.split(input));
|
String lastInput = Iterables.getLast(COMMA.split(input), "");
|
||||||
return delegate.getSuggestions(lastInput);
|
return delegate.getSuggestions(lastInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ import org.enginehub.piston.inject.Key;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix;
|
||||||
|
|
||||||
public class DirectionConverter implements ArgumentConverter<BlockVector3> {
|
public class DirectionConverter implements ArgumentConverter<BlockVector3> {
|
||||||
|
|
||||||
@ -113,8 +114,6 @@ public class DirectionConverter implements ArgumentConverter<BlockVector3> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getSuggestions(String input) {
|
public List<String> getSuggestions(String input) {
|
||||||
return suggestions.stream()
|
return limitByPrefix(suggestions.stream(), input);
|
||||||
.filter(s -> s.startsWith(input))
|
|
||||||
.collect(toList());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,31 +19,20 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.command.argument;
|
package com.sk89q.worldedit.command.argument;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
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.CommandManager;
|
||||||
import org.enginehub.piston.converter.ArgumentConverter;
|
import org.enginehub.piston.converter.ArgumentConverter;
|
||||||
import org.enginehub.piston.converter.ConversionResult;
|
import org.enginehub.piston.converter.MultiKeyConverter;
|
||||||
import org.enginehub.piston.converter.FailedConversion;
|
|
||||||
import org.enginehub.piston.converter.SuccessfulConversion;
|
|
||||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
|
||||||
import org.enginehub.piston.inject.Key;
|
import org.enginehub.piston.inject.Key;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
public class EnumConverter {
|
||||||
import static java.util.stream.Collectors.joining;
|
|
||||||
|
|
||||||
public class EnumConverter<E extends Enum<E>> implements ArgumentConverter<E> {
|
|
||||||
|
|
||||||
public static void register(CommandManager commandManager) {
|
public static void register(CommandManager commandManager) {
|
||||||
commandManager.registerConverter(Key.of(SelectorChoice.class),
|
commandManager.registerConverter(Key.of(SelectorChoice.class),
|
||||||
@ -58,59 +47,25 @@ public class EnumConverter<E extends Enum<E>> implements ArgumentConverter<E> {
|
|||||||
null));
|
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);
|
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);
|
return full(enumClass, e -> ImmutableSet.of(e.name()), unknownValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <E extends Enum<E>> EnumConverter<E> full(Class<E> enumClass,
|
private static <E extends Enum<E>> ArgumentConverter<E> full(Class<E> enumClass,
|
||||||
Function<E, Set<String>> lookupKeys,
|
Function<E, Set<String>> lookupKeys,
|
||||||
@Nullable E unknownValue) {
|
@Nullable E unknownValue) {
|
||||||
return new EnumConverter<>(enumClass, lookupKeys, unknownValue);
|
return MultiKeyConverter.from(
|
||||||
|
EnumSet.allOf(enumClass),
|
||||||
|
lookupKeys,
|
||||||
|
unknownValue
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Component choices;
|
private EnumConverter() {
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,10 @@ import org.enginehub.piston.inject.InjectedValueAccess;
|
|||||||
import org.enginehub.piston.inject.Key;
|
import org.enginehub.piston.inject.Key;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix;
|
||||||
|
|
||||||
public class ExpandAmountConverter implements ArgumentConverter<ExpandAmount> {
|
public class ExpandAmountConverter implements ArgumentConverter<ExpandAmount> {
|
||||||
|
|
||||||
public static void register(CommandManager commandManager) {
|
public static void register(CommandManager commandManager) {
|
||||||
@ -53,9 +54,9 @@ public class ExpandAmountConverter implements ArgumentConverter<ExpandAmount> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getSuggestions(String input) {
|
public List<String> getSuggestions(String input) {
|
||||||
return Stream.concat(Stream.of("vert"), integerConverter.getSuggestions(input).stream())
|
return limitByPrefix(Stream.concat(
|
||||||
.filter(x -> x.startsWith(input))
|
Stream.of("vert"), integerConverter.getSuggestions(input).stream()
|
||||||
.collect(Collectors.toList());
|
), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,54 +19,31 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.command.argument;
|
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.CuboidRegionFactory;
|
||||||
import com.sk89q.worldedit.regions.factory.CylinderRegionFactory;
|
import com.sk89q.worldedit.regions.factory.CylinderRegionFactory;
|
||||||
import com.sk89q.worldedit.regions.factory.RegionFactory;
|
import com.sk89q.worldedit.regions.factory.RegionFactory;
|
||||||
import com.sk89q.worldedit.regions.factory.SphereRegionFactory;
|
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.CommandManager;
|
||||||
import org.enginehub.piston.converter.ArgumentConverter;
|
import org.enginehub.piston.converter.MultiKeyConverter;
|
||||||
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.inject.Key;
|
import org.enginehub.piston.inject.Key;
|
||||||
|
|
||||||
public class RegionFactoryConverter implements ArgumentConverter<RegionFactory> {
|
public class RegionFactoryConverter {
|
||||||
|
|
||||||
public static void register(CommandManager commandManager) {
|
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() {
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.command.argument;
|
package com.sk89q.worldedit.command.argument;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.sk89q.worldedit.registry.Registry;
|
import com.sk89q.worldedit.registry.Registry;
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
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.InjectedValueAccess;
|
||||||
import org.enginehub.piston.inject.Key;
|
import org.enginehub.piston.inject.Key;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix;
|
||||||
|
|
||||||
public class RegistryConverter<V> implements ArgumentConverter<V> {
|
public class RegistryConverter<V> implements ArgumentConverter<V> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public static void register(CommandManager commandManager) {
|
public static void register(CommandManager commandManager) {
|
||||||
commandManager.registerConverter(Key.of(BlockType.class),
|
ImmutableList.of(
|
||||||
new RegistryConverter<>(BlockType.class, BlockType.REGISTRY));
|
BlockType.class,
|
||||||
commandManager.registerConverter(Key.of(BlockCategory.class),
|
BlockCategory.class,
|
||||||
new RegistryConverter<>(BlockCategory.class, BlockCategory.REGISTRY));
|
ItemType.class,
|
||||||
commandManager.registerConverter(Key.of(ItemType.class),
|
ItemCategory.class,
|
||||||
new RegistryConverter<>(ItemType.class, ItemType.REGISTRY));
|
BiomeType.class,
|
||||||
commandManager.registerConverter(Key.of(ItemCategory.class),
|
EntityType.class,
|
||||||
new RegistryConverter<>(ItemCategory.class, ItemCategory.REGISTRY));
|
FluidType.class,
|
||||||
commandManager.registerConverter(Key.of(BiomeType.class),
|
FluidCategory.class,
|
||||||
new RegistryConverter<>(BiomeType.class, BiomeType.REGISTRY));
|
GameMode.class,
|
||||||
commandManager.registerConverter(Key.of(EntityType.class),
|
WeatherType.class
|
||||||
new RegistryConverter<>(EntityType.class, EntityType.REGISTRY));
|
).stream()
|
||||||
commandManager.registerConverter(Key.of(FluidType.class),
|
.map(c -> (Class<Object>) c)
|
||||||
new RegistryConverter<>(FluidType.class, FluidType.REGISTRY));
|
.forEach(registryType ->
|
||||||
commandManager.registerConverter(Key.of(FluidCategory.class),
|
commandManager.registerConverter(Key.of(registryType), from(registryType))
|
||||||
new RegistryConverter<>(FluidCategory.class, FluidCategory.REGISTRY));
|
);
|
||||||
commandManager.registerConverter(Key.of(GameMode.class),
|
}
|
||||||
new RegistryConverter<>(GameMode.class, GameMode.REGISTRY));
|
|
||||||
commandManager.registerConverter(Key.of(WeatherType.class),
|
@SuppressWarnings("unchecked")
|
||||||
new RegistryConverter<>(WeatherType.class, WeatherType.REGISTRY));
|
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 Registry<V> registry;
|
||||||
private final TextComponent choices;
|
private final TextComponent choices;
|
||||||
|
|
||||||
public RegistryConverter(Class<V> clazz, Registry<V> registry) {
|
private RegistryConverter(Class<V> clazz, Registry<V> registry) {
|
||||||
this.registry = registry;
|
this.registry = registry;
|
||||||
this.choices = TextComponent.of("any " + clazz.getSimpleName());
|
this.choices = TextComponent.of("any " + registry.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -85,14 +98,13 @@ public class RegistryConverter<V> implements ArgumentConverter<V> {
|
|||||||
public ConversionResult<V> convert(String argument, InjectedValueAccess injectedValueAccess) {
|
public ConversionResult<V> convert(String argument, InjectedValueAccess injectedValueAccess) {
|
||||||
V result = registry.get(argument);
|
V result = registry.get(argument);
|
||||||
return result == null
|
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);
|
: SuccessfulConversion.fromSingle(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getSuggestions(String input) {
|
public List<String> getSuggestions(String input) {
|
||||||
return registry.keySet().stream()
|
return limitByPrefix(registry.keySet().stream(), input);
|
||||||
.filter(string -> string.startsWith(input))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@ public class Registry<V> implements Iterable<V> {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable V get(final String key) {
|
public @Nullable V get(final String key) {
|
||||||
checkState(key.equals(key.toLowerCase()), "key must be lowercase");
|
checkState(key.equals(key.toLowerCase()), "key must be lowercase");
|
||||||
return this.map.get(key);
|
return this.map.get(key);
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren