Work towards making CommandMeta typesafe (#173)

Co-authored-by: Alexander Söderberg <sauilitired@gmail.com>
This commit is contained in:
zml 2020-12-16 03:07:22 -08:00 committed by Alexander Söderberg
parent ab366be24d
commit 1e91273e0e
33 changed files with 450 additions and 64 deletions

View file

@ -8,10 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added `@Suggestions` annotated methods
- Type safe meta system
### Changed
- Moved the parser injector registry into CommandManager and added injection to CommandContext
### Deprecated
- String keyed command meta
## [1.2.0] - 2020-12-07
### Added

View file

@ -307,7 +307,7 @@ public final class AnnotationParser<C> {
final SimpleCommandMeta.Builder metaBuilder = SimpleCommandMeta.builder()
.with(this.metaFactory.apply(method));
if (methodOrClassHasAnnotation(method, Confirmation.class)) {
metaBuilder.with(CommandConfirmationManager.CONFIRMATION_REQUIRED_META, "true");
metaBuilder.with(CommandConfirmationManager.META_CONFIRMATION_REQUIRED, true);
}
@SuppressWarnings("rawtypes")

View file

@ -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);
}
/**

View file

@ -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);
}
/**

View file

@ -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);

View file

@ -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;
}

View file

@ -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 */

View file

@ -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));

View file

@ -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) {

View file

@ -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);

View file

@ -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++) {

View file

@ -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) {

View file

@ -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" */

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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")
)

View file

