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.impl.subject;
018
019import ca.stellardrift.permissionsex.impl.PermissionsEx;
020import ca.stellardrift.permissionsex.datastore.DataStore;
021import ca.stellardrift.permissionsex.subject.CalculatedSubject;
022import ca.stellardrift.permissionsex.subject.SubjectRef;
023import ca.stellardrift.permissionsex.subject.SubjectType;
024import ca.stellardrift.permissionsex.subject.SubjectTypeCollection;
025import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
026import com.github.benmanes.caffeine.cache.Caffeine;
027
028import java.util.Collection;
029import java.util.Collections;
030import java.util.concurrent.CompletableFuture;
031import java.util.stream.Stream;
032
033public class SubjectTypeCollectionImpl<I> implements SubjectTypeCollection<I> {
034    private final PermissionsEx<?> pex;
035    private final SubjectType<I> type;
036    private final SubjectDataCacheImpl<I> persistentData;
037    private final SubjectDataCacheImpl<I> transientData;
038    private final AsyncLoadingCache<I, CalculatedSubject> cache;
039
040    public SubjectTypeCollectionImpl(PermissionsEx<?> pex, final SubjectType<I> type, SubjectDataCacheImpl<I> persistentData, SubjectDataCacheImpl<I> transientData) {
041        this.pex = pex;
042        this.type = type;
043        this.persistentData = persistentData;
044        this.transientData = transientData;
045        this.cache = Caffeine.newBuilder()
046                .executor(pex.asyncExecutor())
047                .buildAsync((key, executor) -> {
048                    CalculatedSubjectImpl<I> subj = new CalculatedSubjectImpl<>(
049                            SubjectDataBaker.inheritance(),
050                            SubjectRef.subject(this.type, key),
051                            this);
052
053                    return persistentData.referenceTo(key, false)
054                            .thenCombine(
055                                    transientData.referenceTo(key, false),
056                                    (persistentRef, transientRef) -> {
057                                        subj.initialize(persistentRef, transientRef);
058                                        return subj;
059                                    });
060                });
061    }
062
063    @Override
064    public SubjectType<I> type() {
065        return this.type;
066    }
067
068    @Override
069    public void cacheAll() {
070        this.persistentData.cacheAll();
071        this.transientData.cacheAll();
072    }
073
074    @Override
075    public Collection<CalculatedSubject> activeSubjects() {
076        return Collections.unmodifiableCollection(this.cache.synchronous().asMap().values());
077    }
078
079    @Override
080    public void uncache(final I identifier) {
081        this.persistentData.invalidate(identifier);
082        this.transientData.invalidate(identifier);
083        this.cache.synchronous().invalidate(identifier);
084    }
085
086    @Override
087    public CompletableFuture<CalculatedSubject> get(final I identifier) {
088        return this.cache.get(identifier);
089    }
090
091    @Override
092    public SubjectDataCacheImpl<I> transientData() {
093        return this.transientData;
094    }
095
096    @Override
097    public SubjectDataCacheImpl<I> persistentData() {
098        return this.persistentData;
099    }
100
101    PermissionsEx<?> engine() {
102        return this.pex;
103    }
104
105    /**
106     * For internal use only. Update the data store associated with this cache, when for example the
107     * active data store is changed in the engine.
108     *
109     * @param newDataStore The new data store to work off of
110     */
111    public void update(final DataStore newDataStore) {
112        this.persistentData.update(newDataStore);
113    }
114
115    @Override
116    public void load(final I identifier) {
117        this.persistentData.load(identifier);
118        this.transientData.load(identifier);
119    }
120
121    @Override
122    public CompletableFuture<Boolean> isRegistered(final I identifier) {
123        return this.persistentData.isRegistered(identifier)
124                .thenCombine(this.transientData.isRegistered(identifier), Boolean::logicalAnd);
125    }
126
127    @Override
128    public Stream<I> allIdentifiers() {
129        return Stream.concat(
130                this.persistentData.getAllIdentifiers(),
131                this.transientData.getAllIdentifiers())
132            .distinct();
133    }
134}