Begin working on the flag system

This commit is contained in:
Alexander Söderberg 2020-10-01 14:19:26 +02:00
parent 7bd0af0fef
commit 549fbd1d1d
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
5 changed files with 409 additions and 0 deletions

View file

@ -0,0 +1,56 @@
//
// 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.arguments.compound;
import cloud.commandframework.types.tuples.DynamicTuple;
import cloud.commandframework.types.tuples.Tuple;
import io.leangen.geantyref.TypeToken;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.function.Function;
/**
* Container for flag parsing logic. This should not be be used directly.
* Internally, a flag argument is a special case of a {@link CompoundArgument}.
*
* @param <C> Command sender type
*/
public class FlagArgument<C> extends CompoundArgument<DynamicTuple, C, DynamicTuple> {
FlagArgument(final @NonNull Tuple names,
final @NonNull Tuple parserTuple,
final @NonNull Tuple types,
final @NonNull Function<@NonNull DynamicTuple, @NonNull DynamicTuple> mapper,
final @NonNull TypeToken<DynamicTuple> valueType) {
super(false,
"flags",
names,
parserTuple,
types,
mapper,
DynamicTuple::of,
valueType);
}
}

View file

@ -0,0 +1,175 @@
//
// 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.arguments.flags;
import cloud.commandframework.Description;
import cloud.commandframework.arguments.CommandArgument;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Arrays;
import java.util.Collection;
/**
* A flag is an optional command argument that may have an associated parser,
* and is identified by its name. Essentially, it's a mixture of a command literal
* and an optional variable command argument.
*
* @param <T> Command argument type. {@link Void} is used when no argument is present.
*/
@SuppressWarnings("unused")
public final class CommandFlag<T> {
private final @NonNull String name;
private final @NonNull String @NonNull [] aliases;
private final @NonNull Description description;
private final @Nullable CommandArgument<?, T> commandArgument;
private CommandFlag(@NonNull final String name,
@NonNull final String @NonNull [] aliases,
@NonNull final Description description,
@Nullable final CommandArgument<?, T> commandArgument) {
this.name = name;
this.aliases = aliases;
this.description = description;
this.commandArgument = commandArgument;
}
/**
* Create a new flag builder
*
* @param name Flag name
* @return Flag builder
*/
public static @NonNull Builder<Void> newBuilder(@NonNull final String name) {
return new Builder<>(name);
}
/**
* Get the flag name
*
* @return Flag name
*/
public @NonNull String getName() {
return this.name;
}
/**
* Get all flag aliases. This does not include the flag name
*
* @return Flag aliases
*/
public @NonNull Collection<@NonNull String> getAliases() {
return Arrays.asList(this.aliases);
}
/**
* Get the flag description
* <p>
* Flag description
*/
public @NonNull Description getDescription() {
return this.description;
}
/**
* Get the command argument, if it exists
*
* @return Command argument, or {@code null}
*/
public @Nullable CommandArgument<?, T> getCommandArgument() {
return this.commandArgument;
}
@Override
public String toString() {
return String.format("--%s", this.name);
}
public static final class Builder<T> {
private final String name;
private final String[] aliases;
private final Description description;
private final CommandArgument<?, T> commandArgument;
private Builder(@NonNull final String name,
@NonNull final String[] aliases,
@NonNull final Description description,
@Nullable final CommandArgument<?, T> commandArgument) {
this.name = name;
this.aliases = aliases;
this.description = description;
this.commandArgument = commandArgument;
}
private Builder(@NonNull final String name) {
this(name, new String[0], Description.empty(), null);
}
/**
* Create a new builder instance using the given flag aliases
*
* @param aliases Flag aliases
* @return New builder instance
*/
public Builder<T> withAliases(@NonNull final String... aliases) {
return new Builder<>(this.name, aliases, this.description, this.commandArgument);
}
/**
* Create a new builder instance using the given flag description
*
* @param description Flag description
* @return New builder instance
*/
public Builder<T> withDescription(@NonNull final Description description) {
return new Builder<>(this.name, this.aliases, description, this.commandArgument);
}
/**
* Create a new builder instance using the given command argument
*
* @param argument Command argument
* @param <N> New argument type
* @return New builder instance
*/
public <N> Builder<N> withArgument(@NonNull final CommandArgument<?, N> argument) {
return new Builder<>(this.name, this.aliases, this.description, argument);
}
/**
* Build a new command flag instance
*
* @return Constructed instance
*/
public @NonNull CommandFlag<T> build() {
return new CommandFlag<>(this.name, this.aliases, this.description, this.commandArgument);
}
}
}

