<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.sponge.service;

import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.NodeBuilder;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.PermissionHolder;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;

import java.util.*;
import java.util.stream.Collectors;

@SuppressWarnings({"OptionalGetWithoutIsPresent", "unused"})
@AllArgsConstructor
public class LuckPermsSubjectData implements SubjectData {
    private final boolean enduring;
    private final LuckPermsService service;

    @Getter
    private final PermissionHolder holder;

    private void objectSave(PermissionHolder t) {
        if (t instanceof User) {
            service.getPlugin().getStorage().saveUser(((User) t))
                    .thenRunAsync(() -> ((User) t).getRefreshBuffer().request(), service.getPlugin().getAsyncExecutor());
        }
        if (t instanceof Group) {
            service.getPlugin().getStorage().saveGroup((Group) t)
                    .thenRunAsync(() -> service.getPlugin().getUpdateTaskBuffer().request(), service.getPlugin().getAsyncExecutor());
        }
    }

    @Override
    public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
        try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) {
            Map<Set<Context>, Map<String, Boolean>> perms = new HashMap<>();

            for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
                Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts());

                if (n.isServerSpecific()) {
                    contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
                }

                if (n.isWorldSpecific()) {
                    contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
                }

                if (!perms.containsKey(contexts)) {
                    perms.put(contexts, new HashMap<>());
                }

                perms.get(contexts).put(n.getPermission(), n.getValue());
            }

            ImmutableMap.Builder<Set<Context>, Map<String, Boolean>> map = ImmutableMap.builder();
            for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) {
                map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue()));
            }
            return map.build();
        }
    }

    @Override
    public Map<String, Boolean> getPermissions(@NonNull Set<Context> contexts) {
        return getAllPermissions().getOrDefault(contexts, ImmutableMap.of());
    }

    @Override
    public boolean setPermission(@NonNull Set<Context> contexts, @NonNull String permission, @NonNull Tristate tristate) {
        try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_PERMISSION)) {
            if (tristate == Tristate.UNDEFINED) {
                // Unset
                Node.Builder builder = new NodeBuilder(permission);

                for (Context ct : contexts) {
                    builder.withExtraContext(ct.getKey(), ct.getValue());
                }

                try {
                    if (enduring) {
                        holder.unsetPermission(builder.build());
                    } else {
                        holder.unsetTransientPermission(builder.build());
                    }
                } catch (ObjectLacksException ignored) {}

                objectSave(holder);
                return true;
            }

            Node.Builder builder = new NodeBuilder(permission)
                    .setValue(tristate.asBoolean());

            for (Context ct : contexts) {
                builder.withExtraContext(ct.getKey(), ct.getValue());
            }

            try {
                if (enduring) {
                    holder.setPermission(builder.build());
                } else {
                    holder.setTransientPermission(builder.build());
                }
            } catch (ObjectAlreadyHasException ignored) {}

            objectSave(holder);
            return true;
        }
    }

    @Override
    public boolean clearPermissions() {
        try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
            if (enduring) {
                holder.clearNodes();
            } else {
                holder.clearTransientNodes();
            }
            objectSave(holder);
            return true;
        }
    }

    @Override
    public boolean clearPermissions(@NonNull Set<Context> c) {
        try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
            List<Node> toRemove = new ArrayList<>();
            for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
                Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts());

                if (n.isServerSpecific()) {
                    contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
                }<commit_msg>Fix Sponge subject data permission overrides<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.sponge.service;

import co.aikar.timings.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.NodeBuilder;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.PermissionHolder;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.sponge.timings.LPTiming;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;

import java.util.*;
import java.util.stream.Collectors;

@SuppressWarnings({"OptionalGetWithoutIsPresent", "unused"})
@AllArgsConstructor
public class LuckPermsSubjectData implements SubjectData {
    private final boolean enduring;
    private final LuckPermsService service;

    @Getter
    private final PermissionHolder holder;

    private void objectSave(PermissionHolder t) {
        if (t instanceof User) {
            service.getPlugin().getStorage().saveUser(((User) t))
                    .thenRunAsync(() -> ((User) t).getRefreshBuffer().request(), service.getPlugin().getAsyncExecutor());
        }
        if (t instanceof Group) {
            service.getPlugin().getStorage().saveGroup((Group) t)
                    .thenRunAsync(() -> service.getPlugin().getUpdateTaskBuffer().request(), service.getPlugin().getAsyncExecutor());
        }
    }

    @Override
    public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
        try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) {
            Map<Set<Context>, Map<String, Boolean>> perms = new HashMap<>();

            for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
                Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts());

                if (n.isServerSpecific()) {
                    contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
                }

                if (n.isWorldSpecific()) {
                    contexts.add(new Context(Context.WORLD_KEY, n.getWorld().get()));
                }

                if (!perms.containsKey(contexts)) {
                    perms.put(contexts, new HashMap<>());
                }

                perms.get(contexts).put(n.getPermission(), n.getValue());
            }

            ImmutableMap.Builder<Set<Context>, Map<String, Boolean>> map = ImmutableMap.builder();
            for (Map.Entry<Set<Context>, Map<String, Boolean>> e : perms.entrySet()) {
                map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue()));
            }
            return map.build();
        }
    }

    @Override
    public Map<String, Boolean> getPermissions(@NonNull Set<Context> contexts) {
        return getAllPermissions().getOrDefault(contexts, ImmutableMap.of());
    }

    @Override
    public boolean setPermission(@NonNull Set<Context> contexts, @NonNull String permission, @NonNull Tristate tristate) {
        try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_SET_PERMISSION)) {
            if (tristate == Tristate.UNDEFINED) {
                // Unset
                Node.Builder builder = new NodeBuilder(permission);

                for (Context ct : contexts) {
                    builder.withExtraContext(ct.getKey(), ct.getValue());
                }

                try {
                    if (enduring) {
                        holder.unsetPermission(builder.build());
                    } else {
                        holder.unsetTransientPermission(builder.build());
                    }
                } catch (ObjectLacksException ignored) {}

                objectSave(holder);
                return true;
            }

            Node.Builder builder = new NodeBuilder(permission)
                    .setValue(tristate.asBoolean());

            for (Context ct : contexts) {
                builder.withExtraContext(ct.getKey(), ct.getValue());
            }

            Node node = builder.build();

            // Workaround: unset the inverse, to allow false -> true, true -> false overrides.
            try {
                if (enduring) {
                    holder.unsetPermission(node);
                }else {
                    holder.unsetTransientPermission(node);
                }
            } catch (ObjectLacksException ignored) {}

            try {
                if (enduring) {
                    holder.setPermission(node);
                } else {
                    holder.setTransientPermission(node);
                }
            } catch (ObjectAlreadyHasException ignored) {}

            objectSave(holder);
            return true;
        }
    }

    @Override
    public boolean clearPermissions() {
        try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
            if (enduring) {
                holder.clearNodes();
            } else {
                holder.clearTransientNodes();
            }
            objectSave(holder);
            return true;
        }
    }

    @Override
    public boolean clearPermissions(@NonNull Set<Context> c) {
        try (Timing i = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_CLEAR_PERMISSIONS)) {
            List<Node> toRemove = new ArrayList<>();
            for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
                Set<Context> contexts = LuckPermsService.convertContexts(n.getContexts());

                if (n.isServerSpecific()) {
                    contexts.add(new Context(LuckPermsService.SERVER_CONTEXT, n.getServer().get()));
                }
{'fraction_non_alphanumeric': 0.10587828492392808, 'fraction_numerical': 0.0005532503457814661, 'mean_word_length': 2.4603972242163197, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 57, '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': '7593214', 'n_tokens_mistral': 4019, 'n_tokens_neox': 3850, 'n_words': 902}