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.Objects;
024import java.util.Set;
025import java.util.function.Consumer;
026
027import static java.util.Objects.requireNonNull;
028
029/**
030 * A specific type of context, for example {@code world}, {@code server-tag}, or {@code until}.
031 *
032 * @since 2.0.0
033 */
034public abstract class ContextDefinition<V> {
035    private final String name;
036
037    protected ContextDefinition(final String name) {
038        this.name = requireNonNull(name, "name");
039    }
040
041    /**
042     * Create a new context value based on this definition.
043     *
044     * @param value the value to associate with this definition
045     * @return a new context value holder
046     * @since 2.0.0
047     */
048    public final ContextValue<V> createValue(final V value) {
049        return new ContextValue<>(this, value);
050    }
051
052    /**
053     * Gets the name for this context definition.
054     *
055     * @return the definition name
056     * @since 2.0.0
057     */
058    public final String name() {
059        return this.name;
060    }
061
062    /**
063     * Given a parsed value, write data out as a string.
064     *
065     * @param canonicalValue Parsed value
066     * @return serialized form of the value
067     * @since 2.0.0
068     */
069    public abstract String serialize(V canonicalValue);
070
071    /**
072     * Given a string (which may be in user format), return a parsed object.
073     *
074     * @param userValue the value as a string, such as when provided by user input
075     * @return V a deserialized value, or {@code null if unsuccessful}
076     * @since 2.0.0
077     */
078    public abstract @Nullable V deserialize(String userValue);
079
080    /**
081     * Given a defined context and the active value (provided by {@link #accumulateCurrentValues(CalculatedSubject, Consumer)}),
082     * return whether the active value matches the defined value.
083     *
084     * @param ctx a defined context
085     * @param activeValue a value to test for membership in {@code ctx}
086     * @return whether a match was found
087     * @since 2.0.0
088     */
089    public final boolean matches(final ContextValue<V> ctx, final V activeValue) {
090        return matches(ctx.getParsedValue(this), activeValue);
091    }
092
093    /**
094     * Get whether two values match.
095     *
096     * @param ownVal the defined value
097     * @param testVal the value being tested against
098     * @return whether {@code testVal} is an element of {@code ownVal}
099     */
100    public boolean matches(V ownVal, V testVal) {
101        return Objects.equals(ownVal, testVal);
102    }
103
104    /**
105     * Given a player, calculate active context types
106     *
107     * @param subject  The subject active contexts are being calculated for
108     * @param consumer A function that will take the returned value and add it to the active context set
109     * @since 2.0.0
110     */
111    public abstract void accumulateCurrentValues(CalculatedSubject subject, Consumer<V> consumer);
112
113    /**
114     * Given a subject, suggest a set of values that may be valid for this context. This need not be an exhaustive list,
115     * or could even be an empty list, but allows providing users possible suggestions to what sensible values for a context may be.
116     *
117     * @param subject a subject to query for environment information
118     * @return a set of possible values
119     * @since 2.0.0
120     */
121    public Set<V> suggestValues(final CalculatedSubject subject) {
122        return HashTreePSet.empty();
123    }
124
125    @Override
126    public String toString() {
127        return "ContextDefinition{name='" + this.name + "'}";
128    }
129
130    @Override
131    public boolean equals(final @Nullable Object other) {
132        if (this == other) return true;
133        if (!(other instanceof ContextDefinition<?>)) return false;
134
135        return this.name.equals(((ContextDefinition<?>) other).name);
136    }
137
138    @Override
139    public int hashCode() {
140        return 31 * this.name.hashCode();
141    }
142}
143
144