001//
002// MIT License
003//
004// Copyright (c) 2021 Alexander Söderberg & Contributors
005//
006// Permission is hereby granted, free of charge, to any person obtaining a copy
007// of this software and associated documentation files (the "Software"), to deal
008// in the Software without restriction, including without limitation the rights
009// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010// copies of the Software, and to permit persons to whom the Software is
011// furnished to do so, subject to the following conditions:
012//
013// The above copyright notice and this permission notice shall be included in all
014// copies or substantial portions of the Software.
015//
016// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022// SOFTWARE.
023//
024package cloud.commandframework.bukkit.parsers;
025
026import cloud.commandframework.ArgumentDescription;
027import cloud.commandframework.arguments.CommandArgument;
028import cloud.commandframework.arguments.parser.ArgumentParseResult;
029import cloud.commandframework.arguments.parser.ArgumentParser;
030import cloud.commandframework.bukkit.BukkitCaptionKeys;
031import cloud.commandframework.captions.CaptionVariable;
032import cloud.commandframework.context.CommandContext;
033import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
034import cloud.commandframework.exceptions.parsing.ParserException;
035import org.bukkit.NamespacedKey;
036import org.bukkit.enchantments.Enchantment;
037import org.checkerframework.checker.nullness.qual.NonNull;
038import org.checkerframework.checker.nullness.qual.Nullable;
039
040import java.util.ArrayList;
041import java.util.List;
042import java.util.Queue;
043import java.util.function.BiFunction;
044
045/**
046 * cloud argument type that parses Bukkit {@link Enchantment enchantments}
047 *
048 * @param <C> Command sender type
049 */
050public class EnchantmentArgument<C> extends CommandArgument<C, Enchantment> {
051
052    protected EnchantmentArgument(
053            final boolean required,
054            final @NonNull String name,
055            final @NonNull String defaultValue,
056            final @Nullable BiFunction<@NonNull CommandContext<C>, @NonNull String,
057                    @NonNull List<@NonNull String>> suggestionsProvider,
058            final @NonNull ArgumentDescription defaultDescription
059            ) {
060        super(required, name, new EnchantmentParser<>(), defaultValue, Enchantment.class, suggestionsProvider, defaultDescription);
061    }
062
063    /**
064     * Create a new builder
065     *
066     * @param name Name of the argument
067     * @param <C>  Command sender type
068     * @return Created builder
069     */
070    public static <C> EnchantmentArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
071        return new EnchantmentArgument.Builder<>(name);
072    }
073
074    /**
075     * Create a new required argument
076     *
077     * @param name Argument name
078     * @param <C>  Command sender type
079     * @return Created argument
080     */
081    public static <C> @NonNull CommandArgument<C, Enchantment> of(final @NonNull String name) {
082        return EnchantmentArgument.<C>newBuilder(name).asRequired().build();
083    }
084
085    /**
086     * Create a new optional argument
087     *
088     * @param name Argument name
089     * @param <C>  Command sender type
090     * @return Created argument
091     */
092    public static <C> @NonNull CommandArgument<C, Enchantment> optional(final @NonNull String name) {
093        return EnchantmentArgument.<C>newBuilder(name).asOptional().build();
094    }
095
096    /**
097     * Create a new optional argument with a default value
098     *
099     * @param name        Argument name
100     * @param enchantment Default value
101     * @param <C>         Command sender type
102     * @return Created argument
103     */
104    public static <C> @NonNull CommandArgument<C, Enchantment> optional(
105            final @NonNull String name,
106            final @NonNull Enchantment enchantment
107    ) {
108        return EnchantmentArgument.<C>newBuilder(name).asOptionalWithDefault(enchantment.getKey().toString()).build();
109    }
110
111    public static final class Builder<C> extends CommandArgument.Builder<C, Enchantment> {
112
113        private Builder(final @NonNull String name) {
114            super(Enchantment.class, name);
115        }
116
117        @Override
118        public @NonNull CommandArgument<C, Enchantment> build() {
119            return new EnchantmentArgument<>(
120                    this.isRequired(),
121                    this.getName(),
122                    this.getDefaultValue(),
123                    this.getSuggestionsProvider(),
124                    this.getDefaultDescription()
125            );
126        }
127
128    }
129
130    public static final class EnchantmentParser<C> implements ArgumentParser<C, Enchantment> {
131
132        @Override
133        @SuppressWarnings("deprecation")
134        public @NonNull ArgumentParseResult<Enchantment> parse(
135                final @NonNull CommandContext<C> commandContext,
136                final @NonNull Queue<@NonNull String> inputQueue
137        ) {
138            final String input = inputQueue.peek();
139            if (input == null) {
140                return ArgumentParseResult.failure(new NoInputProvidedException(
141                        EnchantmentParser.class,
142                        commandContext
143                ));
144            }
145
146            final NamespacedKey key;
147            if (input.contains(":")) {
148                final String[] splitInput = input.split(":");
149                key = new NamespacedKey(splitInput[0], splitInput[1]);
150            } else {
151                key = NamespacedKey.minecraft(input);
152            }
153
154            final Enchantment enchantment = Enchantment.getByKey(key);
155            if (enchantment == null) {
156                return ArgumentParseResult.failure(new EnchantmentParseException(input, commandContext));
157            }
158            inputQueue.remove();
159            return ArgumentParseResult.success(enchantment);
160        }
161
162        @Override
163        public @NonNull List<@NonNull String> suggestions(
164                final @NonNull CommandContext<C> commandContext,
165                final @NonNull String input
166        ) {
167            final List<String> completions = new ArrayList<>();
168            for (Enchantment value : Enchantment.values()) {
169                if (value.getKey().getNamespace().equals(NamespacedKey.MINECRAFT)) {
170                    completions.add(value.getKey().getKey());
171                } else {
172                    completions.add(value.getKey().toString());
173                }
174            }
175            return completions;
176        }
177
178    }
179
180
181    public static final class EnchantmentParseException extends ParserException {
182
183        private static final long serialVersionUID = 1415174766296065151L;
184        private final String input;
185
186        /**
187         * Construct a new EnchantmentParseException
188         *
189         * @param input   Input
190         * @param context Command context
191         */
192        public EnchantmentParseException(
193                final @NonNull String input,
194                final @NonNull CommandContext<?> context
195        ) {
196            super(
197                    EnchantmentParser.class,
198                    context,
199                    BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_ENCHANTMENT,
200                    CaptionVariable.of("input", input)
201            );
202            this.input = input;
203        }
204
205        /**
206         * Get the input
207         *
208         * @return Input
209         */
210        public @NonNull String getInput() {
211            return this.input;
212        }
213
214    }
215
216}