001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.plot.flag;
020
021import com.google.common.base.Preconditions;
022import com.plotsquared.core.configuration.caption.Caption;
023import net.kyori.adventure.text.Component;
024import org.checkerframework.checker.nullness.qual.NonNull;
025
026import java.util.Collection;
027import java.util.Collections;
028
029/**
030 * A plot flag is any property that can be assigned
031 * to a plot, that will alter its functionality in some way.
032 * These are user assignable in-game, or via configuration files.
033 *
034 * @param <T> Value contained in the flag.
035 */
036public abstract class PlotFlag<T, F extends PlotFlag<T, F>> {
037
038    private final T value;
039    private final Caption flagCategory;
040    private final Caption flagDescription;
041    private final String flagName;
042
043    /**
044     * Construct a new flag instance.
045     *
046     * @param value           Flag value
047     * @param flagCategory    The flag category
048     * @param flagDescription A caption describing the flag functionality
049     */
050    protected PlotFlag(
051            final @NonNull T value, final @NonNull Caption flagCategory,
052            final @NonNull Caption flagDescription
053    ) {
054        this.value = Preconditions.checkNotNull(value, "flag value may not be null");
055        this.flagCategory =
056                Preconditions.checkNotNull(flagCategory, "flag category may not be null");
057        this.flagDescription =
058                Preconditions.checkNotNull(flagDescription, "flag description may not be null");
059        // Parse flag name
060        // noinspection unchecked
061        this.flagName = getFlagName(this.getClass());
062    }
063
064    /**
065     * Return the name of the flag.
066     *
067     * @param flagClass Flag class
068     * @param <T>       Value type
069     * @param <F>       Flag type
070     * @return The name of the flag implemented by the given class
071     */
072    public static <T, F extends PlotFlag<T, F>> String getFlagName(Class<F> flagClass) {
073        final StringBuilder flagName = new StringBuilder();
074        final char[] chars = flagClass.getSimpleName().replace("Flag", "").toCharArray();
075        for (int i = 0; i < chars.length; i++) {
076            if (i == 0) {
077                flagName.append(Character.toLowerCase(chars[i]));
078            } else if (Character.isUpperCase(chars[i])) {
079                flagName.append('-').append(Character.toLowerCase(chars[i]));
080            } else {
081                flagName.append(chars[i]);
082            }
083        }
084        return flagName.toString();
085    }
086
087    /**
088     * Gets the flag name as a Kyori {@link Component}
089     *
090     * @see #getFlagName(Class)
091     * @since 7.0.0
092     */
093    public static <T, F extends PlotFlag<T, F>> Component getFlagNameComponent(Class<F> flagClass) {
094        return Component.text(getFlagName(flagClass));
095    }
096
097    /**
098     * Get the flag value
099     *
100     * @return Non-nullable flag value
101     */
102    public @NonNull
103    final T getValue() {
104        return this.value;
105    }
106
107    /**
108     * Parse a string into a flag, and throw an exception in the case that the
109     * string does not represent a valid flag value. This instance won't change its
110     * state, but instead an instance holding the parsed flag value will be returned.
111     *
112     * @param input String to parse.
113     * @return Parsed value, if valid.
114     * @throws FlagParseException If the value could not be parsed.
115     */
116    public abstract F parse(final @NonNull String input) throws FlagParseException;
117
118    /**
119     * Merge this flag's value with another value and return an instance
120     * holding the merged value.
121     *
122     * @param newValue New flag value.
123     * @return Flag containing parsed flag value.
124     */
125    public abstract F merge(final @NonNull T newValue);
126
127    /**
128     * Returns a string representation of the flag instance, that when
129     * passed through {@link #parse(String)} will result in an equivalent
130     * instance of the flag.
131     *
132     * @return String representation of the flag
133     */
134    public abstract String toString();
135
136    /**
137     * Get the flag name.
138     *
139     * @return Flag name
140     */
141    public final String getName() {
142        return this.flagName;
143    }
144
145    /**
146     * Get a simple caption that describes the flag usage.
147     *
148     * @return Flag description.
149     */
150    public Caption getFlagDescription() {
151        return this.flagDescription;
152    }
153
154    /**
155     * Get the category this flag belongs to. Usually a caption from {@link com.plotsquared.core.configuration.caption.TranslatableCaption}
156     * <p>
157     * These categories are used to categorize the flags when outputting
158     * flag lists to players.
159     *
160     * @return Flag category
161     */
162    public Caption getFlagCategory() {
163        return this.flagCategory;
164    }
165
166    /**
167     * Get if the flag's permission should check for values. E.g. plots.flag.set.music.VALUE
168     *
169     * @return if valued permission
170     * @since 6.0.10
171     */
172    public boolean isValuedPermission() {
173        return true;
174    }
175
176    /**
177     * An example of a string that would parse into a valid
178     * flag value.
179     *
180     * @return An example flag value.
181     */
182    public abstract String getExample();
183
184    protected abstract F flagOf(@NonNull T value);
185
186    /**
187     * Create a new instance of the flag using a provided
188     * (non-null) value.
189     *
190     * @param value The flag value
191     * @return The created flag instance
192     */
193    public final F createFlagInstance(final @NonNull T value) {
194        return flagOf(Preconditions.checkNotNull(value));
195    }
196
197    /**
198     * Get the tab completable values associated with the flag type, or
199     * an empty collection if tab completion isn't supported.
200     *
201     * @return Collection containing tab completable flag values
202     */
203    public Collection<String> getTabCompletions() {
204        return Collections.emptyList();
205    }
206
207    @Override
208    public boolean equals(final Object o) {
209        if (this == o) {
210            return true;
211        }
212        if (o == null || getClass() != o.getClass()) {
213            return false;
214        }
215        final PlotFlag<?, ?> plotFlag = (PlotFlag<?, ?>) o;
216        return value.equals(plotFlag.value);
217    }
218
219    @Override
220    public int hashCode() {
221        return value.hashCode();
222    }
223
224}