/** * PermissionsEx * Copyright (C) zml and PermissionsEx contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ninja.leaping.permissionsex.subject; import com.github.benmanes.caffeine.cache.AsyncLoadingCache; import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import ninja.leaping.permissionsex.PermissionsEx; import ninja.leaping.permissionsex.data.Caching; import ninja.leaping.permissionsex.data.ImmutableSubjectData; import ninja.leaping.permissionsex.data.SubjectDataReference; import ninja.leaping.permissionsex.util.NodeTree; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import static java.util.Map.Entry; /** * This is a holder that maintains the current subject data state */ public class CalculatedSubject implements Caching<ImmutableSubjectData> { private final SubjectDataBaker baker; private final Map.Entry<String, String> identifier; private final SubjectType type; private SubjectDataReference ref, transientRef; private final AsyncLoadingCache<Set<Entry<String, String>>, BakedSubjectData> data; CalculatedSubject(SubjectDataBaker baker, Map.Entry<String, String> identifier, SubjectType type) { this.baker = Preconditions.checkNotNull(baker, "baker"); this.identifier = Preconditions.checkNotNull(identifier, "identifier"); this.type = Preconditions.checkNotNull(type, "type"); this.data = Caffeine.newBuilder() .maximumSize(32) .expireAfterAccess(30, TimeUnit.MINUTES) .buildAsync(((key, executor) -> this.baker.bake(CalculatedSubject.this, key))); } void initialize(SubjectDataReference persistentRef, SubjectDataReference transientRef) { this.ref = persistentRef; this.transientRef = transientRef; } public Map.Entry<String, String> getIdentifier() { return identifier; } PermissionsEx getManager() { return this.type.getManager(); } private BakedSubjectData getData(Set<Entry<String, String>> contexts) { Preconditions.checkNotNull(contexts, "contexts"); return data.synchronous().get(ImmutableSet.copyOf(contexts)); } public NodeTree getPermissions(Set<Map.Entry<String, String>> contexts) { return getData(contexts).getPermissions(); } public Map<String, String> getOptions(Set<Map.Entry<String, String>> contexts) { return getData(contexts).getOptions(); } public List<Map.Entry<String, String>> getParents(Set<Map.Entry<String, String>> contexts) { List<Map.Entry<String, String>> parents = getData(contexts).getParents(); getManager().getNotifier().onParentCheck(getIdentifier(), contexts, parents); return parents; } private Set<Set<Map.Entry<String, String>>> getActiveContexts() { return data.synchronous().asMap().keySet(); } public int getPermission(Set<Entry<String, String>> contexts, String permission) { int ret = getPermissions(contexts).get(Preconditions.checkNotNull(permission, "permission")); getManager().getNotifier().onPermissionCheck(getIdentifier(), contexts, permission, ret); return ret; } public Optional<String> getOption(Set<Entry<String, String>> contexts, String option) { String val = getOptions(contexts).get(Preconditions.checkNotNull(option, "option")); getManager().getNotifier().onOptionCheck(getIdentifier(), contexts, option, val); return Optional.ofNullable(val); } public SubjectDataReference data() { return this.ref; } public SubjectDataReference transientData() { return this.transientRef; } @Override public void clearCache(ImmutableSubjectData newData) { data.synchronous().invalidateAll(); getManager().getActiveSubjectTypes().stream() .flatMap(type -> type.getActiveSubjects().stream()) .filter(subj -> { for (Set<Map.Entry<String, String>> ent : subj.getActiveContexts()) { if (subj.getParents(ent).contains(this.identifier)) { return true; } } return false; }) .forEach(subj -> subj.data.synchronous().invalidateAll()); } public SubjectType getType() { return this.type; } }