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 org.checkerframework.checker.nullness.qual.NonNull;
024
025import java.util.Collection;
026import java.util.Collections;
027
028/**
029 * A plot flag is any property that can be assigned
030 * to a plot, that will alter its functionality in some way.
031 * These are user assignable in-game, or via configuration files.
032 *
033 * @param <T> Value contained in the flag.
034 */
035public abstract class PlotFlag<T, F extends PlotFlag<T, F>> {
036
037    private final T value;
038    private final Caption flagCategory;
039    private final Caption flagDescription;
040    private final String flagName;
041
042    /**
043     * Construct a new flag instance.
044     *
045     * @param value           Flag value
046     * @param flagCategory    The flag category
047     * @param flagDescription A caption describing the flag functionality
048     */
049    protected PlotFlag(
050            final @NonNull T value, final @NonNull Caption flagCategory,
051            final @NonNull Caption flagDescription
052    ) {
053        this.value = Preconditions.checkNotNull(value, "flag value may not be null");
054        this.flagCategory =
055                Preconditions.checkNotNull(flagCategory, "flag category may not be null");
056        this.flagDescription =
057                Preconditions.checkNotNull(flagDescription, "flag description may not be null");
058        // Parse flag name
059        // noinspection unchecked
060        this.flagName = getFlagName(this.getClass());
061    }
062
063    /**
064     * Return the name of the flag.
065     *
066     * @param flagClass Flag class
067     * @param <T>       Value type
068     * @param <F>       Flag type
069     * @return The name of the flag implemented by the given class
070     */
071    public static <T, F extends PlotFlag<T, F>> String getFlagName(Class<F> flagClass) {
072        final StringBuilder flagName = new StringBuilder();
073        final char[] chars = flagClass.getSimpleName().replace("Flag", "").toCharArray();
074        for (int i = 0; i < chars.length; i++) {
075            if (i == 0) {
076                flagName.append(Character.toLowerCase(chars[i]));
077            } else if (Character.isUpperCase(chars[i])) {
078                flagName.append('-').append(Character.toLowerCase(chars[i]));
079            } else {
080                flagName.append(chars[i]);
081            }
082        }
083        return flagName.toString();
084    }
085
086    /**
087     * Get the flag value
088     *
089     * @return Non-nullable flag value
090     */
091    public @NonNull
092    final T getValue() {
093        return this.value;
094    }
095
096    /**
097     * Parse a string into a flag, and throw an exception in the case that the
098     * string does not represent a valid flag value. This instance won't change its
099     * state, but instead an instance holding the parsed flag value will be returned.
100     *
101     * @param input String to parse.
102     * @return Parsed value, if valid.
103     * @throws FlagParseException If the value could not be parsed.
104     */
105    public abstract F parse(final @NonNull String input) throws FlagParseException;
106
107    /**
108     * Merge this flag's value with another value and return an instance
109     * holding the merged value.
110     *
111     * @param newValue New flag value.
112     * @return Flag containing parsed flag value.
113     */
114    public abstract F merge(final @NonNull T newValue);
115
116    /**
117     * Returns a string representation of the flag instance, that when
118     * passed through {@link #parse(String)} will result in an equivalent
119     * instance of the flag.
120     *
121     * @return String representation of the flag
122     */
123    public abstract String toString();
124
125    /**
126     * Get the flag name.
127     *
128     * @return Flag name
129     */
130    public final String getName() {
131        return this.flagName;
132    }
133
134    /**
135     * Get a simple caption that describes the flag usage.
136     *
137     * @return Flag description.
138     */
139    public Caption getFlagDescription() {
140        return this.flagDescription;
141    }
142
143    /**
144     * Get the category this flag belongs to. Usually a caption from {@link com.plotsquared.core.configuration.caption.TranslatableCaption}
145     * <p>
146     * These categories are used to categorize the flags when outputting
147     * flag lists to players.
148     *
149     * @return Flag category
150     */
151    public Caption getFlagCategory() {
152        return this.flagCategory;
153    }
154
155    /**
156     * Get if the flag's permission should check for values. E.g. plots.flag.set.music.VALUE
157     *
158     * @return if valued permission
159     * @since 6.0.10
160     */
161    public boolean isValuedPermission() {
162        return true;
163    }
164
165    /**
166     * An example of a string that would parse into a valid
167     * flag value.
168     *
169     * @return An example flag value.
170     */
171    public abstract String getExample();
172
173    protected abstract F flagOf(@NonNull T value);
174
175    /**
176     * Create a new instance of the flag using a provided
177     * (non-null) value.
178     *
179     * @param value The flag value
180     * @return The created flag instance
181     */
182    public final F createFlagInstance(final @NonNull T value) {
183        return flagOf(Preconditions.checkNotNull(value));
184    }
185
186    /**
187     * Get the tab completable values associated with the flag type, or
188     * an empty collection if tab completion isn't supported.
189     *
190     * @return Collection containing tab completable flag values
191     */
192    public Collection<String> getTabCompletions() {
193        return Collections.emptyList();
194    }
195
196    @Override
197    public boolean equals(final Object o) {
198        if (this == o) {
199            return true;
200        }
201        if (o == null || getClass() != o.getClass()) {
202            return false;
203        }
204        final PlotFlag<?, ?> plotFlag = (PlotFlag<?, ?>) o;
205        return value.equals(plotFlag.value);
206    }
207
208    @Override
209    public int hashCode() {
210        return value.hashCode();
211    }
212
213    /**
214     * @deprecated This method is not meant to be invoked or overridden, with no replacement.
215     */
216    @Deprecated(forRemoval = true, since = "6.6.0")
217    protected boolean canEqual(final Object other) {
218        return other instanceof PlotFlag;
219    }
220
221
222}