View file

@ -0,0 +1,106 @@
//
// 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.arguments.flags;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.HashMap;
import java.util.Map;
/**
* Flag value mappings
*/
public class FlagContext {
/**
* Dummy object stored as a flag value when the flag has no associated parser
*/
public static final Object FLAG_PRESENCE_VALUE = new Object();
private final Map<String, Object> flagValues;
private FlagContext() {
this.flagValues = new HashMap<>();
}
/**
* Create a new flag context instance
*
* @return Constructed instance
*/
public static @NonNull FlagContext create() {
return new FlagContext();
}
/**
* Indicate that a presence flag was supplied
*
* @param flag Flag instance
*/
public void addPresenceFlag(@NonNull final CommandFlag<?> flag) {
this.flagValues.put(flag.getName(), FLAG_PRESENCE_VALUE);
}
/**
* Store a value associated with a value flag
*
* @param flag Value flag
* @param value Flag value
* @param <T> Value type
*/
public <T> void addValueFlag(@NonNull final CommandFlag<T> flag, @NonNull final T value) {
this.flagValues.put(flag.getName(), value);
}
/**
* Check whether or not a flag is present. This will return {@code false}
* for all value flags.
*
* @param flag Flag name
* @return {@code true} if the flag is presence and the flag is a presence flag,
* else {@code false}
*/
public boolean isPresent(@NonNull final String flag) {
final Object value = this.flagValues.get(flag);
return FLAG_PRESENCE_VALUE.equals(value);
}
/**
* Get a flag value
*
* @param name Flag name
* @param defaultValue Default value
* @param <T> Value type
* @return Stored value, or the supplied default value
*/
public <T> T getValue(@NonNull final String name, @NonNull final T defaultValue) {
final Object value = this.flagValues.get(name);
if (value == null) {
return defaultValue;
}
@SuppressWarnings("unchecked") final T casted = (T) value;
return casted;
}
}

View file

@ -24,6 +24,7 @@
package cloud.commandframework.context; package cloud.commandframework.context;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.flags.FlagContext;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collections; import java.util.Collections;
@ -39,6 +40,7 @@ import java.util.Optional;
public final class CommandContext<C> { public final class CommandContext<C> {
private final Map<CommandArgument<C, ?>, ArgumentTiming> argumentTimings = new HashMap<>(); private final Map<CommandArgument<C, ?>, ArgumentTiming> argumentTimings = new HashMap<>();
private final FlagContext flagContext = FlagContext.create();
private final Map<String, Object> internalStorage = new HashMap<>(); private final Map<String, Object> internalStorage = new HashMap<>();
private final C commandSender; private final C commandSender;
private final boolean suggestions; private final boolean suggestions;
@ -164,6 +166,15 @@ public final class CommandContext<C> {
return Collections.unmodifiableMap(this.argumentTimings); return Collections.unmodifiableMap(this.argumentTimings);
} }
/**
* Get the associated {@link FlagContext} instance
*
* @return Flag context
*/
public @NonNull FlagContext flags() {
return this.flagContext;
}
/** /**
* Used to track performance metrics related to command parsing. This is attached * Used to track performance metrics related to command parsing. This is attached

View file

@ -0,0 +1,61 @@
//
// 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.types.tuples;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Dynamic sized tuple backed by a {@code Object[]}
*/
public final class DynamicTuple implements Tuple {
private final Object[] internalArray;
private DynamicTuple(@NonNull final Object @NonNull [] internalArray) {
this.internalArray = internalArray;
}
/**
* Create a new dynamic tuple, containing the given elements
*
* @param elements Elements that should be contained in the tuple
* @return Created tuple, preserving the order of the given elements
*/
public static @NonNull DynamicTuple of(@NonNull final Object... elements) {
return new DynamicTuple(elements);
}
@Override
public final int getSize() {
return this.internalArray.length;
}
@Override
public @NonNull Object @NonNull [] toArray() {
final @NonNull Object @NonNull [] newArray = new Object[this.internalArray.length];
System.arraycopy(this.internalArray, 0, newArray, 0, this.internalArray.length);
return newArray;
}
}