@ -119,8 +119,8 @@ public class PircBotXCommandManager<C> extends CommandManager<C> {
@Override
public final boolean hasPermission(
@NonNull final C sender,
@NonNull final String permission
final @NonNull C sender,
final @NonNull String permission
) {
return this.permissionFunction.apply(sender, permission);
}
@ -140,7 +140,7 @@ public class PircBotXCommandManager<C> extends CommandManager<C> {
return this.commandPrefix;
}
@NonNull final Function<User, C> getUserMapper() {
final @NonNull Function<User, C> getUserMapper() {
return this.userMapper;
}

View file

@ -128,8 +128,8 @@ public final class UserArgument<C> extends CommandArgument<C, User> {
@Override
public @NonNull ArgumentParseResult<@NonNull User> 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 input = inputQueue.peek();
if (input == null) {

View file

@ -31,6 +31,7 @@ import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException;
import cloud.commandframework.exceptions.NoSuchCommandException;
import cloud.commandframework.meta.CommandMeta;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginIdentifiableCommand;
@ -63,7 +64,7 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
) {
super(
label,
cloudCommand.getCommandMeta().getOrDefault("description", ""),
cloudCommand.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, ""),
"",
aliases
);
@ -163,7 +164,7 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
@Override
public String getDescription() {
return this.cloudCommand.getCommandMeta().getOrDefault("description", "");
return this.cloudCommand.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, "");
}
@Override

View file

@ -215,6 +215,7 @@ public class BukkitCommandManager<C> extends CommandManager<C> implements Brigad
* @return Meta data
*/
@Override
@SuppressWarnings("deprecation")
public @NonNull BukkitCommandMeta createDefaultCommandMeta() {
return BukkitCommandMetaBuilder.builder().withDescription("").build();
}

View file

@ -26,6 +26,12 @@ package cloud.commandframework.bukkit;
import cloud.commandframework.meta.SimpleCommandMeta;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Bukkit-specific command metadata holder.
*
* @deprecated for removal since 1.3.0. Use the standard {@link SimpleCommandMeta instead}.
*/
@Deprecated
public class BukkitCommandMeta extends SimpleCommandMeta {
/**
@ -34,7 +40,7 @@ public class BukkitCommandMeta extends SimpleCommandMeta {
* @param simpleCommandMeta Simple command meta data instance that gets mirrored
*/
public BukkitCommandMeta(final @NonNull SimpleCommandMeta simpleCommandMeta) {
super(simpleCommandMeta.getAll());
super(simpleCommandMeta);
}
}

View file

@ -24,8 +24,15 @@
package cloud.commandframework.bukkit;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Command meta builder with bukkit-specific parameters.
*
* @deprecated for removal since 1.3.0, use plain {@link SimpleCommandMeta.Builder} instead.
*/
@Deprecated
public final class BukkitCommandMetaBuilder {
private BukkitCommandMetaBuilder() {
@ -73,7 +80,7 @@ public final class BukkitCommandMetaBuilder {
* @return Meta instance
*/
public @NonNull BukkitCommandMeta build() {
return new BukkitCommandMeta(CommandMeta.simple().with("description", this.description).build());
return new BukkitCommandMeta(CommandMeta.simple().with(CommandMeta.DESCRIPTION, this.description).build());
}
}

View file

@ -139,8 +139,8 @@ public final class PlayerArgument<C> extends CommandArgument<C, ProxiedPlayer> {
@Override
public @NonNull ArgumentParseResult<@NonNull ProxiedPlayer> 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 input = inputQueue.peek();
if (input == null) {

View file

@ -137,8 +137,8 @@ public final class ServerArgument<C> extends CommandArgument<C, ServerInfo> {
@Override
public @NonNull ArgumentParseResult<@NonNull ServerInfo> 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 input = inputQueue.peek();
if (input == null) {

View file

@ -31,6 +31,7 @@ import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException;
import cloud.commandframework.exceptions.NoSuchCommandException;
import cloud.commandframework.meta.CommandMeta;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.server.command.CommandSender;
import org.cloudburstmc.server.command.PluginCommand;
@ -61,7 +62,7 @@ final class CloudburstCommand<C> extends PluginCommand<Plugin> {
super(manager.getOwningPlugin(), CommandData.builder(label)
.addAliases(aliases.toArray(new String[0]))
.addPermission(cloudCommand.getCommandPermission().toString())
.setDescription(cloudCommand.getCommandMeta().getOrDefault("description", ""))
.setDescription(cloudCommand.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, ""))
.build());
this.command = command;
this.manager = manager;

View file

@ -137,8 +137,8 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
@Override
public @NonNull ArgumentParseResult<@NonNull Player> 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 input = inputQueue.peek();
if (input == null) {

View file

@ -133,8 +133,8 @@ public final class ServerArgument<C> extends CommandArgument<C, RegisteredServer
@Override
public @NonNull ArgumentParseResult<@NonNull RegisteredServer> 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 input = inputQueue.peek();
if (input == null) {

View file

@ -44,7 +44,6 @@ import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.arguments.standard.IntegerArgument;
import cloud.commandframework.arguments.standard.StringArrayArgument;
import cloud.commandframework.bukkit.BukkitCommandManager;
import cloud.commandframework.bukkit.BukkitCommandMetaBuilder;
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
import cloud.commandframework.bukkit.arguments.selector.SingleEntitySelector;
import cloud.commandframework.bukkit.parsers.EnchantmentArgument;
@ -178,9 +177,9 @@ public final class ExamplePlugin extends JavaPlugin {
// @CommandMethod
//
final Function<ParserParameters, CommandMeta> commandMetaFunction = p ->
BukkitCommandMetaBuilder.builder()
CommandMeta.simple()
// This will allow you to decorate commands with descriptions
.withDescription(p.get(StandardParameters.DESCRIPTION, "No description"))
.with(CommandMeta.DESCRIPTION, p.get(StandardParameters.DESCRIPTION, "No description"))
.build();
this.annotationParser = new AnnotationParser<>(
/* Manager */ this.manager,

View file

@ -50,7 +50,7 @@ public final class ExampleBot {
*
* @param args Arguments to start the bot with (NOT used)
*/
public static void main(@NonNull final String[] args) {
public static void main(final @NonNull String[] args) {
SimplixInstaller
.instance()
.register(ExampleBot.class);

View file

@ -56,9 +56,9 @@ public class ExampleApplication {
*/
@Inject
public ExampleApplication(
@NonNull final DiscordApiComponent discordApiComponent,
@NonNull final CommandsComponent commandsComponent,
@NonNull final ScheduledExecutorService executorService
final @NonNull DiscordApiComponent discordApiComponent,
final @NonNull CommandsComponent commandsComponent,
final @NonNull ScheduledExecutorService executorService
) throws
Exception {
this.discordApiComponent = discordApiComponent;

View file

@ -53,7 +53,7 @@ public class CommandsComponent {
* @param discordApiComponent Instance of the {@link DiscordApiComponent} for registering the command listener
*/
@Inject
public CommandsComponent(@NonNull final DiscordApiComponent discordApiComponent) {
public CommandsComponent(final @NonNull DiscordApiComponent discordApiComponent) {
this.discordApiComponent = discordApiComponent;
}

View file

@ -55,7 +55,7 @@ public class DiscordApiComponent {
*
* @param token The Discord Bot token
*/
public final void login(@NonNull final String token) {
public final void login(final @NonNull String token) {
LOG.info("Logging in to Discord...");
this.api = this.builder.setToken(token).login().join();