<commit_before>/*
 * Copyright (c) 2016 Lucko (Luck) <<EMAIL>>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

package me.lucko.luckperms.common.core.model;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;

import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.event.events.GroupAddEvent;
import me.lucko.luckperms.api.event.events.GroupRemoveEvent;
import me.lucko.luckperms.api.event.events.PermissionNodeExpireEvent;
import me.lucko.luckperms.api.event.events.PermissionNodeSetEvent;
import me.lucko.luckperms.api.event.events.PermissionNodeUnsetEvent;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.internal.GroupLink;
import me.lucko.luckperms.common.api.internal.PermissionHolderLink;
import me.lucko.luckperms.common.caching.MetaHolder;
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
import me.lucko.luckperms.common.caching.handlers.GroupReference;
import me.lucko.luckperms.common.caching.handlers.HolderReference;
import me.lucko.luckperms.common.caching.holder.ExportNodesHolder;
import me.lucko.luckperms.common.caching.holder.GetAllNodesHolder;
import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.core.InheritanceInfo;
import me.lucko.luckperms.common.core.NodeBuilder;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.PriorityComparator;
import me.lucko.luckperms.common.utils.Cache;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.common.utils.ImmutableLocalizedNode;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * Represents an object that can hold permissions
 * For example a User or a Group
 */
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class PermissionHolder {

    /**
     * The UUID of the user / name of the group.
     * Used to prevent circular inheritance issues
     */
    @Getter
    private final String objectName;

    /**
     * Reference to the main plugin instance
     */
    @Getter(AccessLevel.PROTECTED)
    private final LuckPermsPlugin plugin;

    /**
     * The holders persistent nodes
     */
    private final Set<Node> nodes = new HashSet<>();

    /**
     * The holders transient nodes
     */
    private final Set<Node> transientNodes = new HashSet<>();

    /**
     * Lock used by Storage implementations to prevent concurrent read/writes
     */
    @Getter
    private final Lock ioLock = new ReentrantLock();


    /*
     * CACHES - cache the result of a number of methods in this class, until they are invalidated.
     */

    /* Internal Caches - only depend on the state of this instance. */

    private Cache<ImmutableSet<Node>> enduringCache = new Cache<>(() -> {
        synchronized (nodes) {
            return ImmutableSet.copyOf(nodes);
        }
    });
    private Cache<ImmutableSet<Node>> transientCache = new Cache<>(() -> {
        synchronized (transientNodes) {
            return ImmutableSet.copyOf(transientNodes);
        }
    });
    private Cache<ImmutableSortedSet<LocalizedNode>> cache = new Cache<>(this::cacheApply);
    private Cache<ImmutableSortedSet<LocalizedNode>> mergedCache = new Cache<>(this::mergedCacheApply);

    /* External Caches - may depend on the state of other instances. */

    private LoadingCache<GetAllNodesHolder, SortedSet<LocalizedNode>> getAllNodesCache = CacheBuilder.newBuilder()
            .build(new CacheLoader<GetAllNodesHolder, SortedSet<LocalizedNode>>() {
                @Override
                public SortedSet<LocalizedNode> load(GetAllNodesHolder getAllNodesHolder) {
                    return getAllNodesCacheApply(getAllNodesHolder);
                }
            });
    private LoadingCache<ExtractedContexts, Set<LocalizedNode>> getAllNodesFilteredCache = CacheBuilder.newBuilder()
            .build(new CacheLoader<ExtractedContexts, Set<LocalizedNode>>() {
                @Override
                public Set<LocalizedNode> load(ExtractedContexts extractedContexts) throws Exception {
                    return getAllNodesFilteredApply(extractedContexts);
                }
            });
    private LoadingCache<ExportNodesHolder, Map<String, Boolean>> exportNodesCache = CacheBuilder.newBuilder()
            .build(new CacheLoader<ExportNodesHolder, Map<String, Boolean>>() {
                @Override
                public Map<String, Boolean> load(ExportNodesHolder exportNodesHolder) throws Exception {
                    return exportNodesApply(exportNodesHolder);
                }
            });



    /* Caching apply methods. Are just called by the caching instances to gather data about the instance. */

    private void invalidateCache(boolean enduring) {
        if (enduring) {
            enduringCache.invalidate();
        } else {
            transientCache.invalidate();
        }
        cache.invalidate();
        mergedCache.invalidate();

        // Invalidate inheritance caches
        getAllNodesCache.invalidateAll();
        getAllNodesFilteredCache.invalidateAll();
        exportNodesCache.invalidateAll();

        // Get previous references
        Set<HolderReference> refs = plugin.getCachedStateManager().getInheritances(toReference());

        // Declare new state to the state manager
        declareState();

        // Add all new references affected by the state change.
        refs.addAll(plugin.getCachedStateManager().getInheritances(toReference()));

        // Invalidate all affected children.
        CachedStateManager.invalidateInheritances(plugin, refs);
    }

    private ImmutableSortedSet<LocalizedNode> cacheApply() {<commit_msg>declare child states<commit_after>/*
 * Copyright (c) 2016 Lucko (Luck) <<EMAIL>>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

package me.lucko.luckperms.common.core.model;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;

import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.event.events.GroupAddEvent;
import me.lucko.luckperms.api.event.events.GroupRemoveEvent;
import me.lucko.luckperms.api.event.events.PermissionNodeExpireEvent;
import me.lucko.luckperms.api.event.events.PermissionNodeSetEvent;
import me.lucko.luckperms.api.event.events.PermissionNodeUnsetEvent;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.internal.GroupLink;
import me.lucko.luckperms.common.api.internal.PermissionHolderLink;
import me.lucko.luckperms.common.caching.MetaHolder;
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
import me.lucko.luckperms.common.caching.handlers.GroupReference;
import me.lucko.luckperms.common.caching.handlers.HolderReference;
import me.lucko.luckperms.common.caching.holder.ExportNodesHolder;
import me.lucko.luckperms.common.caching.holder.GetAllNodesHolder;
import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.core.InheritanceInfo;
import me.lucko.luckperms.common.core.NodeBuilder;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.PriorityComparator;
import me.lucko.luckperms.common.utils.Cache;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.common.utils.ImmutableLocalizedNode;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * Represents an object that can hold permissions
 * For example a User or a Group
 */
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class PermissionHolder {

    /**
     * The UUID of the user / name of the group.
     * Used to prevent circular inheritance issues
     */
    @Getter
    private final String objectName;

    /**
     * Reference to the main plugin instance
     */
    @Getter(AccessLevel.PROTECTED)
    private final LuckPermsPlugin plugin;

    /**
     * The holders persistent nodes
     */
    private final Set<Node> nodes = new HashSet<>();

    /**
     * The holders transient nodes
     */
    private final Set<Node> transientNodes = new HashSet<>();

    /**
     * Lock used by Storage implementations to prevent concurrent read/writes
     */
    @Getter
    private final Lock ioLock = new ReentrantLock();


    /*
     * CACHES - cache the result of a number of methods in this class, until they are invalidated.
     */

    /* Internal Caches - only depend on the state of this instance. */

    private Cache<ImmutableSet<Node>> enduringCache = new Cache<>(() -> {
        synchronized (nodes) {
            return ImmutableSet.copyOf(nodes);
        }
    });
    private Cache<ImmutableSet<Node>> transientCache = new Cache<>(() -> {
        synchronized (transientNodes) {
            return ImmutableSet.copyOf(transientNodes);
        }
    });
    private Cache<ImmutableSortedSet<LocalizedNode>> cache = new Cache<>(this::cacheApply);
    private Cache<ImmutableSortedSet<LocalizedNode>> mergedCache = new Cache<>(this::mergedCacheApply);

    /* External Caches - may depend on the state of other instances. */

    private LoadingCache<GetAllNodesHolder, SortedSet<LocalizedNode>> getAllNodesCache = CacheBuilder.newBuilder()
            .build(new CacheLoader<GetAllNodesHolder, SortedSet<LocalizedNode>>() {
                @Override
                public SortedSet<LocalizedNode> load(GetAllNodesHolder getAllNodesHolder) {
                    return getAllNodesCacheApply(getAllNodesHolder);
                }
            });
    private LoadingCache<ExtractedContexts, Set<LocalizedNode>> getAllNodesFilteredCache = CacheBuilder.newBuilder()
            .build(new CacheLoader<ExtractedContexts, Set<LocalizedNode>>() {
                @Override
                public Set<LocalizedNode> load(ExtractedContexts extractedContexts) throws Exception {
                    return getAllNodesFilteredApply(extractedContexts);
                }
            });
    private LoadingCache<ExportNodesHolder, Map<String, Boolean>> exportNodesCache = CacheBuilder.newBuilder()
            .build(new CacheLoader<ExportNodesHolder, Map<String, Boolean>>() {
                @Override
                public Map<String, Boolean> load(ExportNodesHolder exportNodesHolder) throws Exception {
                    return exportNodesApply(exportNodesHolder);
                }
            });



    /* Caching apply methods. Are just called by the caching instances to gather data about the instance. */

    private void invalidateCache(boolean enduring) {
        if (enduring) {
            enduringCache.invalidate();
        } else {
            transientCache.invalidate();
        }
        cache.invalidate();
        mergedCache.invalidate();

        // Invalidate inheritance caches
        invalidateInheritanceCaches();

        // Get previous references
        Set<HolderReference> refs = plugin.getCachedStateManager().getInheritances(toReference());

        // Declare new state to the state manager
        declareState();

        // Add all new references affected by the state change.
        refs.addAll(plugin.getCachedStateManager().getInheritances(toReference()));

        // Invalidate all affected children.
        CachedStateManager.invalidateInheritances(plugin, refs);
    }

    public void invalidateInheritanceCaches() {
        getAllNodesCache.invalidateAll();
        getAllNodesFilteredCache.invalidateAll();
        exportNodesCache.invalidateAll();
        declareState();
    }

    private ImmutableSortedSet<LocalizedNode> cacheApply() {
{'fraction_non_alphanumeric': 0.09025155219868143, 'fraction_numerical': 0.0005120655443896819, 'mean_word_length': 3.6102095013278253, 'pattern_counts': {'":': 0, '<': 73, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5631710', 'n_tokens_mistral': 4522, 'n_tokens_neox': 4413, 'n_words': 1099}