✨ Work towards making CommandMeta typesafe (#173)
Co-authored-by: Alexander Söderberg <sauilitired@gmail.com>
This commit is contained in:
parent
ab366be24d
commit
1e91273e0e
33 changed files with 450 additions and 64 deletions
|
|
@ -269,7 +269,7 @@ public class Command<C> {
|
|||
* @return {@code true} if the command is hidden, {@code false} if not
|
||||
*/
|
||||
public boolean isHidden() {
|
||||
return this.getCommandMeta().getOrDefault("hidden", "true").equals("true");
|
||||
return this.getCommandMeta().getOrDefault(CommandMeta.HIDDEN, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -337,7 +337,9 @@ public class Command<C> {
|
|||
* @param key Meta key
|
||||
* @param value Meta value
|
||||
* @return New builder instance using the inserted meta key-value pair
|
||||
* @deprecated for removal since 1.2.0, use the typesafe variant at {@link #meta(CommandMeta.Key, Object)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public @NonNull Builder<C> meta(final @NonNull String key, final @NonNull String value) {
|
||||
final CommandMeta commandMeta = SimpleCommandMeta.builder().with(this.commandMeta).with(key, value).build();
|
||||
return new Builder<>(
|
||||
|
|
@ -351,6 +353,28 @@ public class Command<C> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add command meta to the internal command meta map
|
||||
*
|
||||
* @param <V> Meta value type
|
||||
* @param key Meta key
|
||||
* @param value Meta value
|
||||
* @return New builder instance using the inserted meta key-value pair
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public <V> @NonNull Builder<C> meta(final CommandMeta.@NonNull Key<V> key, final @NonNull V value) {
|
||||
final CommandMeta commandMeta = SimpleCommandMeta.builder().with(this.commandMeta).with(key, value).build();
|
||||
return new Builder<>(
|
||||
this.commandManager,
|
||||
commandMeta,
|
||||
this.senderType,
|
||||
this.commandArguments,
|
||||
this.commandExecutionHandler,
|
||||
this.commandPermission,
|
||||
this.flags
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supply a command manager instance to the builder. This will be used when attempting to
|
||||
* retrieve command argument parsers, in the case that they're needed. This
|
||||
|
|
@ -746,7 +770,7 @@ public class Command<C> {
|
|||
* @return New builder instance that indicates that the constructed command should be hidden
|
||||
*/
|
||||
public @NonNull Builder<C> hidden() {
|
||||
return this.meta("hidden", "true");
|
||||
return this.meta(CommandMeta.HIDDEN, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ package cloud.commandframework;
|
|||
|
||||
import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.StaticArgument;
|
||||
import cloud.commandframework.meta.CommandMeta;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ public final class CommandHelpHandler<C> {
|
|||
final List<VerboseHelpEntry<C>> syntaxHints = new ArrayList<>();
|
||||
for (final Command<C> command : this.commandManager.getCommands()) {
|
||||
final List<CommandArgument<C, ?>> arguments = command.getArguments();
|
||||
final String description = command.getCommandMeta().getOrDefault("description", "");
|
||||
final String description = command.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, "");
|
||||
syntaxHints.add(new VerboseHelpEntry<>(
|
||||
command,
|
||||
this.commandManager.getCommandSyntaxFormatter()
|
||||
|
|
@ -163,7 +164,7 @@ public final class CommandHelpHandler<C> {
|
|||
final List<VerboseHelpEntry<C>> syntaxHints = new ArrayList<>();
|
||||
for (final Command<C> command : availableCommands) {
|
||||
final List<CommandArgument<C, ?>> arguments = command.getArguments();
|
||||
final String description = command.getCommandMeta().getOrDefault("description", "");
|
||||
final String description = command.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, "");
|
||||
syntaxHints.add(new VerboseHelpEntry<>(
|
||||
command,
|
||||
this.commandManager.getCommandSyntaxFormatter()
|
||||
|
|
@ -350,8 +351,8 @@ public final class CommandHelpHandler<C> {
|
|||
|
||||
private VerboseHelpTopic(final @NonNull Command<C> command) {
|
||||
this.command = command;
|
||||
final String shortDescription = command.getCommandMeta().getOrDefault("description", "No description");
|
||||
this.description = command.getCommandMeta().getOrDefault("long-description", shortDescription);
|
||||
final String shortDescription = command.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, "No description");
|
||||
this.description = command.getCommandMeta().getOrDefault(CommandMeta.LONG_DESCRIPTION, shortDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ final class AnnotatedElementAccessor implements AnnotationAccessor {
|
|||
|
||||
@Override
|
||||
public <A extends Annotation> @Nullable A annotation(
|
||||
@NonNull final Class<A> clazz
|
||||
final @NonNull Class<A> clazz
|
||||
) {
|
||||
try {
|
||||
return element.getAnnotation(clazz);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public interface AnnotationAccessor {
|
|||
final class NullAnnotationAccessor implements AnnotationAccessor {
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> @Nullable A annotation(@NonNull final Class<A> clazz) {
|
||||
public <A extends Annotation> @Nullable A annotation(final @NonNull Class<A> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ public final class DelegatingCommandSuggestionEngine<C> implements CommandSugges
|
|||
|
||||
@Override
|
||||
public @NonNull List<@NonNull String> getSuggestions(
|
||||
@NonNull final CommandContext<C> context,
|
||||
@NonNull final String input
|
||||
final @NonNull CommandContext<C> context,
|
||||
final @NonNull String input
|
||||
) {
|
||||
final @NonNull LinkedList<@NonNull String> inputQueue = new CommandInputTokenizer(input).tokenize();
|
||||
/* Store a copy of the input queue in the context */
|
||||
|
|
|
|||
|
|
@ -232,15 +232,15 @@ public final class StandardParserRegistry<C> implements ParserRegistry<C> {
|
|||
|
||||
@Override
|
||||
public void registerSuggestionProvider(
|
||||
@NonNull final String name,
|
||||
@NonNull final BiFunction<@NonNull CommandContext<C>, @NonNull String, @NonNull List<String>> suggestionsProvider
|
||||
final @NonNull String name,
|
||||
final @NonNull BiFunction<@NonNull CommandContext<C>, @NonNull String, @NonNull List<String>> suggestionsProvider
|
||||
) {
|
||||
this.namedSuggestionProviders.put(name.toLowerCase(Locale.ENGLISH), suggestionsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Optional<BiFunction<@NonNull CommandContext<C>, @NonNull String, @NonNull List<String>>> getSuggestionProvider(
|
||||
@NonNull final String name
|
||||
final @NonNull String name
|
||||
) {
|
||||
final BiFunction<@NonNull CommandContext<C>, @NonNull String, @NonNull List<String>> suggestionProvider =
|
||||
this.namedSuggestionProviders.get(name.toLowerCase(Locale.ENGLISH));
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public final class RegexPreprocessor<C> implements BiFunction<@NonNull CommandCo
|
|||
|
||||
@Override
|
||||
public @NonNull ArgumentParseResult<Boolean> apply(
|
||||
@NonNull final CommandContext<C> context, @NonNull final Queue<@NonNull String> strings
|
||||
final @NonNull CommandContext<C> context, final @NonNull Queue<@NonNull String> strings
|
||||
) {
|
||||
final String head = strings.peek();
|
||||
if (head == null) {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class EnumArgument<C, E extends Enum<E>> extends CommandArgument<C, E> {
|
|||
* @return Created builder
|
||||
*/
|
||||
public static <C, E extends Enum<E>> EnumArgument.@NonNull Builder<C, E> newBuilder(
|
||||
@NonNull final Class<E> enumClass,
|
||||
final @NonNull Class<E> enumClass,
|
||||
final @NonNull String name
|
||||
) {
|
||||
return new EnumArgument.Builder<>(name, enumClass);
|
||||
|
|
|
|||
|
|
@ -107,8 +107,8 @@ public final class StringArrayArgument<C> extends CommandArgument<C, String[]> {
|
|||
|
||||
@Override
|
||||
public @NonNull ArgumentParseResult<String @NonNull []> parse(
|
||||
@NonNull final CommandContext<@NonNull C> commandContext,
|
||||
@NonNull final Queue<@NonNull String> inputQueue
|
||||
final @NonNull CommandContext<@NonNull C> commandContext,
|
||||
final @NonNull Queue<@NonNull String> inputQueue
|
||||
) {
|
||||
final String[] result = new String[inputQueue.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
|
|
|
|||
|
|
@ -140,8 +140,8 @@ public class SimpleCaptionRegistry<C> implements FactoryDelegatingCaptionRegistr
|
|||
|
||||
@Override
|
||||
public final @NonNull String getCaption(
|
||||
@NonNull final Caption caption,
|
||||
@NonNull final C sender
|
||||
final @NonNull Caption caption,
|
||||
final @NonNull C sender
|
||||
) {
|
||||
final BiFunction<Caption, C, String> messageFactory = this.messageFactories.get(caption);
|
||||
if (messageFactory == null) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import cloud.commandframework.CommandManager;
|
|||
import cloud.commandframework.execution.CommandExecutionHandler;
|
||||
import cloud.commandframework.execution.postprocessor.CommandPostprocessingContext;
|
||||
import cloud.commandframework.execution.postprocessor.CommandPostprocessor;
|
||||
import cloud.commandframework.meta.CommandMeta;
|
||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||
import cloud.commandframework.services.types.ConsumerService;
|
||||
import cloud.commandframework.types.tuples.Pair;
|
||||
|
|
@ -54,8 +55,27 @@ public class CommandConfirmationManager<C> {
|
|||
|
||||
/**
|
||||
* Meta data stored for commands that require confirmation
|
||||
*
|
||||
* @deprecated for removal since 1.3.0. Use {@link #META_CONFIRMATION_REQUIRED} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String CONFIRMATION_REQUIRED_META = "__REQUIRE_CONFIRMATION__";
|
||||
|
||||
private static final CommandMeta.Key<String> LEGACY_CONFIRMATION_META = CommandMeta.Key.of(
|
||||
String.class,
|
||||
CONFIRMATION_REQUIRED_META
|
||||
);
|
||||
|
||||
/**
|
||||
* Meta data stored for commands that require confirmation
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public static final CommandMeta.Key<Boolean> META_CONFIRMATION_REQUIRED = CommandMeta.Key.of(
|
||||
Boolean.class,
|
||||
"cloud:require_confirmation",
|
||||
meta -> meta.get(LEGACY_CONFIRMATION_META).map(Boolean::valueOf).orElse(null)
|
||||
);
|
||||
private static final int MAXIMUM_PENDING_SIZE = 100;
|
||||
|
||||
private final Consumer<CommandPostprocessingContext<C>> notifier;
|
||||
|
|
@ -120,7 +140,7 @@ public class CommandConfirmationManager<C> {
|
|||
* @return Builder instance
|
||||
*/
|
||||
public SimpleCommandMeta.@NonNull Builder decorate(final SimpleCommandMeta.@NonNull Builder builder) {
|
||||
return builder.with(CONFIRMATION_REQUIRED_META, "true");
|
||||
return builder.with(META_CONFIRMATION_REQUIRED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -158,8 +178,7 @@ public class CommandConfirmationManager<C> {
|
|||
public void accept(final @NonNull CommandPostprocessingContext<C> context) {
|
||||
if (!context.getCommand()
|
||||
.getCommandMeta()
|
||||
.getOrDefault(CONFIRMATION_REQUIRED_META, "false")
|
||||
.equals("true")) {
|
||||
.getOrDefault(META_CONFIRMATION_REQUIRED, false)) {
|
||||
return;
|
||||
}
|
||||
/* Add it to the "queue" */
|
||||
|
|
|
|||
|
|
@ -24,10 +24,16 @@
|
|||
package cloud.commandframework.meta;
|
||||
|
||||
import cloud.commandframework.Command;
|
||||
import io.leangen.geantyref.GenericTypeReflector;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Object that is associated with a {@link Command}.
|
||||
|
|
@ -37,6 +43,15 @@ import java.util.Optional;
|
|||
*/
|
||||
public abstract class CommandMeta {
|
||||
|
||||
private static final Key<String> LEGACY_HIDDEN = Key.of(String.class, "hidden");
|
||||
public static final Key<String> DESCRIPTION = Key.of(String.class, "description");
|
||||
public static final Key<String> LONG_DESCRIPTION = Key.of(String.class, "long-description");
|
||||
public static final Key<Boolean> HIDDEN = Key.of(
|
||||
Boolean.class,
|
||||
"cloud:hidden",
|
||||
meta -> Boolean.valueOf(meta.getOrDefault(LEGACY_HIDDEN, "false"))
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a new simple command meta builder
|
||||
*
|
||||
|
|
@ -56,7 +71,9 @@ public abstract class CommandMeta {
|
|||
*
|
||||
* @param key Key
|
||||
* @return Optional that may contain the associated value
|
||||
* @deprecated for removal since 1.3.0, see typesafe variant at {@link #get(Key)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract @NonNull Optional<String> getValue(@NonNull String key);
|
||||
|
||||
/**
|
||||
|
|
@ -65,14 +82,157 @@ public abstract class CommandMeta {
|
|||
* @param key Key
|
||||
* @param defaultValue Default value
|
||||
* @return Value, or default value
|
||||
* @deprecated for removal since 1.3.0, see typesafe variant at {@link #getOrDefault(Key, Object)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract @NonNull String getOrDefault(@NonNull String key, @NonNull String defaultValue);
|
||||
|
||||
/**
|
||||
* Get the value associated with a key.
|
||||
*
|
||||
* @param <V> Value type
|
||||
* @param key Key
|
||||
* @return Optional that may contain the associated value
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public abstract <V> @NonNull Optional<V> get(@NonNull Key<V> key);
|
||||
|
||||
/**
|
||||
* Get the value if it exists, else return the default value.
|
||||
*
|
||||
* @param <V> Value type
|
||||
* @param key Key
|
||||
* @param defaultValue Default value
|
||||
* @return Value, or default value
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public abstract <V> @NonNull V getOrDefault(@NonNull Key<V> key, @NonNull V defaultValue);
|
||||
|
||||
/**
|
||||
* Get a copy of the meta map
|
||||
*
|
||||
* @return Copy of meta map
|
||||
* @deprecated for removal since 1.3.0, use {@link #getAllValues()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract @NonNull Map<@NonNull String, @NonNull String> getAll();
|
||||
|
||||
/**
|
||||
* Get a copy of the meta map, without type information.
|
||||
*
|
||||
* @return Copy of meta map
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public abstract @NonNull Map<@NonNull String, @NonNull ?> getAllValues();
|
||||
|
||||
/**
|
||||
* A key into the metadata map.
|
||||
*
|
||||
* @param <V> value type
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public interface Key<V> {
|
||||
|
||||
/**
|
||||
* Create a new metadata key.
|
||||
*
|
||||
* @param type the value type
|
||||
* @param key the name for the key
|
||||
* @param <T> the value type
|
||||
* @return a new key
|
||||
*/
|
||||
static <T> @NonNull Key<T> of(final @NonNull Class<T> type, final @NonNull String key) {
|
||||
if (GenericTypeReflector.isMissingTypeParameters(type)) {
|
||||
throw new IllegalArgumentException("Raw type " + type + " is prohibited");
|
||||
}
|
||||
|
||||
return new SimpleKey<>(
|
||||
TypeToken.get(requireNonNull(type, "type")),
|
||||
requireNonNull(key, "key"),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new metadata key.
|
||||
*
|
||||
* @param type the value type
|
||||
* @param key the name for the key
|
||||
* @param <T> the value type
|
||||
* @return a new key
|
||||
*/
|
||||
static <T> @NonNull Key<T> of(final @NonNull TypeToken<T> type, final @NonNull String key) {
|
||||
return new SimpleKey<>(
|
||||
requireNonNull(type, "type"),
|
||||
requireNonNull(key, "key"),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new metadata key.
|
||||
*
|
||||
* @param type the value type
|
||||
* @param key the name for the key
|
||||
* @param fallbackDerivation A function that will be called if no value is present for the key
|
||||
* @param <T> the value type
|
||||
* @return a new key
|
||||
*/
|
||||
static <T> @NonNull Key<T> of(
|
||||
final @NonNull Class<T> type,
|
||||
final @NonNull String key,
|
||||
final @NonNull Function<@NonNull CommandMeta, @Nullable T> fallbackDerivation) {
|
||||
return new SimpleKey<>(
|
||||
TypeToken.get(requireNonNull(type, "type")),
|
||||
requireNonNull(key, "key"),
|
||||
fallbackDerivation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new metadata key.
|
||||
*
|
||||
* @param type the value type
|
||||
* @param key the name for the key
|
||||
* @param fallbackDerivation A function that will be called if no value is present for the key
|
||||
* @param <T> the value type
|
||||
* @return a new key
|
||||
*/
|
||||
static <T> @NonNull Key<T> of(
|
||||
final @NonNull TypeToken<T> type,
|
||||
final @NonNull String key,
|
||||
final @NonNull Function<@NonNull CommandMeta, @Nullable T> fallbackDerivation
|
||||
) {
|
||||
return new SimpleKey<>(
|
||||
requireNonNull(type, "type"),
|
||||
requireNonNull(key, "key"),
|
||||
fallbackDerivation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a representation of the type of value this key holds.
|
||||
*
|
||||
* @return the value type
|
||||
*/
|
||||
@NonNull TypeToken<V> getValueType();
|
||||
|
||||
/**
|
||||
* Get the name of this key
|
||||
*
|
||||
* @return the key type
|
||||
*/
|
||||
@NonNull String getName();
|
||||
|
||||
/**
|
||||
* Get a function that can be used to compute a fallback based on existing meta.
|
||||
*
|
||||
* <p>This function will only be used if no value is directly for the key.</p>
|
||||
*
|
||||
* @return the fallback derivation
|
||||
*/
|
||||
@Nullable Function<@NonNull CommandMeta, @Nullable V> getFallbackDerivation();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
//
|
||||
package cloud.commandframework.meta;
|
||||
|
||||
import io.leangen.geantyref.GenericTypeReflector;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Collections;
|
||||
|
|
@ -30,6 +31,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A simple immutable string-string map containing command meta
|
||||
|
|
@ -37,12 +39,22 @@ import java.util.Optional;
|
|||
@SuppressWarnings("unused")
|
||||
public class SimpleCommandMeta extends CommandMeta {
|
||||
|
||||
private final Map<String, String> metaMap;
|
||||
private final Map<String, Object> metaMap;
|
||||
|
||||
@Deprecated
|
||||
protected SimpleCommandMeta(final @NonNull Map<@NonNull String, @NonNull String> metaMap) {
|
||||
this.metaMap = Collections.unmodifiableMap(metaMap);
|
||||
}
|
||||
|
||||
protected SimpleCommandMeta(final SimpleCommandMeta source) {
|
||||
this.metaMap = source.metaMap;
|
||||
}
|
||||
|
||||
// Constructor needs an extra flag to distinguish it from the old one (for reified generics)
|
||||
SimpleCommandMeta(final @NonNull Map<@NonNull String, @NonNull Object> metaMap, final boolean unusedMarkerForNew) {
|
||||
this.metaMap = Collections.unmodifiableMap(metaMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new meta builder
|
||||
*
|
||||
|
|
@ -62,35 +74,78 @@ public class SimpleCommandMeta extends CommandMeta {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final @NonNull Optional<String> getValue(final @NonNull String key) {
|
||||
return Optional.ofNullable(this.metaMap.get(key));
|
||||
final Object result = this.metaMap.get(key);
|
||||
if (result != null && !(result instanceof String)) {
|
||||
throw new IllegalArgumentException("Key '" + key + "' has been used for a new typed command meta and contains a "
|
||||
+ "non-string value!");
|
||||
}
|
||||
return Optional.ofNullable((String) result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final @NonNull String getOrDefault(final @NonNull String key, final @NonNull String defaultValue) {
|
||||
return this.getValue(key).orElse(defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final @NonNull <V> Optional<V> get(final @NonNull Key<V> key) {
|
||||
final Object value = this.metaMap.get(key.getName());
|
||||
if (value == null) {
|
||||
// Attempt to use a fallback legacy type
|
||||
if (key.getFallbackDerivation() != null) {
|
||||
return Optional.ofNullable(key.getFallbackDerivation().apply(this));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
if (!GenericTypeReflector.isSuperType(key.getValueType().getType(), value.getClass())) {
|
||||
throw new IllegalArgumentException("Conflicting argument types between key type of "
|
||||
+ key.getValueType().getType() + " and value type of " + value.getClass());
|
||||
}
|
||||
|
||||
return Optional.of((V) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <V> @NonNull V getOrDefault(final @NonNull Key<V> key, final @NonNull V defaultValue) {
|
||||
return this.get(key).orElse(defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final @NonNull Map<@NonNull String, @NonNull String> getAll() {
|
||||
return this.metaMap.entrySet()
|
||||
.stream().filter(ent -> ent.getValue() instanceof String)
|
||||
.collect(Collectors.<Map.Entry<String, Object>, String, String>toMap(
|
||||
Map.Entry::getKey,
|
||||
ent -> ent.getValue().toString()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @NonNull Map<@NonNull String, @NonNull ?> getAllValues() {
|
||||
return new HashMap<>(this.metaMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
public final boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final SimpleCommandMeta that = (SimpleCommandMeta) o;
|
||||
return Objects.equals(metaMap, that.metaMap);
|
||||
final SimpleCommandMeta that = (SimpleCommandMeta) other;
|
||||
return Objects.equals(this.metaMap, that.metaMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return Objects.hashCode(metaMap);
|
||||
return Objects.hashCode(this.metaMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -98,7 +153,7 @@ public class SimpleCommandMeta extends CommandMeta {
|
|||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private final Map<String, String> map = new HashMap<>();
|
||||
private final Map<String, Object> map = new HashMap<>();
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
|
@ -110,7 +165,11 @@ public class SimpleCommandMeta extends CommandMeta {
|
|||
* @return Builder instance
|
||||
*/
|
||||
public @NonNull Builder with(final @NonNull CommandMeta commandMeta) {
|
||||
commandMeta.getAll().forEach(this::with);
|
||||
if (commandMeta instanceof SimpleCommandMeta) {
|
||||
this.map.putAll(((SimpleCommandMeta) commandMeta).metaMap);
|
||||
} else {
|
||||
this.map.putAll(commandMeta.getAllValues());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +179,9 @@ public class SimpleCommandMeta extends CommandMeta {
|
|||
* @param key Key
|
||||
* @param value Value
|
||||
* @return Builder instance
|
||||
* @deprecated For removal since 1.3.0, use {@link #with(Key, Object) the typesafe alternative} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public @NonNull Builder with(
|
||||
final @NonNull String key,
|
||||
final @NonNull String value
|
||||
|
|
@ -129,13 +190,30 @@ public class SimpleCommandMeta extends CommandMeta {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new key-value pair in the meta map
|
||||
*
|
||||
* @param <V> Value type
|
||||
* @param key Key
|
||||
* @param value Value
|
||||
* @return Builder instance
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public <V> @NonNull Builder with(
|
||||
final @NonNull Key<V> key,
|
||||
final @NonNull V value
|
||||
) {
|
||||
this.map.put(key.getName(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new meta instance
|
||||
*
|
||||
* @return Meta instance
|
||||
*/
|
||||
public @NonNull SimpleCommandMeta build() {
|
||||
return new SimpleCommandMeta(this.map);
|
||||
return new SimpleCommandMeta(this.map, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg & 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 cloud.commandframework.meta;
|
||||
|
||||
import io.leangen.geantyref.GenericTypeReflector;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
final class SimpleKey<V> implements CommandMeta.Key<V> {
|
||||
|
||||
private final @NonNull TypeToken<V> valueType;
|
||||
private final @NonNull String name;
|
||||
private final @Nullable Function<@NonNull CommandMeta, @Nullable V> derivationFunction;
|
||||
|
||||
SimpleKey(
|
||||
final @NonNull TypeToken<V> valueType,
|
||||
final @NonNull String name,
|
||||
final @Nullable Function<@NonNull CommandMeta, @Nullable V> derivationFunction
|
||||
) {
|
||||
this.valueType = valueType;
|
||||
this.name = name;
|
||||
this.derivationFunction = derivationFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull TypeToken<V> getValueType() {
|
||||
return this.valueType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Function<@NonNull CommandMeta, @Nullable V> getFallbackDerivation() {
|
||||
return this.derivationFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final SimpleKey<?> that = (SimpleKey<?>) other;
|
||||
return this.valueType.equals(that.valueType)
|
||||
&& this.name.equals(that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 7 * GenericTypeReflector.hashCode(this.valueType.getAnnotatedType())
|
||||
+ 31 * this.name.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ package cloud.commandframework;
|
|||
|
||||
import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.standard.IntegerArgument;
|
||||
import cloud.commandframework.meta.CommandMeta;
|
||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||
import cloud.commandframework.types.tuples.Pair;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
|
@ -43,14 +44,14 @@ class CommandHelpHandlerTest {
|
|||
@BeforeAll
|
||||
static void setup() {
|
||||
manager = new TestCommandManager();
|
||||
final SimpleCommandMeta meta1 = SimpleCommandMeta.builder().with("description", "Command with only literals").build();
|
||||
final SimpleCommandMeta meta1 = SimpleCommandMeta.builder().with(CommandMeta.DESCRIPTION, "Command with only literals").build();
|
||||
manager.command(manager.commandBuilder("test", meta1).literal("this").literal("thing").build());
|
||||
final SimpleCommandMeta meta2 = SimpleCommandMeta.builder().with("description", "Command with variables").build();
|
||||
final SimpleCommandMeta meta2 = SimpleCommandMeta.builder().with(CommandMeta.DESCRIPTION, "Command with variables").build();
|
||||
manager.command(manager.commandBuilder("test", meta2).literal("int").
|
||||
argument(IntegerArgument.of("int"), Description.of("A number")).build());
|
||||
|
||||
manager.command(manager.commandBuilder("vec")
|
||||
.meta("description", "Takes in a vector")
|
||||
.meta(CommandMeta.DESCRIPTION, "Takes in a vector")
|
||||
.argumentPair("vec", Pair.of("x", "y"),
|
||||
Pair.of(Double.class, Double.class), Description.of("Vector")
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue