ninja.leaping.permissionsex.sponge.PEXSubjectData.java Source code

Java tutorial

Introduction

Here is the source code for ninja.leaping.permissionsex.sponge.PEXSubjectData.java

Source

/**
 * 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.sponge;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import ninja.leaping.permissionsex.data.Change;
import ninja.leaping.permissionsex.data.ImmutableSubjectData;
import ninja.leaping.permissionsex.data.SubjectDataReference;
import ninja.leaping.permissionsex.util.GuavaCollectors;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.util.Tristate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;

/**
 * Wrapper around ImmutableSubjectData that writes to backend each change
 */
class PEXSubjectData implements SubjectData {
    private final PermissionsExPlugin plugin;
    private SubjectDataReference data;
    private final ConcurrentMap<Set<Map.Entry<String, String>>, List<Subject>> parentsCache = new ConcurrentHashMap<>();

    public PEXSubjectData(SubjectDataReference data, PermissionsExPlugin plugin) throws ExecutionException {
        this.plugin = plugin;
        this.data = data;
        this.data.onUpdate(this::clearCache);
    }

    /**
     * This is valid because all Contexts are Map.Entries
     *
     * @param input The input set
     * @return A properly casted set
     */
    @SuppressWarnings("unchecked")
    static Set<Map.Entry<String, String>> parSet(Set<Context> input) {
        return (Set) input;
    }

    private static <T> Map<Set<Context>, T> tKeys(Map<Set<Map.Entry<String, String>>, T> input) {
        final ImmutableMap.Builder<Set<Context>, T> ret = ImmutableMap.builder();
        for (Map.Entry<Set<Map.Entry<String, String>>, T> ent : input.entrySet()) {
            ret.put(ent.getKey().stream()
                    .map(ctx -> ctx instanceof Context ? (Context) ctx : new Context(ctx.getKey(), ctx.getValue()))
                    .collect(GuavaCollectors.toImmutableSet()), ent.getValue());
        }
        return ret.build();
    }

    private boolean wasSuccess(CompletableFuture<Change<ImmutableSubjectData>> future) {
        if (future.isDone()) {
            try {
                future.get();
                return true;
            } catch (InterruptedException | ExecutionException e) {
                return false;
            }
        } else {
            return true;
        }
    }

    private void clearCache(ImmutableSubjectData newData) {
        synchronized (parentsCache) {
            parentsCache.clear();
        }
    }

    @Override
    public Map<Set<Context>, Map<String, String>> getAllOptions() {
        return tKeys(this.data.get().getAllOptions());
    }

    @Override
    public Map<String, String> getOptions(Set<Context> contexts) {
        return this.data.get().getOptions(parSet(contexts));
    }

    @Override
    public boolean setOption(final Set<Context> contexts, final String key, final String value) {
        return wasSuccess(data.update(input -> input.setOption(parSet(contexts), key, value)));
    }

    @Override
    public boolean clearOptions(final Set<Context> contexts) {
        return wasSuccess(data.update(input -> input.clearOptions(parSet(contexts))));
    }

    @Override
    public boolean clearOptions() {
        return wasSuccess(data.update(ImmutableSubjectData::clearOptions));
    }

    @Override
    public Map<Set<Context>, Map<String, Boolean>> getAllPermissions() {
        return Maps.transformValues(tKeys(data.get().getAllPermissions()),
                map -> Maps.transformValues(map, i -> i > 0));
    }

    @Override
    public Map<String, Boolean> getPermissions(Set<Context> set) {
        return Maps.transformValues(data.get().getPermissions(parSet(set)), value -> value > 0);
    }

    @Override
    public boolean setPermission(final Set<Context> set, final String s, Tristate tristate) {
        final int val;
        switch (tristate) {
        case TRUE:
            val = 1;
            break;
        case FALSE:
            val = -1;
            break;
        case UNDEFINED:
            val = 0;
            break;
        default:
            throw new IllegalStateException("Unknown tristate provided " + tristate);
        }

        return wasSuccess(data.update(input -> input.setPermission(parSet(set), s, val)));
    }

    @Override
    public boolean clearPermissions() {
        return wasSuccess(data.update(ImmutableSubjectData::clearPermissions));
    }

    @Override
    public boolean clearPermissions(final Set<Context> set) {
        return wasSuccess(data.update(input -> input.clearPermissions(parSet(set))));
    }

    @Override
    public Map<Set<Context>, List<Subject>> getAllParents() {
        synchronized (parentsCache) {
            data.get().getActiveContexts().forEach(this::getParentsInternal);
            return tKeys(parentsCache);
        }
    }

    @Override
    public List<Subject> getParents(Set<Context> set) {
        return getParentsInternal(parSet(set));
    }

    public List<Subject> getParentsInternal(Set<Map.Entry<String, String>> set) {
        List<Subject> parents = parentsCache.get(set);
        if (parents == null) {
            synchronized (parentsCache) {
                List<Map.Entry<String, String>> rawParents = data.get().getParents(set);
                if (rawParents == null) {
                    parents = ImmutableList.of();
                } else {
                    parents = new ArrayList<>(rawParents.size());
                    for (Map.Entry<String, String> ent : rawParents) {
                        parents.add(plugin.getSubjects(ent.getKey()).get(ent.getValue())); // TODO: Parallelize
                    }
                }
                List<Subject> existingParents = parentsCache.putIfAbsent(set, parents);
                if (existingParents != null) {
                    parents = existingParents;
                }
            }
        }
        return parents;
    }

    @Override
    public boolean addParent(final Set<Context> set, final Subject subject) {
        return wasSuccess(data.update(input -> input.addParent(parSet(set),
                subject.getContainingCollection().getIdentifier(), subject.getIdentifier())));
    }

    @Override
    public boolean removeParent(final Set<Context> set, final Subject subject) {
        return wasSuccess(data.update(input -> input.removeParent(parSet(set),
                subject.getContainingCollection().getIdentifier(), subject.getIdentifier())));
    }

    @Override
    public boolean clearParents() {
        return wasSuccess(data.update(ImmutableSubjectData::clearParents));
    }

    @Override
    public boolean clearParents(final Set<Context> set) {
        return wasSuccess(data.update(input -> input.clearParents(parSet(set))));
    }
}