001/*
002 * PermissionsEx
003 * Copyright (C) zml and PermissionsEx contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ca.stellardrift.permissionsex.context;
018
019import ca.stellardrift.permissionsex.subject.CalculatedSubject;
020import org.checkerframework.checker.nullness.qual.Nullable;
021import org.pcollections.HashTreePSet;
022
023import java.util.Set;
024import java.util.function.Consumer;
025
026import static java.util.Objects.requireNonNull;
027
028/**
029 * A specific type of context, for example {@code world}, {@code server-tag}, or {@code until}.
030 *
031 * @since 2.0.0
032 */
033public abstract class ContextDefinition<V> {
034    private final String name;
035
036    protected ContextDefinition(final String name) {
037        this.name = requireNonNull(name, "name");
038    }
039
040    public final ContextValue<V> createValue(final V value) {
041        return new ContextValue<>(this, value);
042    }
043
044    /**
045     * Gets the name for this context definition.
046     *
047     * @return the definition name
048     * @since 2.0.0
049     */
050    public final String name() {
051        return this.name;
052    }
053
054    /**
055     * Given a parsed value, write data out as a string.
056     *
057     * @param canonicalValue Parsed value
058     * @return serialized form of the value
059     */
060    public abstract String serialize(V canonicalValue);
061
062    /**
063     * Given a string (which may be in user format), return a parsed object.
064     *
065     * @param userValue the value as a string, such as when provided by user input
066     * @return V a deserialized value, or {@code null if unsuccessful}
067     */
068    public abstract @Nullable V deserialize(String userValue);
069
070    /**
071     * Given a defined context and the active value (provided by {@link #accumulateCurrentValues(CalculatedSubject, Consumer)}),
072     * return whether the active value matches the defined value.
073     */
074    public final boolean matches(final ContextValue<V> ctx, final V activeValue) {
075        return matches(ctx.getParsedValue(this), activeValue);
076    }
077
078    public abstract boolean matches(V ownVal, V testVal);
079
080    /**
081     * Given a player, calculate active context types
082     *
083     * @param subject  The subject active contexts are being calculated for
084     * @param consumer A function that will take the returned value and add it to the active context set
085     */
086    public abstract void accumulateCurrentValues(CalculatedSubject subject, Consumer<V> consumer);
087
088    /**
089     * Given a subject, suggest a set of values that may be valid for this context. This need not be an exhaustive list,
090     * or could even be an empty list, but allows providing users possible suggestions to what sensible values for a context may be.
091     */
092    public Set<V> suggestValues(final CalculatedSubject subject) {
093        return HashTreePSet.empty();
094    }
095
096    @Override
097    public String toString() {
098        return "ContextDefinition{name='" + this.name + "'}";
099    }
100
101    @Override
102    public boolean equals(final @Nullable Object other) {
103        if (this == other) return true;
104        if (!(other instanceof ContextDefinition<?>)) return false;
105
106        return this.name.equals(((ContextDefinition<?>) other).name);
107    }
108
109    @Override
110    public int hashCode() {
111        return 31 * this.name.hashCode();
112    }
113}
114
115