Only show enduring nodes in command output, encapsulate the Node multimaps in PermissionHolder into separate class

This commit is contained in:
Luck 2018-01-21 19:53:20 +00:00
parent 821dc4ef56
commit 4c3e28ba85
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
11 changed files with 451 additions and 479 deletions

View File

@ -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();

View File

@ -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

View File

@ -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());

View File

@ -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 ->

View File

@ -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());

View File

@ -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());

View File

@ -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();
}
}
}
}

View File

@ -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
}

View File

@ -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();

View File

@ -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;
} }

View File

@ -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;
} }