Only show enduring nodes in command output, encapsulate the Node multimaps in PermissionHolder into separate class
This commit is contained in:
parent
821dc4ef56
commit
4c3e28ba85
@ -175,7 +175,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// convert the constructed node to a transient node instance to refer back to this attachment
|
// convert the constructed node to a transient node instance to refer back to this attachment
|
||||||
ImmutableTransientNode transientNode = ImmutableTransientNode.of(node, this);
|
ImmutableTransientNode<LPPermissionAttachment> transientNode = ImmutableTransientNode.of(node, this);
|
||||||
|
|
||||||
// set the transient node
|
// set the transient node
|
||||||
User user = this.permissible.getUser();
|
User user = this.permissible.getUser();
|
||||||
|
@ -41,6 +41,7 @@ import me.lucko.luckperms.api.caching.CachedData;
|
|||||||
import me.lucko.luckperms.api.context.ContextSet;
|
import me.lucko.luckperms.api.context.ContextSet;
|
||||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||||
import me.lucko.luckperms.common.model.Group;
|
import me.lucko.luckperms.common.model.Group;
|
||||||
|
import me.lucko.luckperms.common.model.NodeMapType;
|
||||||
import me.lucko.luckperms.common.model.User;
|
import me.lucko.luckperms.common.model.User;
|
||||||
import me.lucko.luckperms.common.node.MetaType;
|
import me.lucko.luckperms.common.node.MetaType;
|
||||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||||
@ -161,14 +162,14 @@ public class ApiPermissionHolder implements PermissionHolder {
|
|||||||
@Override
|
@Override
|
||||||
public Tristate hasPermission(@Nonnull Node node) {
|
public Tristate hasPermission(@Nonnull Node node) {
|
||||||
Objects.requireNonNull(node, "node");
|
Objects.requireNonNull(node, "node");
|
||||||
return this.handle.hasPermission(node, false);
|
return this.handle.hasPermission(node, NodeMapType.ENDURING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public Tristate hasTransientPermission(@Nonnull Node node) {
|
public Tristate hasTransientPermission(@Nonnull Node node) {
|
||||||
Objects.requireNonNull(node, "node");
|
Objects.requireNonNull(node, "node");
|
||||||
return this.handle.hasPermission(node, true);
|
return this.handle.hasPermission(node, NodeMapType.TRANSIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@ -76,7 +76,7 @@ public class ParentInfo extends SharedSubCommand {
|
|||||||
SortMode sortMode = SortMode.determine(args);
|
SortMode sortMode = SortMode.determine(args);
|
||||||
|
|
||||||
// get the holders nodes
|
// get the holders nodes
|
||||||
List<LocalizedNode> nodes = new ArrayList<>(holder.getOwnNodesSorted());
|
List<LocalizedNode> nodes = new ArrayList<>(holder.getEnduringData().asSortedSet());
|
||||||
|
|
||||||
// remove irrelevant types (these are displayed in the other info commands)
|
// remove irrelevant types (these are displayed in the other info commands)
|
||||||
nodes.removeIf(node -> !node.isGroupNode() || !node.getValuePrimitive());
|
nodes.removeIf(node -> !node.isGroupNode() || !node.getValuePrimitive());
|
||||||
|
@ -76,7 +76,7 @@ public class PermissionInfo extends SharedSubCommand {
|
|||||||
SortMode sortMode = SortMode.determine(args);
|
SortMode sortMode = SortMode.determine(args);
|
||||||
|
|
||||||
// get the holders nodes
|
// get the holders nodes
|
||||||
List<LocalizedNode> nodes = new ArrayList<>(holder.getOwnNodesSorted());
|
List<LocalizedNode> nodes = new ArrayList<>(holder.getEnduringData().asSortedSet());
|
||||||
|
|
||||||
// remove irrelevant types (these are displayed in the other info commands)
|
// remove irrelevant types (these are displayed in the other info commands)
|
||||||
nodes.removeIf(node ->
|
nodes.removeIf(node ->
|
||||||
|
@ -60,19 +60,19 @@ public class GroupInfo extends SubCommand<Group> {
|
|||||||
group.getName(),
|
group.getName(),
|
||||||
group.getDisplayName().orElse(group.getName()),
|
group.getDisplayName().orElse(group.getName()),
|
||||||
group.getWeight().isPresent() ? group.getWeight().getAsInt() : "None",
|
group.getWeight().isPresent() ? group.getWeight().getAsInt() : "None",
|
||||||
group.getOwnNodes().size(),
|
group.getEnduringData().asList().size(),
|
||||||
group.getOwnNodes().stream().filter(n -> !(n.isGroupNode() || n.isPrefix() || n.isSuffix() || n.isMeta())).mapToInt(n -> 1).sum(),
|
group.getEnduringData().asList().stream().filter(n -> !(n.isGroupNode() || n.isPrefix() || n.isSuffix() || n.isMeta())).mapToInt(n -> 1).sum(),
|
||||||
group.getOwnNodes().stream().filter(Node::isPrefix).mapToInt(n -> 1).sum(),
|
group.getEnduringData().asList().stream().filter(Node::isPrefix).mapToInt(n -> 1).sum(),
|
||||||
group.getOwnNodes().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(),
|
group.getEnduringData().asList().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(),
|
||||||
group.getOwnNodes().stream().filter(Node::isMeta).mapToInt(n -> 1).sum()
|
group.getEnduringData().asList().stream().filter(Node::isMeta).mapToInt(n -> 1).sum()
|
||||||
);
|
);
|
||||||
|
|
||||||
Set<Node> parents = group.getOwnNodesSet().stream()
|
Set<Node> parents = group.getEnduringData().asSet().stream()
|
||||||
.filter(Node::isGroupNode)
|
.filter(Node::isGroupNode)
|
||||||
.filter(Node::isPermanent)
|
.filter(Node::isPermanent)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
Set<Node> tempParents = group.getOwnNodesSet().stream()
|
Set<Node> tempParents = group.getEnduringData().asSet().stream()
|
||||||
.filter(Node::isGroupNode)
|
.filter(Node::isGroupNode)
|
||||||
.filter(Node::isTemporary)
|
.filter(Node::isTemporary)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
@ -66,19 +66,19 @@ public class UserInfo extends SubCommand<User> {
|
|||||||
user.getUuid(),
|
user.getUuid(),
|
||||||
status.asString(plugin.getLocaleManager()),
|
status.asString(plugin.getLocaleManager()),
|
||||||
user.getPrimaryGroup().getValue(),
|
user.getPrimaryGroup().getValue(),
|
||||||
user.getOwnNodes().size(),
|
user.getEnduringData().asList().size(),
|
||||||
user.getOwnNodes().stream().filter(n -> !(n.isGroupNode() || n.isPrefix() || n.isSuffix() || n.isMeta())).mapToInt(n -> 1).sum(),
|
user.getEnduringData().asList().stream().filter(n -> !(n.isGroupNode() || n.isPrefix() || n.isSuffix() || n.isMeta())).mapToInt(n -> 1).sum(),
|
||||||
user.getOwnNodes().stream().filter(Node::isPrefix).mapToInt(n -> 1).sum(),
|
user.getEnduringData().asList().stream().filter(Node::isPrefix).mapToInt(n -> 1).sum(),
|
||||||
user.getOwnNodes().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(),
|
user.getEnduringData().asList().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(),
|
||||||
user.getOwnNodes().stream().filter(Node::isMeta).mapToInt(n -> 1).sum()
|
user.getEnduringData().asList().stream().filter(Node::isMeta).mapToInt(n -> 1).sum()
|
||||||
);
|
);
|
||||||
|
|
||||||
Set<Node> parents = user.getOwnNodesSet().stream()
|
Set<Node> parents = user.getEnduringData().asSet().stream()
|
||||||
.filter(Node::isGroupNode)
|
.filter(Node::isGroupNode)
|
||||||
.filter(Node::isPermanent)
|
.filter(Node::isPermanent)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
Set<Node> tempParents = user.getOwnNodesSet().stream()
|
Set<Node> tempParents = user.getEnduringData().asSet().stream()
|
||||||
.filter(Node::isGroupNode)
|
.filter(Node::isGroupNode)
|
||||||
.filter(Node::isTemporary)
|
.filter(Node::isTemporary)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* 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.model;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.MultimapBuilder;
|
||||||
|
import com.google.common.collect.SortedSetMultimap;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.api.LocalizedNode;
|
||||||
|
import me.lucko.luckperms.api.Node;
|
||||||
|
import me.lucko.luckperms.api.context.ContextSet;
|
||||||
|
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||||
|
import me.lucko.luckperms.common.buffers.Cache;
|
||||||
|
import me.lucko.luckperms.common.contexts.ContextSetComparator;
|
||||||
|
import me.lucko.luckperms.common.node.ImmutableLocalizedNode;
|
||||||
|
import me.lucko.luckperms.common.node.NodeComparator;
|
||||||
|
import me.lucko.luckperms.common.node.NodeWithContextComparator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of nodes held by a {@link PermissionHolder}.
|
||||||
|
*
|
||||||
|
* Each holder has two of these maps, one for enduring and transient nodes.
|
||||||
|
*/
|
||||||
|
public final class NodeMap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The holder which this map is for
|
||||||
|
*/
|
||||||
|
private final PermissionHolder holder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backing data map.
|
||||||
|
*
|
||||||
|
* <p>Nodes are mapped by the result of {@link Node#getFullContexts()}, and keys are sorted by the weight of the
|
||||||
|
* ContextSet. ContextSets are ordered first by the presence of a server key, then by the presence of a world
|
||||||
|
* key, and finally by the overall size of the set. Nodes are ordered according to the priority rules
|
||||||
|
* defined in {@link NodeComparator}.</p>
|
||||||
|
*/
|
||||||
|
private final SortedSetMultimap<ImmutableContextSet, Node> map = MultimapBuilder
|
||||||
|
.treeKeys(ContextSetComparator.reverse())
|
||||||
|
.treeSetValues(NodeComparator.reverse())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lock which synchronizes the instance
|
||||||
|
*/
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache which holds an immutable copy of the backing map
|
||||||
|
*/
|
||||||
|
private final NodeMapCache cache = new NodeMapCache(this);
|
||||||
|
|
||||||
|
NodeMap(PermissionHolder holder) {
|
||||||
|
this.holder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Node> asList() {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
return new ArrayList<>(this.map.values());
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedHashSet<Node> asSet() {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
return new LinkedHashSet<>(this.map.values());
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SortedSet<LocalizedNode> asSortedSet() {
|
||||||
|
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||||
|
copyToLocalized(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyTo(Collection<? super Node> collection) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
collection.addAll(this.map.values());
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyTo(Collection<? super Node> collection, ContextSet filter) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.map.asMap().entrySet()) {
|
||||||
|
if (e.getKey().isSatisfiedBy(filter)) {
|
||||||
|
collection.addAll(e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyToLocalized(Collection<LocalizedNode> collection) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
for (Node node : this.map.values()) {
|
||||||
|
collection.add(ImmutableLocalizedNode.of(node, this.holder.getObjectName()));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable representation of the maps current state.
|
||||||
|
*
|
||||||
|
* @return an immutable copy
|
||||||
|
*/
|
||||||
|
public ImmutableSetMultimap<ImmutableContextSet, Node> immutable() {
|
||||||
|
return this.cache.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates the cache
|
||||||
|
*/
|
||||||
|
void invalidate() {
|
||||||
|
this.cache.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(Node node) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.map.put(node.getFullContexts().makeImmutable(), node);
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void replace(Node node, Node previous) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.map.remove(previous.getFullContexts().makeImmutable(), previous);
|
||||||
|
this.map.put(node.getFullContexts().makeImmutable(), node);
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(Node node) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.map.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node));
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.map.clear();
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear(ContextSet contextSet) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.map.removeAll(contextSet.makeImmutable());
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setContent(Set<Node> set) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.map.clear();
|
||||||
|
for (Node n : set) {
|
||||||
|
this.map.put(n.getFullContexts().makeImmutable(), n);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setContent(Multimap<ImmutableContextSet, Node> multimap) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.map.clear();
|
||||||
|
this.map.putAll(multimap);
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean removeIf(Predicate<? super Node> predicate) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
return this.map.values().removeIf(predicate);
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
SortedSet<Node> nodes = this.map.get(contextSet.makeImmutable());
|
||||||
|
return nodes != null && nodes.removeIf(predicate);
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean auditTemporaryNodes(Set<Node> removed) {
|
||||||
|
boolean work = false;
|
||||||
|
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
Iterator<Node> it = this.map.values().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Node entry = it.next();
|
||||||
|
if (entry.hasExpired()) {
|
||||||
|
removed.add(entry);
|
||||||
|
work = true;
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return work;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class NodeMapCache extends Cache<ImmutableSetMultimap<ImmutableContextSet, Node>> {
|
||||||
|
private final NodeMap handle;
|
||||||
|
|
||||||
|
private NodeMapCache(NodeMap handle) {
|
||||||
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ImmutableSetMultimap<ImmutableContextSet, Node> supply() {
|
||||||
|
this.handle.lock.lock();
|
||||||
|
try {
|
||||||
|
return ImmutableSetMultimap.copyOf(this.handle.map);
|
||||||
|
} finally {
|
||||||
|
this.handle.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* 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.model;
|
||||||
|
|
||||||
|
public enum NodeMapType {
|
||||||
|
|
||||||
|
ENDURING, TRANSIENT
|
||||||
|
|
||||||
|
}
|
@ -31,8 +31,6 @@ import com.google.common.collect.ImmutableSet;
|
|||||||
import com.google.common.collect.ImmutableSetMultimap;
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.MultimapBuilder;
|
|
||||||
import com.google.common.collect.SortedSetMultimap;
|
|
||||||
|
|
||||||
import me.lucko.luckperms.api.Contexts;
|
import me.lucko.luckperms.api.Contexts;
|
||||||
import me.lucko.luckperms.api.DataMutateResult;
|
import me.lucko.luckperms.api.DataMutateResult;
|
||||||
@ -63,11 +61,9 @@ import me.lucko.luckperms.common.references.HolderReference;
|
|||||||
import me.lucko.luckperms.common.references.HolderType;
|
import me.lucko.luckperms.common.references.HolderType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -119,40 +115,8 @@ public abstract class PermissionHolder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The holders persistent nodes.
|
* The holders persistent nodes.
|
||||||
*
|
|
||||||
* <p>Nodes are mapped by the result of {@link Node#getFullContexts()}, and keys are sorted by the weight of the
|
|
||||||
* ContextSet. ContextSets are ordered first by the presence of a server key, then by the presence of a world
|
|
||||||
* key, and finally by the overall size of the set. Nodes are ordered according to the priority rules
|
|
||||||
* defined in {@link NodeComparator}.</p>
|
|
||||||
*/
|
*/
|
||||||
private final SortedSetMultimap<ImmutableContextSet, Node> nodes = MultimapBuilder
|
private final NodeMap enduringNodes = new NodeMap(this);
|
||||||
.treeKeys(ContextSetComparator.reverse())
|
|
||||||
.treeSetValues(NodeComparator.reverse())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
protected PermissionHolder(String objectName, LuckPermsPlugin plugin) {
|
|
||||||
this.objectName = objectName;
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches an immutable copy of the above nodes multimap
|
|
||||||
*/
|
|
||||||
private final class NodesCache extends Cache<ImmutableSetMultimap<ImmutableContextSet, Node>> {
|
|
||||||
@Override
|
|
||||||
protected ImmutableSetMultimap<ImmutableContextSet, Node> supply() {
|
|
||||||
PermissionHolder.this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
return ImmutableSetMultimap.copyOf(PermissionHolder.this.nodes);
|
|
||||||
} finally {
|
|
||||||
PermissionHolder.this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private final NodesCache nodesCopy = new NodesCache();
|
|
||||||
|
|
||||||
// used to ensure thread safe access to the backing nodes map
|
|
||||||
private final ReentrantLock nodesLock = new ReentrantLock();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The holders transient nodes.
|
* The holders transient nodes.
|
||||||
@ -160,46 +124,18 @@ public abstract class PermissionHolder {
|
|||||||
* <p>These are nodes which are never stored or persisted to a file, and only
|
* <p>These are nodes which are never stored or persisted to a file, and only
|
||||||
* last until the end of the objects lifetime. (for a group, that's when the server stops, and for a user, it's when
|
* last until the end of the objects lifetime. (for a group, that's when the server stops, and for a user, it's when
|
||||||
* they log out, or get unloaded.)</p>
|
* they log out, or get unloaded.)</p>
|
||||||
*
|
|
||||||
* <p>Nodes are mapped by the result of {@link Node#getFullContexts()}, and keys are sorted by the weight of the
|
|
||||||
* ContextSet. ContextSets are ordered first by the presence of a server key, then by the presence of a world
|
|
||||||
* key, and finally by the overall size of the set. Nodes are ordered according to the priority rules
|
|
||||||
* defined in {@link NodeComparator}.</p>
|
|
||||||
*/
|
*/
|
||||||
private final SortedSetMultimap<ImmutableContextSet, Node> transientNodes = MultimapBuilder
|
private final NodeMap transientNodes = new NodeMap(this);
|
||||||
.treeKeys(ContextSetComparator.reverse())
|
|
||||||
.treeSetValues(NodeComparator.reverse())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches an immutable copy of the above transientNodes multimap
|
|
||||||
*/
|
|
||||||
private final class TransientNodesCache extends Cache<ImmutableSetMultimap<ImmutableContextSet, Node>> {
|
|
||||||
@Override
|
|
||||||
protected ImmutableSetMultimap<ImmutableContextSet, Node> supply() {
|
|
||||||
PermissionHolder.this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
return ImmutableSetMultimap.copyOf(PermissionHolder.this.transientNodes);
|
|
||||||
} finally {
|
|
||||||
PermissionHolder.this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private final TransientNodesCache transientNodesCopy = new TransientNodesCache();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caches the holders weight lookup
|
* Caches the holders weight lookup
|
||||||
*/
|
*/
|
||||||
private final class WeightCache extends Cache<OptionalInt> {
|
private final Cache<OptionalInt> weightCache = new Cache<OptionalInt>() {
|
||||||
@Override
|
@Override
|
||||||
protected OptionalInt supply() {
|
protected OptionalInt supply() {
|
||||||
return calculateWeight();
|
return calculateWeight();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
private final WeightCache weightCache = new WeightCache();
|
|
||||||
|
|
||||||
// used to ensure thread safe access to the backing transientNodes map
|
|
||||||
private final ReentrantLock transientNodesLock = new ReentrantLock();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lock used by Storage implementations to prevent concurrent read/writes
|
* Lock used by Storage implementations to prevent concurrent read/writes
|
||||||
@ -216,6 +152,11 @@ public abstract class PermissionHolder {
|
|||||||
*/
|
*/
|
||||||
private final Set<StateListener> stateListeners = ConcurrentHashMap.newKeySet();
|
private final Set<StateListener> stateListeners = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
protected PermissionHolder(String objectName, LuckPermsPlugin plugin) {
|
||||||
|
this.objectName = objectName;
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
public String getObjectName() {
|
public String getObjectName() {
|
||||||
return this.objectName;
|
return this.objectName;
|
||||||
}
|
}
|
||||||
@ -233,8 +174,8 @@ public abstract class PermissionHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void invalidateCache() {
|
private void invalidateCache() {
|
||||||
this.nodesCopy.invalidate();
|
this.enduringNodes.invalidate();
|
||||||
this.transientNodesCopy.invalidate();
|
this.transientNodes.invalidate();
|
||||||
this.weightCache.invalidate();
|
this.weightCache.invalidate();
|
||||||
|
|
||||||
// Invalidate listeners
|
// Invalidate listeners
|
||||||
@ -295,13 +236,32 @@ public abstract class PermissionHolder {
|
|||||||
*/
|
*/
|
||||||
public abstract ApiPermissionHolder getDelegate();
|
public abstract ApiPermissionHolder getDelegate();
|
||||||
|
|
||||||
|
public NodeMap getData(NodeMapType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ENDURING:
|
||||||
|
return this.enduringNodes;
|
||||||
|
case TRANSIENT:
|
||||||
|
return this.transientNodes;
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeMap getEnduringData() {
|
||||||
|
return this.enduringNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeMap getTransientData() {
|
||||||
|
return this.transientNodes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an immutable copy of this objects nodes
|
* Returns an immutable copy of this objects nodes
|
||||||
*
|
*
|
||||||
* @return an immutable copy of the multimap storing this objects nodes
|
* @return an immutable copy of the multimap storing this objects nodes
|
||||||
*/
|
*/
|
||||||
public ImmutableSetMultimap<ImmutableContextSet, Node> getEnduringNodes() {
|
public ImmutableSetMultimap<ImmutableContextSet, Node> getEnduringNodes() {
|
||||||
return this.nodesCopy.get();
|
return this.enduringNodes.immutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -310,7 +270,7 @@ public abstract class PermissionHolder {
|
|||||||
* @return an immutable copy of the multimap storing this objects transient nodes
|
* @return an immutable copy of the multimap storing this objects transient nodes
|
||||||
*/
|
*/
|
||||||
public ImmutableSetMultimap<ImmutableContextSet, Node> getTransientNodes() {
|
public ImmutableSetMultimap<ImmutableContextSet, Node> getTransientNodes() {
|
||||||
return this.transientNodesCopy.get();
|
return this.transientNodes.immutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -319,15 +279,7 @@ public abstract class PermissionHolder {
|
|||||||
* @param set the set of nodes to apply to the object
|
* @param set the set of nodes to apply to the object
|
||||||
*/
|
*/
|
||||||
public void setEnduringNodes(Set<Node> set) {
|
public void setEnduringNodes(Set<Node> set) {
|
||||||
this.nodesLock.lock();
|
this.enduringNodes.setContent(set);
|
||||||
try {
|
|
||||||
this.nodes.clear();
|
|
||||||
for (Node n : set) {
|
|
||||||
this.nodes.put(n.getFullContexts().makeImmutable(), n);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,37 +289,7 @@ public abstract class PermissionHolder {
|
|||||||
* @param multimap the replacement multimap
|
* @param multimap the replacement multimap
|
||||||
*/
|
*/
|
||||||
public void replaceEnduringNodes(Multimap<ImmutableContextSet, Node> multimap) {
|
public void replaceEnduringNodes(Multimap<ImmutableContextSet, Node> multimap) {
|
||||||
this.nodesLock.lock();
|
this.enduringNodes.setContent(multimap);
|
||||||
try {
|
|
||||||
this.nodes.clear();
|
|
||||||
this.nodes.putAll(multimap);
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
invalidateCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTransientNodes(Set<Node> set) {
|
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.transientNodes.clear();
|
|
||||||
for (Node n : set) {
|
|
||||||
this.transientNodes.put(n.getFullContexts().makeImmutable(), n);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
invalidateCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replaceTransientNodes(Multimap<ImmutableContextSet, Node> multimap) {
|
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.transientNodes.clear();
|
|
||||||
this.transientNodes.putAll(multimap);
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,172 +300,49 @@ public abstract class PermissionHolder {
|
|||||||
*/
|
*/
|
||||||
public LinkedHashSet<Node> getOwnNodesSet() {
|
public LinkedHashSet<Node> getOwnNodesSet() {
|
||||||
LinkedHashSet<Node> ret = new LinkedHashSet<>();
|
LinkedHashSet<Node> ret = new LinkedHashSet<>();
|
||||||
|
this.transientNodes.copyTo(ret);
|
||||||
this.transientNodesLock.lock();
|
this.enduringNodes.copyTo(ret);
|
||||||
try {
|
|
||||||
ret.addAll(this.transientNodes.values());
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
ret.addAll(this.nodes.values());
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Node> getOwnNodes() {
|
public List<Node> getOwnNodes() {
|
||||||
List<Node> ret = new ArrayList<>();
|
List<Node> ret = new ArrayList<>();
|
||||||
|
this.transientNodes.copyTo(ret);
|
||||||
|
this.enduringNodes.copyTo(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
this.transientNodesLock.lock();
|
public List<Node> getOwnNodes(ContextSet filter) {
|
||||||
try {
|
List<Node> ret = new ArrayList<>();
|
||||||
ret.addAll(this.transientNodes.values());
|
this.transientNodes.copyTo(ret, filter);
|
||||||
} finally {
|
this.enduringNodes.copyTo(ret, filter);
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
ret.addAll(this.nodes.values());
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SortedSet<LocalizedNode> getOwnNodesSorted() {
|
public SortedSet<LocalizedNode> getOwnNodesSorted() {
|
||||||
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
|
||||||
|
this.transientNodes.copyToLocalized(ret);
|
||||||
this.transientNodesLock.lock();
|
this.enduringNodes.copyToLocalized(ret);
|
||||||
try {
|
|
||||||
for (Node node : this.transientNodes.values()) {
|
|
||||||
ret.add(ImmutableLocalizedNode.of(node, getObjectName()));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
for (Node node : this.nodes.values()) {
|
|
||||||
ret.add(ImmutableLocalizedNode.of(node, getObjectName()));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Node> filterEnduringNodes(ContextSet filter) {
|
|
||||||
return filterEnduringNodes(new ArrayList<>(), filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <C extends Collection<Node>> C filterEnduringNodes(C ret, ContextSet filter) {
|
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.nodes.asMap().entrySet()) {
|
|
||||||
if (e.getKey().isSatisfiedBy(filter)) {
|
|
||||||
ret.addAll(e.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Node> filterTransientNodes(ContextSet filter) {
|
|
||||||
return filterTransientNodes(new ArrayList<>(), filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <C extends Collection<Node>> C filterTransientNodes(C ret, ContextSet filter) {
|
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.transientNodes.asMap().entrySet()) {
|
|
||||||
if (e.getKey().isSatisfiedBy(filter)) {
|
|
||||||
ret.addAll(e.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Node> filterNodes(ContextSet filter) {
|
|
||||||
return filterNodes(new ArrayList<>(), filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <C extends Collection<Node>> C filterNodes(C ret, ContextSet filter) {
|
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.transientNodes.asMap().entrySet()) {
|
|
||||||
if (e.getKey().isSatisfiedBy(filter)) {
|
|
||||||
ret.addAll(e.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.nodes.asMap().entrySet()) {
|
|
||||||
if (e.getKey().isSatisfiedBy(filter)) {
|
|
||||||
ret.addAll(e.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeIf(Predicate<Node> predicate) {
|
public boolean removeIf(Predicate<Node> predicate) {
|
||||||
boolean result;
|
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
if (!this.enduringNodes.removeIf(predicate)) {
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
result = this.nodes.values().removeIf(predicate);
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
|
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeIfTransient(Predicate<Node> predicate) {
|
public boolean removeIfTransient(Predicate<Node> predicate) {
|
||||||
boolean result;
|
boolean result = this.transientNodes.removeIf(predicate);
|
||||||
|
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
result = this.transientNodes.values().removeIf(predicate);
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +367,7 @@ public abstract class PermissionHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get and add the objects own nodes
|
// get and add the objects own nodes
|
||||||
List<Node> nodes = filterNodes(context.getContexts());
|
List<Node> nodes = getOwnNodes(context.getContexts());
|
||||||
for (Node node : nodes) {
|
for (Node node : nodes) {
|
||||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, getObjectName());
|
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, getObjectName());
|
||||||
accumulator.add(localizedNode);
|
accumulator.add(localizedNode);
|
||||||
@ -702,7 +501,7 @@ public abstract class PermissionHolder {
|
|||||||
entries = resolveInheritances(new LinkedList<>(), null, context);
|
entries = resolveInheritances(new LinkedList<>(), null, context);
|
||||||
} else {
|
} else {
|
||||||
entries = new LinkedList<>();
|
entries = new LinkedList<>();
|
||||||
for (Node n : filterNodes(context.getContexts())) {
|
for (Node n : getOwnNodes(context.getContexts())) {
|
||||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
|
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
|
||||||
entries.add(localizedNode);
|
entries.add(localizedNode);
|
||||||
}
|
}
|
||||||
@ -727,7 +526,7 @@ public abstract class PermissionHolder {
|
|||||||
entries = resolveInheritances(new LinkedList<>(), null, context);
|
entries = resolveInheritances(new LinkedList<>(), null, context);
|
||||||
} else {
|
} else {
|
||||||
entries = new LinkedList<>();
|
entries = new LinkedList<>();
|
||||||
for (Node n : filterNodes(context.getContexts())) {
|
for (Node n : getOwnNodes(context.getContexts())) {
|
||||||
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
|
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
|
||||||
entries.add(localizedNode);
|
entries.add(localizedNode);
|
||||||
}
|
}
|
||||||
@ -795,7 +594,7 @@ public abstract class PermissionHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get and add the objects own nodes
|
// get and add the objects own nodes
|
||||||
List<Node> nodes = filterNodes(context.getContexts());
|
List<Node> nodes = getOwnNodes(context.getContexts());
|
||||||
|
|
||||||
for (Node node : nodes) {
|
for (Node node : nodes) {
|
||||||
if (!node.getValuePrimitive()) continue;
|
if (!node.getValuePrimitive()) continue;
|
||||||
@ -903,47 +702,11 @@ public abstract class PermissionHolder {
|
|||||||
* @return true if permissions had expired and were removed
|
* @return true if permissions had expired and were removed
|
||||||
*/
|
*/
|
||||||
public boolean auditTemporaryPermissions() {
|
public boolean auditTemporaryPermissions() {
|
||||||
boolean work = false;
|
|
||||||
Set<Node> removed = new HashSet<>();
|
Set<Node> removed = new HashSet<>();
|
||||||
|
|
||||||
ImmutableSet<Node> before = ImmutableSet.copyOf(getOwnNodesSet());
|
ImmutableSet<Node> before = ImmutableSet.copyOf(getOwnNodesSet());
|
||||||
|
|
||||||
this.nodesLock.lock();
|
if (this.enduringNodes.auditTemporaryNodes(removed) | this.transientNodes.auditTemporaryNodes(removed)) {
|
||||||
try {
|
|
||||||
Iterator<Node> it = this.nodes.values().iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Node entry = it.next();
|
|
||||||
if (entry.hasExpired()) {
|
|
||||||
removed.add(entry);
|
|
||||||
work = true;
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (work) {
|
|
||||||
invalidateCache();
|
|
||||||
work = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
Iterator<Node> it = this.transientNodes.values().iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Node entry = it.next();
|
|
||||||
if (entry.hasExpired()) {
|
|
||||||
removed.add(entry);
|
|
||||||
work = true;
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (work) {
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,7 +715,6 @@ public abstract class PermissionHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImmutableSet<Node> after = ImmutableSet.copyOf(getOwnNodesSet());
|
ImmutableSet<Node> after = ImmutableSet.copyOf(getOwnNodesSet());
|
||||||
|
|
||||||
for (Node r : removed) {
|
for (Node r : removed) {
|
||||||
this.plugin.getEventFactory().handleNodeRemove(r, this, before, after);
|
this.plugin.getEventFactory().handleNodeRemove(r, this, before, after);
|
||||||
}
|
}
|
||||||
@ -960,8 +722,8 @@ public abstract class PermissionHolder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Node> getAlmostEquals(Node node, boolean t) {
|
public Optional<Node> getAlmostEquals(Node node, NodeMapType type) {
|
||||||
for (Node n : t ? getTransientNodes().values() : getEnduringNodes().values()) {
|
for (Node n : getData(type).immutable().values()) {
|
||||||
if (n.almostEquals(node)) {
|
if (n.almostEquals(node)) {
|
||||||
return Optional.of(n);
|
return Optional.of(n);
|
||||||
}
|
}
|
||||||
@ -974,19 +736,19 @@ public abstract class PermissionHolder {
|
|||||||
* Check if the holder has a permission node
|
* Check if the holder has a permission node
|
||||||
*
|
*
|
||||||
* @param node the node to check
|
* @param node the node to check
|
||||||
* @param checkTransient whether to check transient nodes
|
* @param type which backing map to check
|
||||||
* @return a tristate
|
* @return a tristate
|
||||||
*/
|
*/
|
||||||
public Tristate hasPermission(Node node, boolean checkTransient) {
|
public Tristate hasPermission(Node node, NodeMapType type) {
|
||||||
if (this.getType().isGroup() && node.isGroupNode() && node.getGroupName().equalsIgnoreCase(getObjectName())) {
|
if (this.getType().isGroup() && node.isGroupNode() && node.getGroupName().equalsIgnoreCase(getObjectName())) {
|
||||||
return Tristate.TRUE;
|
return Tristate.TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getAlmostEquals(node, checkTransient).map(Node::getTristate).orElse(Tristate.UNDEFINED);
|
return getAlmostEquals(node, type).map(Node::getTristate).orElse(Tristate.UNDEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tristate hasPermission(Node node) {
|
public Tristate hasPermission(Node node) {
|
||||||
return hasPermission(node, false);
|
return hasPermission(node, NodeMapType.ENDURING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1021,20 +783,13 @@ public abstract class PermissionHolder {
|
|||||||
* @param node the node to set
|
* @param node the node to set
|
||||||
*/
|
*/
|
||||||
public DataMutateResult setPermission(Node node) {
|
public DataMutateResult setPermission(Node node) {
|
||||||
if (hasPermission(node, false) != Tristate.UNDEFINED) {
|
if (hasPermission(node, NodeMapType.ENDURING) != Tristate.UNDEFINED) {
|
||||||
return DataMutateResult.ALREADY_HAS;
|
return DataMutateResult.ALREADY_HAS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
this.enduringNodes.add(node);
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.nodes.put(node.getFullContexts().makeImmutable(), node);
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
|
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
|
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
|
||||||
@ -1052,7 +807,7 @@ public abstract class PermissionHolder {
|
|||||||
if (node.isTemporary()) {
|
if (node.isTemporary()) {
|
||||||
if (modifier == TemporaryModifier.ACCUMULATE) {
|
if (modifier == TemporaryModifier.ACCUMULATE) {
|
||||||
// Try to accumulate with an existing node
|
// Try to accumulate with an existing node
|
||||||
Optional<Node> existing = getAlmostEquals(node, false);
|
Optional<Node> existing = getAlmostEquals(node, NodeMapType.ENDURING);
|
||||||
|
|
||||||
// An existing node was found
|
// An existing node was found
|
||||||
if (existing.isPresent()) {
|
if (existing.isPresent()) {
|
||||||
@ -1061,26 +816,19 @@ public abstract class PermissionHolder {
|
|||||||
// Create a new node with the same properties, but add the expiry dates together
|
// Create a new node with the same properties, but add the expiry dates together
|
||||||
Node newNode = NodeFactory.builder(node).setExpiry(previous.getExpiryUnixTime() + node.getSecondsTilExpiry()).build();
|
Node newNode = NodeFactory.builder(node).setExpiry(previous.getExpiryUnixTime() + node.getSecondsTilExpiry()).build();
|
||||||
|
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
|
||||||
|
|
||||||
// Remove the old node & add the new one.
|
// Remove the old node & add the new one.
|
||||||
this.nodesLock.lock();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
try {
|
this.enduringNodes.replace(newNode, previous);
|
||||||
this.nodes.remove(previous.getFullContexts().makeImmutable(), previous);
|
|
||||||
this.nodes.put(newNode.getFullContexts().makeImmutable(), newNode);
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeAdd(newNode, this, before, after);
|
this.plugin.getEventFactory().handleNodeAdd(newNode, this, before, after);
|
||||||
return Maps.immutableEntry(DataMutateResult.SUCCESS, newNode);
|
return Maps.immutableEntry(DataMutateResult.SUCCESS, newNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (modifier == TemporaryModifier.REPLACE) {
|
} else if (modifier == TemporaryModifier.REPLACE) {
|
||||||
// Try to replace an existing node
|
// Try to replace an existing node
|
||||||
Optional<Node> existing = getAlmostEquals(node, false);
|
Optional<Node> existing = getAlmostEquals(node, NodeMapType.ENDURING);
|
||||||
|
|
||||||
// An existing node was found
|
// An existing node was found
|
||||||
if (existing.isPresent()) {
|
if (existing.isPresent()) {
|
||||||
@ -1090,17 +838,10 @@ public abstract class PermissionHolder {
|
|||||||
if (node.getExpiryUnixTime() > previous.getExpiryUnixTime()) {
|
if (node.getExpiryUnixTime() > previous.getExpiryUnixTime()) {
|
||||||
|
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
this.enduringNodes.replace(node, previous);
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.nodes.remove(previous.getFullContexts().makeImmutable(), previous);
|
|
||||||
this.nodes.put(node.getFullContexts().makeImmutable(), node);
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
|
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
|
||||||
return Maps.immutableEntry(DataMutateResult.SUCCESS, node);
|
return Maps.immutableEntry(DataMutateResult.SUCCESS, node);
|
||||||
}
|
}
|
||||||
@ -1120,21 +861,13 @@ public abstract class PermissionHolder {
|
|||||||
* @param node the node to set
|
* @param node the node to set
|
||||||
*/
|
*/
|
||||||
public DataMutateResult setTransientPermission(Node node) {
|
public DataMutateResult setTransientPermission(Node node) {
|
||||||
if (hasPermission(node, true) != Tristate.UNDEFINED) {
|
if (hasPermission(node, NodeMapType.TRANSIENT) != Tristate.UNDEFINED) {
|
||||||
return DataMutateResult.ALREADY_HAS;
|
return DataMutateResult.ALREADY_HAS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableCollection<Node> before = getTransientNodes().values();
|
ImmutableCollection<Node> before = getTransientNodes().values();
|
||||||
|
this.transientNodes.add(node);
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.transientNodes.put(node.getFullContexts().makeImmutable(), node);
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
|
|
||||||
ImmutableCollection<Node> after = getTransientNodes().values();
|
ImmutableCollection<Node> after = getTransientNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
|
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
|
||||||
@ -1147,22 +880,15 @@ public abstract class PermissionHolder {
|
|||||||
* @param node the node to unset
|
* @param node the node to unset
|
||||||
*/
|
*/
|
||||||
public DataMutateResult unsetPermission(Node node) {
|
public DataMutateResult unsetPermission(Node node) {
|
||||||
if (hasPermission(node, false) == Tristate.UNDEFINED) {
|
if (hasPermission(node, NodeMapType.ENDURING) == Tristate.UNDEFINED) {
|
||||||
return DataMutateResult.LACKS;
|
return DataMutateResult.LACKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
this.enduringNodes.remove(node);
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.nodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node));
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
|
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeRemove(node, this, before, after);
|
this.plugin.getEventFactory().handleNodeRemove(node, this, before, after);
|
||||||
return DataMutateResult.SUCCESS;
|
return DataMutateResult.SUCCESS;
|
||||||
}
|
}
|
||||||
@ -1173,22 +899,15 @@ public abstract class PermissionHolder {
|
|||||||
* @param node the node to unset
|
* @param node the node to unset
|
||||||
*/
|
*/
|
||||||
public DataMutateResult unsetTransientPermission(Node node) {
|
public DataMutateResult unsetTransientPermission(Node node) {
|
||||||
if (hasPermission(node, true) == Tristate.UNDEFINED) {
|
if (hasPermission(node, NodeMapType.TRANSIENT) == Tristate.UNDEFINED) {
|
||||||
return DataMutateResult.LACKS;
|
return DataMutateResult.LACKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableCollection<Node> before = getTransientNodes().values();
|
ImmutableCollection<Node> before = getTransientNodes().values();
|
||||||
|
this.transientNodes.remove(node);
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.transientNodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node));
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
|
|
||||||
ImmutableCollection<Node> after = getTransientNodes().values();
|
ImmutableCollection<Node> after = getTransientNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeRemove(node, this, before, after);
|
this.plugin.getEventFactory().handleNodeRemove(node, this, before, after);
|
||||||
return DataMutateResult.SUCCESS;
|
return DataMutateResult.SUCCESS;
|
||||||
}
|
}
|
||||||
@ -1206,14 +925,7 @@ public abstract class PermissionHolder {
|
|||||||
*/
|
*/
|
||||||
public boolean clearNodes() {
|
public boolean clearNodes() {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
this.enduringNodes.clear();
|
||||||
this.nodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.nodes.clear();
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
@ -1227,13 +939,7 @@ public abstract class PermissionHolder {
|
|||||||
|
|
||||||
public boolean clearNodes(ContextSet contextSet) {
|
public boolean clearNodes(ContextSet contextSet) {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
this.nodesLock.lock();
|
this.enduringNodes.clear(contextSet);
|
||||||
try {
|
|
||||||
this.nodes.removeAll(contextSet.makeImmutable());
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
@ -1248,22 +954,16 @@ public abstract class PermissionHolder {
|
|||||||
public boolean clearParents(boolean giveDefault) {
|
public boolean clearParents(boolean giveDefault) {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
|
||||||
this.nodesLock.lock();
|
if (!this.enduringNodes.removeIf(Node::isGroupNode)) {
|
||||||
try {
|
return false;
|
||||||
boolean b = this.nodes.values().removeIf(Node::isGroupNode);
|
|
||||||
if (!b) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getType().isUser() && giveDefault) {
|
if (this.getType().isUser() && giveDefault) {
|
||||||
this.plugin.getUserManager().giveDefaultIfNeeded((User) this, false);
|
this.plugin.getUserManager().giveDefaultIfNeeded((User) this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1271,125 +971,71 @@ public abstract class PermissionHolder {
|
|||||||
public boolean clearParents(ContextSet contextSet, boolean giveDefault) {
|
public boolean clearParents(ContextSet contextSet, boolean giveDefault) {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
|
||||||
this.nodesLock.lock();
|
if (!this.enduringNodes.removeIf(contextSet, Node::isGroupNode)) {
|
||||||
try {
|
return false;
|
||||||
SortedSet<Node> nodes = this.nodes.get(contextSet.makeImmutable());
|
|
||||||
if (nodes == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean b = nodes.removeIf(Node::isGroupNode);
|
|
||||||
if (!b) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getType().isUser() && giveDefault) {
|
if (this.getType().isUser() && giveDefault) {
|
||||||
this.plugin.getUserManager().giveDefaultIfNeeded((User) this, false);
|
this.plugin.getUserManager().giveDefaultIfNeeded((User) this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean clearMeta(MetaType type) {
|
public boolean clearMeta(MetaType type) {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
if (!this.enduringNodes.removeIf(type::matches)) {
|
||||||
this.nodesLock.lock();
|
return false;
|
||||||
try {
|
|
||||||
if (!this.nodes.values().removeIf(type::matches)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean clearMeta(MetaType type, ContextSet contextSet) {
|
public boolean clearMeta(MetaType type, ContextSet contextSet) {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
if (!this.enduringNodes.removeIf(contextSet, type::matches)) {
|
||||||
this.nodesLock.lock();
|
return false;
|
||||||
try {
|
|
||||||
SortedSet<Node> nodes = this.nodes.get(contextSet.makeImmutable());
|
|
||||||
if (nodes == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean b = nodes.removeIf(type::matches);
|
|
||||||
if (!b) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean clearMetaKeys(String key, boolean temp) {
|
public boolean clearMetaKeys(String key, boolean temp) {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
if (!this.enduringNodes.removeIf(n -> n.isMeta() && (n.isTemporary() == temp) && n.getMeta().getKey().equalsIgnoreCase(key))) {
|
||||||
this.nodesLock.lock();
|
return false;
|
||||||
try {
|
|
||||||
boolean b = this.nodes.values().removeIf(n -> n.isMeta() && (n.isTemporary() == temp) && n.getMeta().getKey().equalsIgnoreCase(key));
|
|
||||||
if (!b) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean clearMetaKeys(String key, ContextSet contextSet, boolean temp) {
|
public boolean clearMetaKeys(String key, ContextSet contextSet, boolean temp) {
|
||||||
ImmutableCollection<Node> before = getEnduringNodes().values();
|
ImmutableCollection<Node> before = getEnduringNodes().values();
|
||||||
|
if (!this.enduringNodes.removeIf(contextSet, n -> n.isMeta() && (n.isTemporary() == temp) && n.getMeta().getKey().equalsIgnoreCase(key))) {
|
||||||
this.nodesLock.lock();
|
return false;
|
||||||
try {
|
|
||||||
SortedSet<Node> nodes = this.nodes.get(contextSet.makeImmutable());
|
|
||||||
if (nodes == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean b = nodes.removeIf(n -> n.isMeta() && (n.isTemporary() == temp) && n.getMeta().getKey().equalsIgnoreCase(key));
|
|
||||||
if (!b) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.nodesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getEnduringNodes().values();
|
ImmutableCollection<Node> after = getEnduringNodes().values();
|
||||||
|
|
||||||
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
this.plugin.getEventFactory().handleNodeClear(this, before, after);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean clearTransientNodes() {
|
public boolean clearTransientNodes() {
|
||||||
ImmutableCollection<Node> before = getTransientNodes().values();
|
ImmutableCollection<Node> before = getTransientNodes().values();
|
||||||
|
this.transientNodes.clear();
|
||||||
this.transientNodesLock.lock();
|
|
||||||
try {
|
|
||||||
this.transientNodes.clear();
|
|
||||||
} finally {
|
|
||||||
this.transientNodesLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
ImmutableCollection<Node> after = getTransientNodes().values();
|
ImmutableCollection<Node> after = getTransientNodes().values();
|
||||||
|
|
||||||
|
@ -40,17 +40,17 @@ import javax.annotation.Nonnull;
|
|||||||
/**
|
/**
|
||||||
* Holds a Node and plus an owning object. All calls are passed onto the contained Node instance.
|
* Holds a Node and plus an owning object. All calls are passed onto the contained Node instance.
|
||||||
*/
|
*/
|
||||||
public final class ImmutableTransientNode implements Node {
|
public final class ImmutableTransientNode<O> implements Node {
|
||||||
public static ImmutableTransientNode of(Node node, Object owner) {
|
public static <O> ImmutableTransientNode<O> of(Node node, O owner) {
|
||||||
Objects.requireNonNull(node, "node");
|
Objects.requireNonNull(node, "node");
|
||||||
Objects.requireNonNull(owner, "owner");
|
Objects.requireNonNull(owner, "owner");
|
||||||
return new ImmutableTransientNode(node, owner);
|
return new ImmutableTransientNode<>(node, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Node node;
|
private final Node node;
|
||||||
private final Object owner;
|
private final O owner;
|
||||||
|
|
||||||
private ImmutableTransientNode(Node node, Object owner) {
|
private ImmutableTransientNode(Node node, O owner) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ public final class ImmutableTransientNode implements Node {
|
|||||||
return this.node;
|
return this.node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getOwner() {
|
public O getOwner() {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ public class ParentsByWeightHolder extends CachedPrimaryGroupHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Set<Group> groups = new LinkedHashSet<>();
|
Set<Group> groups = new LinkedHashSet<>();
|
||||||
for (Node node : this.user.filterNodes(contexts.getContexts())) {
|
for (Node node : this.user.getOwnNodes(contexts.getContexts())) {
|
||||||
if (!node.getValuePrimitive() || !node.isGroupNode()) {
|
if (!node.getValuePrimitive() || !node.isGroupNode()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user