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.subject;
018
019import ca.stellardrift.permissionsex.context.ContextDefinitionProvider;
020import ca.stellardrift.permissionsex.context.ContextValue;
021import org.checkerframework.checker.nullness.qual.Nullable;
022
023import java.util.Map;
024import java.util.Set;
025import java.util.function.BiFunction;
026import java.util.function.Function;
027import java.util.function.UnaryOperator;
028
029/**
030 * The core subject data interface.
031 *
032 * <p>This class is an immutable holder for all of a single subject's data. No inherited data is
033 * included, and no context calculations are performed when querying objects of this class.</p>
034 *
035 * <p>The global context is represented by an empty set
036 * ({@link ContextDefinitionProvider#GLOBAL_CONTEXT}), not a null value.</p>
037 *
038 * @since 2.0.0
039 */
040public interface ImmutableSubjectData {
041
042    /**
043     * Get all non-empty segments associated with this subject.
044     *
045     * @return all segments
046     * @since 2.0.0
047     */
048    Map<? extends Set<ContextValue<?>>, Segment> segments();
049
050    /**
051     * Apply a transformation to every segment contained in this subject.
052     *
053     * @param transformer action to apply
054     * @return the modified subject data
055     * @since 2.0.0
056     */
057    ImmutableSubjectData withSegments(final BiFunction<Set<ContextValue<?>>, Segment, Segment> transformer);
058
059    /**
060     * Apply a transformation to the segment at the provided contexts.
061     *
062     * <p>If no segment is present at {@code contexts}, an empty segment will be used as the
063     * input value.</p>
064     *
065     * @param contexts the contexts to modify at
066     * @param operation the operation to perform
067     * @return an updated subject data
068     * @since 2.0.0
069     */
070    ImmutableSubjectData withSegment(final Set<ContextValue<?>> contexts, final UnaryOperator<Segment> operation);
071
072    /**
073     * Return a map derived from all segments in this data, transformed by {@code mapper}.
074     *
075     * @param mapper the mapper
076     * @param <V> the output value type
077     * @return the map
078     * @since 2.0.0
079     */
080    <V> Map<Set<ContextValue<?>>, V> mapSegmentValues(final Function<Segment, V> mapper);
081
082    /**
083     * Get a value derived from a single segment.
084     *
085     * @param contexts the contexts to query the segment from
086     * @param mapper the transformation
087     * @param <V> the output value type
088     * @return the transformed value, or null if no segment was present at the context
089     * @since 2.0.0
090     */
091    <V> @Nullable V mapSegment(final Set<ContextValue<?>> contexts, final Function<Segment, V> mapper);
092
093    /**
094     * Get a segment at the specified coordinates.
095     *
096     * @param contexts the context coordinates for the segment
097     * @return the segment at the coordinates, or an empty segment if none is present
098     * @since 2.0.0
099     */
100    Segment segment(final Set<ContextValue<?>> contexts);
101
102    /**
103     * Make an updated subject data with the segment applied at the specified context set.
104     *
105     * @param contexts contexts to set at
106     * @param segment the segment to set
107     * @return an updated subject data
108     * @since 2.0.0
109     */
110    ImmutableSubjectData withSegment(final Set<ContextValue<?>> contexts, final Segment segment);
111
112    /**
113     * Gets the contexts with data set in this subject.
114     *
115     * @return An immutable set of all sets of contexts with data stored
116     * @since 2.0.0
117     */
118    Set<? extends Set<ContextValue<?>>> activeContexts();
119
120    /**
121     * Create a new subject data instance, applying all data from {@code other}.
122     *
123     * <p>This will <em>add</em> to existing data, rather than overwriting.</p>
124     *
125     * @param other source to add from
126     * @return a modified subject data
127     * @since 2.0.0
128     */
129    default ImmutableSubjectData mergeFrom(final ImmutableSubjectData other) {
130        ImmutableSubjectData output = this;
131        for (final Map.Entry<? extends Set<ContextValue<?>>, Segment> entry : other.segments().entrySet()) {
132            output = output.withSegment(entry.getKey(), output.segment(entry.getKey()).mergeFrom(entry.getValue()));
133        }
134        return output;
135    }
136
137}