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.arguments.flags;
025
026import org.checkerframework.checker.nullness.qual.NonNull;
027import org.checkerframework.checker.nullness.qual.Nullable;
028
029import java.util.HashMap;
030import java.util.Map;
031import java.util.Optional;
032
033/**
034 * Flag value mappings
035 */
036public final class FlagContext {
037
038    /**
039     * Dummy object stored as a flag value when the flag has no associated parser
040     */
041    public static final Object FLAG_PRESENCE_VALUE = new Object();
042
043    private final Map<String, Object> flagValues;
044
045    private FlagContext() {
046        this.flagValues = new HashMap<>();
047    }
048
049    /**
050     * Create a new flag context instance
051     *
052     * @return Constructed instance
053     */
054    public static @NonNull FlagContext create() {
055        return new FlagContext();
056    }
057
058    /**
059     * Indicate that a presence flag was supplied
060     *
061     * @param flag Flag instance
062     */
063    public void addPresenceFlag(final @NonNull CommandFlag<?> flag) {
064        this.flagValues.put(flag.getName(), FLAG_PRESENCE_VALUE);
065    }
066
067    /**
068     * Store a value associated with a value flag
069     *
070     * @param flag  Value flag
071     * @param value Flag value
072     * @param <T>   Value type
073     */
074    public <T> void addValueFlag(
075            final @NonNull CommandFlag<T> flag,
076            final @NonNull T value
077    ) {
078        this.flagValues.put(flag.getName(), value);
079    }
080
081    /**
082     * Check whether a presence flag is present. This will return {@code false}
083     * for all value flags.
084     *
085     * @param flag Flag name
086     * @return {@code true} if the flag is a presence flag and is present,
087     *         else {@code false}
088     */
089    public boolean isPresent(final @NonNull String flag) {
090        final Object value = this.flagValues.get(flag);
091        return FLAG_PRESENCE_VALUE.equals(value);
092    }
093
094    /**
095     * Check whether a presence flag is present. This will return {@code false}
096     * for all value flags.
097     *
098     * @param flag A presence flag instance
099     * @return {@code true} if the flag is a presence flag and is present,
100     *         else {@code false}
101     * @since 1.4.0
102     */
103    public boolean isPresent(final @NonNull CommandFlag<Void> flag) {
104        return this.isPresent(flag.getName());
105    }
106
107    /**
108     * Get a flag value as an optional. Will be empty if the value is not present.
109     *
110     * @param name Flag name
111     * @param <T>  Value type
112     * @return Optional containing stored value if present
113     * @since 1.2.0
114     */
115    public <T> @NonNull Optional<T> getValue(
116            final @NonNull String name
117    ) {
118        final Object value = this.flagValues.get(name);
119        if (value == null) {
120            return Optional.empty();
121        }
122        @SuppressWarnings("unchecked") final T casted = (T) value;
123        return Optional.of(casted);
124    }
125
126    /**
127     * Get a flag value as an optional. Will be empty if the value is not present.
128     *
129     * @param flag Flag type
130     * @param <T>  Value type
131     * @return Optional containing stored value if present
132     * @since 1.4.0
133     */
134    public <T> @NonNull Optional<T> getValue(
135            final @NonNull CommandFlag<T> flag
136    ) {
137        return this.getValue(flag.getName());
138    }
139
140    /**
141     * Get a flag value
142     *
143     * @param name         Flag name
144     * @param defaultValue Default value
145     * @param <T>          Value type
146     * @return Stored value, or the supplied default value
147     */
148    public <T> @Nullable T getValue(
149            final @NonNull String name,
150            final @Nullable T defaultValue
151    ) {
152        return this.<T>getValue(name).orElse(defaultValue);
153    }
154
155    /**
156     * Get a flag value
157     *
158     * @param name         Flag value
159     * @param defaultValue Default value
160     * @param <T>          Value type
161     * @return Stored value, or the supplied default value
162     * @since 1.4.0
163     */
164    public <T> @Nullable T getValue(
165            final @NonNull CommandFlag<T> name,
166            final @Nullable T defaultValue
167    ) {
168        return this.getValue(name).orElse(defaultValue);
169    }
170
171    /**
172     * Check whether a flag is present. This will return {@code true} if the flag
173     * is a presence flag and is present, or if the flag is a value flag and has
174     * a value provided.
175     *
176     * @param name Flag name
177     * @return whether the flag is present
178     * @since 1.2.0
179     */
180    public boolean hasFlag(
181            final @NonNull String name
182    ) {
183        return this.getValue(name).isPresent();
184    }
185
186    /**
187     * Check whether a flag is present. This will return {@code true} if the flag
188     * is a presence flag and is present, or if the flag is a value flag and has
189     * a value provided.
190     *
191     * @param flag The flag instance
192     * @return whether the flag is present
193     * @since 1.4.0
194     */
195    public boolean hasFlag(
196            final @NonNull CommandFlag<?> flag
197    ) {
198        return this.getValue(flag).isPresent();
199    }
200
201    /**
202     * Check whether a flag is present. This will return {@code true} if the flag
203     * is a presence flag and is present, or if the flag is a value flag and has
204     * a value provided.
205     *
206     * @param name Flag name
207     * @return whether the flag is present
208     * @since 1.3.0
209     */
210    public boolean contains(final @NonNull String name) {
211        return this.hasFlag(name);
212    }
213
214    /**
215     * Check whether a flag is present. This will return {@code true} if the flag
216     * is a presence flag and is present, or if the flag is a value flag and has
217     * a value provided.
218     *
219     * @param flag Flag instance
220     * @return whether the flag is present
221     * @since 1.4.0
222     */
223    public boolean contains(final @NonNull CommandFlag<?> flag) {
224        return this.hasFlag(flag);
225    }
226
227    /**
228     * Get a flag value
229     *
230     * @param name         Flag name
231     * @param <T>          Value type
232     * @return Stored value if present, else {@code null}
233     * @since 1.3.0
234     */
235    @SuppressWarnings("TypeParameterUnusedInFormals")
236    public <T> @Nullable T get(
237            final @NonNull String name
238    ) {
239        return this.<T>getValue(name).orElse(null);
240    }
241
242    /**
243     * Get a flag value
244     *
245     * @param flag         Flag name
246     * @param <T>          Value type
247     * @return Stored value if present, else {@code null}
248     * @since 1.4.0
249     */
250    public <T> @Nullable T get(
251            final @NonNull CommandFlag<T> flag
252    ) {
253        return this.getValue(flag).orElse(null);
254    }
255
256}