From 4c3e28ba85e9ef1a0fe9a7af547c6e3be7c9d16f Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 21 Jan 2018 19:53:20 +0000 Subject: [PATCH] Only show enduring nodes in command output, encapsulate the Node multimaps in PermissionHolder into separate class --- .../bukkit/model/LPPermissionAttachment.java | 2 +- .../delegates/model/ApiPermissionHolder.java | 5 +- .../impl/generic/parent/ParentInfo.java | 2 +- .../generic/permission/PermissionInfo.java | 2 +- .../common/commands/impl/group/GroupInfo.java | 14 +- .../common/commands/impl/user/UserInfo.java | 14 +- .../lucko/luckperms/common/model/NodeMap.java | 293 ++++++++++ .../luckperms/common/model/NodeMapType.java | 32 + .../common/model/PermissionHolder.java | 552 ++++-------------- .../common/node/ImmutableTransientNode.java | 12 +- .../primarygroup/ParentsByWeightHolder.java | 2 +- 11 files changed, 451 insertions(+), 479 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/model/NodeMapType.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java index cc987aab..9c9cb465 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java @@ -175,7 +175,7 @@ public class LPPermissionAttachment extends PermissionAttachment { .build(); // convert the constructed node to a transient node instance to refer back to this attachment - ImmutableTransientNode transientNode = ImmutableTransientNode.of(node, this); + ImmutableTransientNode transientNode = ImmutableTransientNode.of(node, this); // set the transient node User user = this.permissible.getUser(); diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java index 7a040722..dc4d615e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java @@ -41,6 +41,7 @@ import me.lucko.luckperms.api.caching.CachedData; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; 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.node.MetaType; import me.lucko.luckperms.common.utils.ImmutableCollectors; @@ -161,14 +162,14 @@ public class ApiPermissionHolder implements PermissionHolder { @Override public Tristate hasPermission(@Nonnull Node node) { Objects.requireNonNull(node, "node"); - return this.handle.hasPermission(node, false); + return this.handle.hasPermission(node, NodeMapType.ENDURING); } @Nonnull @Override public Tristate hasTransientPermission(@Nonnull Node node) { Objects.requireNonNull(node, "node"); - return this.handle.hasPermission(node, true); + return this.handle.hasPermission(node, NodeMapType.TRANSIENT); } @Nonnull diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java index 634192aa..a843c66d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java @@ -76,7 +76,7 @@ public class ParentInfo extends SharedSubCommand { SortMode sortMode = SortMode.determine(args); // get the holders nodes - List nodes = new ArrayList<>(holder.getOwnNodesSorted()); + List nodes = new ArrayList<>(holder.getEnduringData().asSortedSet()); // remove irrelevant types (these are displayed in the other info commands) nodes.removeIf(node -> !node.isGroupNode() || !node.getValuePrimitive()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java index ebed8fba..3c87bf54 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java @@ -76,7 +76,7 @@ public class PermissionInfo extends SharedSubCommand { SortMode sortMode = SortMode.determine(args); // get the holders nodes - List nodes = new ArrayList<>(holder.getOwnNodesSorted()); + List nodes = new ArrayList<>(holder.getEnduringData().asSortedSet()); // remove irrelevant types (these are displayed in the other info commands) nodes.removeIf(node -> diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java index 161538e2..b5c6e3f1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupInfo.java @@ -60,19 +60,19 @@ public class GroupInfo extends SubCommand { group.getName(), group.getDisplayName().orElse(group.getName()), group.getWeight().isPresent() ? group.getWeight().getAsInt() : "None", - group.getOwnNodes().size(), - group.getOwnNodes().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.getOwnNodes().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(), - group.getOwnNodes().stream().filter(Node::isMeta).mapToInt(n -> 1).sum() + group.getEnduringData().asList().size(), + group.getEnduringData().asList().stream().filter(n -> !(n.isGroupNode() || n.isPrefix() || n.isSuffix() || n.isMeta())).mapToInt(n -> 1).sum(), + group.getEnduringData().asList().stream().filter(Node::isPrefix).mapToInt(n -> 1).sum(), + group.getEnduringData().asList().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(), + group.getEnduringData().asList().stream().filter(Node::isMeta).mapToInt(n -> 1).sum() ); - Set parents = group.getOwnNodesSet().stream() + Set parents = group.getEnduringData().asSet().stream() .filter(Node::isGroupNode) .filter(Node::isPermanent) .collect(Collectors.toSet()); - Set tempParents = group.getOwnNodesSet().stream() + Set tempParents = group.getEnduringData().asSet().stream() .filter(Node::isGroupNode) .filter(Node::isTemporary) .collect(Collectors.toSet()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java index 8342d95e..79c86497 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java @@ -66,19 +66,19 @@ public class UserInfo extends SubCommand { user.getUuid(), status.asString(plugin.getLocaleManager()), user.getPrimaryGroup().getValue(), - user.getOwnNodes().size(), - user.getOwnNodes().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.getOwnNodes().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(), - user.getOwnNodes().stream().filter(Node::isMeta).mapToInt(n -> 1).sum() + user.getEnduringData().asList().size(), + user.getEnduringData().asList().stream().filter(n -> !(n.isGroupNode() || n.isPrefix() || n.isSuffix() || n.isMeta())).mapToInt(n -> 1).sum(), + user.getEnduringData().asList().stream().filter(Node::isPrefix).mapToInt(n -> 1).sum(), + user.getEnduringData().asList().stream().filter(Node::isSuffix).mapToInt(n -> 1).sum(), + user.getEnduringData().asList().stream().filter(Node::isMeta).mapToInt(n -> 1).sum() ); - Set parents = user.getOwnNodesSet().stream() + Set parents = user.getEnduringData().asSet().stream() .filter(Node::isGroupNode) .filter(Node::isPermanent) .collect(Collectors.toSet()); - Set tempParents = user.getOwnNodesSet().stream() + Set tempParents = user.getEnduringData().asSet().stream() .filter(Node::isGroupNode) .filter(Node::isTemporary) .collect(Collectors.toSet()); diff --git a/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java b/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java new file mode 100644 index 00000000..dc933622 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/model/NodeMap.java @@ -0,0 +1,293 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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. + * + *

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

+ */ + private final SortedSetMultimap 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 asList() { + this.lock.lock(); + try { + return new ArrayList<>(this.map.values()); + } finally { + this.lock.unlock(); + } + } + + public LinkedHashSet asSet() { + this.lock.lock(); + try { + return new LinkedHashSet<>(this.map.values()); + } finally { + this.lock.unlock(); + } + } + + public SortedSet asSortedSet() { + SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); + copyToLocalized(ret); + return ret; + } + + public void copyTo(Collection collection) { + this.lock.lock(); + try { + collection.addAll(this.map.values()); + } finally { + this.lock.unlock(); + } + } + + public void copyTo(Collection collection, ContextSet filter) { + this.lock.lock(); + try { + for (Map.Entry> e : this.map.asMap().entrySet()) { + if (e.getKey().isSatisfiedBy(filter)) { + collection.addAll(e.getValue()); + } + } + } finally { + this.lock.unlock(); + } + } + + public void copyToLocalized(Collection 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 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 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 multimap) { + this.lock.lock(); + try { + this.map.clear(); + this.map.putAll(multimap); + } finally { + this.lock.unlock(); + } + } + + boolean removeIf(Predicate predicate) { + this.lock.lock(); + try { + return this.map.values().removeIf(predicate); + } finally { + this.lock.unlock(); + } + } + + boolean removeIf(ContextSet contextSet, Predicate predicate) { + this.lock.lock(); + try { + SortedSet nodes = this.map.get(contextSet.makeImmutable()); + return nodes != null && nodes.removeIf(predicate); + } finally { + this.lock.unlock(); + } + } + + boolean auditTemporaryNodes(Set removed) { + boolean work = false; + + this.lock.lock(); + try { + Iterator 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> { + private final NodeMap handle; + + private NodeMapCache(NodeMap handle) { + this.handle = handle; + } + + @Override + protected ImmutableSetMultimap supply() { + this.handle.lock.lock(); + try { + return ImmutableSetMultimap.copyOf(this.handle.map); + } finally { + this.handle.lock.unlock(); + } + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/model/NodeMapType.java b/common/src/main/java/me/lucko/luckperms/common/model/NodeMapType.java new file mode 100644 index 00000000..9abdb773 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/model/NodeMapType.java @@ -0,0 +1,32 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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 + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 054f7eee..7c00bb1e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -31,8 +31,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Maps; 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.DataMutateResult; @@ -63,11 +61,9 @@ import me.lucko.luckperms.common.references.HolderReference; import me.lucko.luckperms.common.references.HolderType; import java.util.ArrayList; -import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -119,40 +115,8 @@ public abstract class PermissionHolder { /** * The holders persistent nodes. - * - *

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

*/ - private final SortedSetMultimap nodes = MultimapBuilder - .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> { - @Override - protected ImmutableSetMultimap 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(); + private final NodeMap enduringNodes = new NodeMap(this); /** * The holders transient nodes. @@ -160,46 +124,18 @@ public abstract class PermissionHolder { *

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 * they log out, or get unloaded.)

- * - *

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

*/ - private final SortedSetMultimap transientNodes = MultimapBuilder - .treeKeys(ContextSetComparator.reverse()) - .treeSetValues(NodeComparator.reverse()) - .build(); - - /** - * Caches an immutable copy of the above transientNodes multimap - */ - private final class TransientNodesCache extends Cache> { - @Override - protected ImmutableSetMultimap supply() { - PermissionHolder.this.transientNodesLock.lock(); - try { - return ImmutableSetMultimap.copyOf(PermissionHolder.this.transientNodes); - } finally { - PermissionHolder.this.transientNodesLock.unlock(); - } - } - } - private final TransientNodesCache transientNodesCopy = new TransientNodesCache(); + private final NodeMap transientNodes = new NodeMap(this); /** * Caches the holders weight lookup */ - private final class WeightCache extends Cache { + private final Cache weightCache = new Cache() { @Override protected OptionalInt supply() { 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 @@ -216,6 +152,11 @@ public abstract class PermissionHolder { */ private final Set stateListeners = ConcurrentHashMap.newKeySet(); + protected PermissionHolder(String objectName, LuckPermsPlugin plugin) { + this.objectName = objectName; + this.plugin = plugin; + } + public String getObjectName() { return this.objectName; } @@ -233,8 +174,8 @@ public abstract class PermissionHolder { } private void invalidateCache() { - this.nodesCopy.invalidate(); - this.transientNodesCopy.invalidate(); + this.enduringNodes.invalidate(); + this.transientNodes.invalidate(); this.weightCache.invalidate(); // Invalidate listeners @@ -295,13 +236,32 @@ public abstract class PermissionHolder { */ 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 * * @return an immutable copy of the multimap storing this objects nodes */ public ImmutableSetMultimap 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 */ public ImmutableSetMultimap 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 */ public void setEnduringNodes(Set set) { - this.nodesLock.lock(); - try { - this.nodes.clear(); - for (Node n : set) { - this.nodes.put(n.getFullContexts().makeImmutable(), n); - } - } finally { - this.nodesLock.unlock(); - } + this.enduringNodes.setContent(set); invalidateCache(); } @@ -337,37 +289,7 @@ public abstract class PermissionHolder { * @param multimap the replacement multimap */ public void replaceEnduringNodes(Multimap multimap) { - this.nodesLock.lock(); - try { - this.nodes.clear(); - this.nodes.putAll(multimap); - } finally { - this.nodesLock.unlock(); - } - invalidateCache(); - } - - public void setTransientNodes(Set 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 multimap) { - this.transientNodesLock.lock(); - try { - this.transientNodes.clear(); - this.transientNodes.putAll(multimap); - } finally { - this.transientNodesLock.unlock(); - } + this.enduringNodes.setContent(multimap); invalidateCache(); } @@ -378,172 +300,49 @@ public abstract class PermissionHolder { */ public LinkedHashSet getOwnNodesSet() { LinkedHashSet ret = new LinkedHashSet<>(); - - this.transientNodesLock.lock(); - try { - ret.addAll(this.transientNodes.values()); - } finally { - this.transientNodesLock.unlock(); - } - - this.nodesLock.lock(); - try { - ret.addAll(this.nodes.values()); - } finally { - this.nodesLock.unlock(); - } - + this.transientNodes.copyTo(ret); + this.enduringNodes.copyTo(ret); return ret; } public List getOwnNodes() { List ret = new ArrayList<>(); + this.transientNodes.copyTo(ret); + this.enduringNodes.copyTo(ret); + return ret; + } - this.transientNodesLock.lock(); - try { - ret.addAll(this.transientNodes.values()); - } finally { - this.transientNodesLock.unlock(); - } - - this.nodesLock.lock(); - try { - ret.addAll(this.nodes.values()); - } finally { - this.nodesLock.unlock(); - } - + public List getOwnNodes(ContextSet filter) { + List ret = new ArrayList<>(); + this.transientNodes.copyTo(ret, filter); + this.enduringNodes.copyTo(ret, filter); return ret; } public SortedSet getOwnNodesSorted() { SortedSet ret = new TreeSet<>(NodeWithContextComparator.reverse()); - - this.transientNodesLock.lock(); - 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 filterEnduringNodes(ContextSet filter) { - return filterEnduringNodes(new ArrayList<>(), filter); - } - - public > C filterEnduringNodes(C ret, ContextSet filter) { - this.nodesLock.lock(); - try { - for (Map.Entry> e : this.nodes.asMap().entrySet()) { - if (e.getKey().isSatisfiedBy(filter)) { - ret.addAll(e.getValue()); - } - } - } finally { - this.nodesLock.unlock(); - } - - return ret; - } - - public List filterTransientNodes(ContextSet filter) { - return filterTransientNodes(new ArrayList<>(), filter); - } - - public > C filterTransientNodes(C ret, ContextSet filter) { - this.transientNodesLock.lock(); - try { - for (Map.Entry> e : this.transientNodes.asMap().entrySet()) { - if (e.getKey().isSatisfiedBy(filter)) { - ret.addAll(e.getValue()); - } - } - } finally { - this.transientNodesLock.unlock(); - } - - return ret; - } - - public List filterNodes(ContextSet filter) { - return filterNodes(new ArrayList<>(), filter); - } - - public > C filterNodes(C ret, ContextSet filter) { - this.transientNodesLock.lock(); - try { - for (Map.Entry> 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> e : this.nodes.asMap().entrySet()) { - if (e.getKey().isSatisfiedBy(filter)) { - ret.addAll(e.getValue()); - } - } - } finally { - this.nodesLock.unlock(); - } - + this.transientNodes.copyToLocalized(ret); + this.enduringNodes.copyToLocalized(ret); return ret; } public boolean removeIf(Predicate predicate) { - boolean result; ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - result = this.nodes.values().removeIf(predicate); - } finally { - this.nodesLock.unlock(); - } - - if (!result) { + if (!this.enduringNodes.removeIf(predicate)) { return false; } - invalidateCache(); - ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeClear(this, before, after); return true; } public boolean removeIfTransient(Predicate predicate) { - boolean result; - - this.transientNodesLock.lock(); - try { - result = this.transientNodes.values().removeIf(predicate); - } finally { - this.transientNodesLock.unlock(); - } - + boolean result = this.transientNodes.removeIf(predicate); if (result) { invalidateCache(); } - return result; } @@ -568,7 +367,7 @@ public abstract class PermissionHolder { } // get and add the objects own nodes - List nodes = filterNodes(context.getContexts()); + List nodes = getOwnNodes(context.getContexts()); for (Node node : nodes) { ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, getObjectName()); accumulator.add(localizedNode); @@ -702,7 +501,7 @@ public abstract class PermissionHolder { entries = resolveInheritances(new LinkedList<>(), null, context); } else { entries = new LinkedList<>(); - for (Node n : filterNodes(context.getContexts())) { + for (Node n : getOwnNodes(context.getContexts())) { ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName()); entries.add(localizedNode); } @@ -727,7 +526,7 @@ public abstract class PermissionHolder { entries = resolveInheritances(new LinkedList<>(), null, context); } else { entries = new LinkedList<>(); - for (Node n : filterNodes(context.getContexts())) { + for (Node n : getOwnNodes(context.getContexts())) { ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName()); entries.add(localizedNode); } @@ -795,7 +594,7 @@ public abstract class PermissionHolder { } // get and add the objects own nodes - List nodes = filterNodes(context.getContexts()); + List nodes = getOwnNodes(context.getContexts()); for (Node node : nodes) { if (!node.getValuePrimitive()) continue; @@ -903,47 +702,11 @@ public abstract class PermissionHolder { * @return true if permissions had expired and were removed */ public boolean auditTemporaryPermissions() { - boolean work = false; Set removed = new HashSet<>(); ImmutableSet before = ImmutableSet.copyOf(getOwnNodesSet()); - this.nodesLock.lock(); - try { - Iterator 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 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) { + if (this.enduringNodes.auditTemporaryNodes(removed) | this.transientNodes.auditTemporaryNodes(removed)) { invalidateCache(); } @@ -952,7 +715,6 @@ public abstract class PermissionHolder { } ImmutableSet after = ImmutableSet.copyOf(getOwnNodesSet()); - for (Node r : removed) { this.plugin.getEventFactory().handleNodeRemove(r, this, before, after); } @@ -960,8 +722,8 @@ public abstract class PermissionHolder { return true; } - public Optional getAlmostEquals(Node node, boolean t) { - for (Node n : t ? getTransientNodes().values() : getEnduringNodes().values()) { + public Optional getAlmostEquals(Node node, NodeMapType type) { + for (Node n : getData(type).immutable().values()) { if (n.almostEquals(node)) { return Optional.of(n); } @@ -974,19 +736,19 @@ public abstract class PermissionHolder { * Check if the holder has a permission node * * @param node the node to check - * @param checkTransient whether to check transient nodes + * @param type which backing map to check * @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())) { 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) { - return hasPermission(node, false); + return hasPermission(node, NodeMapType.ENDURING); } /** @@ -1021,20 +783,13 @@ public abstract class PermissionHolder { * @param node the node to set */ public DataMutateResult setPermission(Node node) { - if (hasPermission(node, false) != Tristate.UNDEFINED) { + if (hasPermission(node, NodeMapType.ENDURING) != Tristate.UNDEFINED) { return DataMutateResult.ALREADY_HAS; } ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - this.nodes.put(node.getFullContexts().makeImmutable(), node); - } finally { - this.nodesLock.unlock(); - } + this.enduringNodes.add(node); invalidateCache(); - ImmutableCollection after = getEnduringNodes().values(); this.plugin.getEventFactory().handleNodeAdd(node, this, before, after); @@ -1052,7 +807,7 @@ public abstract class PermissionHolder { if (node.isTemporary()) { if (modifier == TemporaryModifier.ACCUMULATE) { // Try to accumulate with an existing node - Optional existing = getAlmostEquals(node, false); + Optional existing = getAlmostEquals(node, NodeMapType.ENDURING); // An existing node was found 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 Node newNode = NodeFactory.builder(node).setExpiry(previous.getExpiryUnixTime() + node.getSecondsTilExpiry()).build(); - ImmutableCollection before = getEnduringNodes().values(); - // Remove the old node & add the new one. - this.nodesLock.lock(); - try { - this.nodes.remove(previous.getFullContexts().makeImmutable(), previous); - this.nodes.put(newNode.getFullContexts().makeImmutable(), newNode); - } finally { - this.nodesLock.unlock(); - } - + ImmutableCollection before = getEnduringNodes().values(); + this.enduringNodes.replace(newNode, previous); invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeAdd(newNode, this, before, after); return Maps.immutableEntry(DataMutateResult.SUCCESS, newNode); } } else if (modifier == TemporaryModifier.REPLACE) { // Try to replace an existing node - Optional existing = getAlmostEquals(node, false); + Optional existing = getAlmostEquals(node, NodeMapType.ENDURING); // An existing node was found if (existing.isPresent()) { @@ -1090,17 +838,10 @@ public abstract class PermissionHolder { if (node.getExpiryUnixTime() > previous.getExpiryUnixTime()) { ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - this.nodes.remove(previous.getFullContexts().makeImmutable(), previous); - this.nodes.put(node.getFullContexts().makeImmutable(), node); - } finally { - this.nodesLock.unlock(); - } - + this.enduringNodes.replace(node, previous); invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeAdd(node, this, before, after); return Maps.immutableEntry(DataMutateResult.SUCCESS, node); } @@ -1120,21 +861,13 @@ public abstract class PermissionHolder { * @param node the node to set */ public DataMutateResult setTransientPermission(Node node) { - if (hasPermission(node, true) != Tristate.UNDEFINED) { + if (hasPermission(node, NodeMapType.TRANSIENT) != Tristate.UNDEFINED) { return DataMutateResult.ALREADY_HAS; } ImmutableCollection before = getTransientNodes().values(); - - this.transientNodesLock.lock(); - try { - this.transientNodes.put(node.getFullContexts().makeImmutable(), node); - } finally { - this.transientNodesLock.unlock(); - } - + this.transientNodes.add(node); invalidateCache(); - ImmutableCollection after = getTransientNodes().values(); this.plugin.getEventFactory().handleNodeAdd(node, this, before, after); @@ -1147,22 +880,15 @@ public abstract class PermissionHolder { * @param node the node to unset */ public DataMutateResult unsetPermission(Node node) { - if (hasPermission(node, false) == Tristate.UNDEFINED) { + if (hasPermission(node, NodeMapType.ENDURING) == Tristate.UNDEFINED) { return DataMutateResult.LACKS; } ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - this.nodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node)); - } finally { - this.nodesLock.unlock(); - } - + this.enduringNodes.remove(node); invalidateCache(); - ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeRemove(node, this, before, after); return DataMutateResult.SUCCESS; } @@ -1173,22 +899,15 @@ public abstract class PermissionHolder { * @param node the node to unset */ public DataMutateResult unsetTransientPermission(Node node) { - if (hasPermission(node, true) == Tristate.UNDEFINED) { + if (hasPermission(node, NodeMapType.TRANSIENT) == Tristate.UNDEFINED) { return DataMutateResult.LACKS; } ImmutableCollection before = getTransientNodes().values(); - - this.transientNodesLock.lock(); - try { - this.transientNodes.get(node.getFullContexts().makeImmutable()).removeIf(e -> e.almostEquals(node)); - } finally { - this.transientNodesLock.unlock(); - } - + this.transientNodes.remove(node); invalidateCache(); - ImmutableCollection after = getTransientNodes().values(); + this.plugin.getEventFactory().handleNodeRemove(node, this, before, after); return DataMutateResult.SUCCESS; } @@ -1206,14 +925,7 @@ public abstract class PermissionHolder { */ public boolean clearNodes() { ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - this.nodes.clear(); - } finally { - this.nodesLock.unlock(); - } - + this.enduringNodes.clear(); invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); @@ -1227,13 +939,7 @@ public abstract class PermissionHolder { public boolean clearNodes(ContextSet contextSet) { ImmutableCollection before = getEnduringNodes().values(); - this.nodesLock.lock(); - try { - this.nodes.removeAll(contextSet.makeImmutable()); - } finally { - this.nodesLock.unlock(); - } - + this.enduringNodes.clear(contextSet); invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); @@ -1248,22 +954,16 @@ public abstract class PermissionHolder { public boolean clearParents(boolean giveDefault) { ImmutableCollection before = getEnduringNodes().values(); - this.nodesLock.lock(); - try { - boolean b = this.nodes.values().removeIf(Node::isGroupNode); - if (!b) { - return false; - } - } finally { - this.nodesLock.unlock(); + if (!this.enduringNodes.removeIf(Node::isGroupNode)) { + return false; } - if (this.getType().isUser() && giveDefault) { this.plugin.getUserManager().giveDefaultIfNeeded((User) this, false); } invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeClear(this, before, after); return true; } @@ -1271,125 +971,71 @@ public abstract class PermissionHolder { public boolean clearParents(ContextSet contextSet, boolean giveDefault) { ImmutableCollection before = getEnduringNodes().values(); - this.nodesLock.lock(); - try { - SortedSet 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.enduringNodes.removeIf(contextSet, Node::isGroupNode)) { + return false; } - if (this.getType().isUser() && giveDefault) { this.plugin.getUserManager().giveDefaultIfNeeded((User) this, false); } + invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMeta(MetaType type) { ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - if (!this.nodes.values().removeIf(type::matches)) { - return false; - } - } finally { - this.nodesLock.unlock(); + if (!this.enduringNodes.removeIf(type::matches)) { + return false; } - invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMeta(MetaType type, ContextSet contextSet) { ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - SortedSet 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(); + if (!this.enduringNodes.removeIf(contextSet, type::matches)) { + return false; } - invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMetaKeys(String key, boolean temp) { ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - 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(); + if (!this.enduringNodes.removeIf(n -> n.isMeta() && (n.isTemporary() == temp) && n.getMeta().getKey().equalsIgnoreCase(key))) { + return false; } - invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearMetaKeys(String key, ContextSet contextSet, boolean temp) { ImmutableCollection before = getEnduringNodes().values(); - - this.nodesLock.lock(); - try { - SortedSet 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(); + if (!this.enduringNodes.removeIf(contextSet, n -> n.isMeta() && (n.isTemporary() == temp) && n.getMeta().getKey().equalsIgnoreCase(key))) { + return false; } - invalidateCache(); ImmutableCollection after = getEnduringNodes().values(); + this.plugin.getEventFactory().handleNodeClear(this, before, after); return true; } public boolean clearTransientNodes() { ImmutableCollection before = getTransientNodes().values(); - - this.transientNodesLock.lock(); - try { - this.transientNodes.clear(); - } finally { - this.transientNodesLock.unlock(); - } - + this.transientNodes.clear(); invalidateCache(); ImmutableCollection after = getTransientNodes().values(); diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableTransientNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableTransientNode.java index bfe72ffc..7678a3d5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableTransientNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableTransientNode.java @@ -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. */ -public final class ImmutableTransientNode implements Node { - public static ImmutableTransientNode of(Node node, Object owner) { +public final class ImmutableTransientNode implements Node { + public static ImmutableTransientNode of(Node node, O owner) { Objects.requireNonNull(node, "node"); Objects.requireNonNull(owner, "owner"); - return new ImmutableTransientNode(node, owner); + return new ImmutableTransientNode<>(node, owner); } 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.owner = owner; } @@ -69,7 +69,7 @@ public final class ImmutableTransientNode implements Node { return this.node; } - public Object getOwner() { + public O getOwner() { return this.owner; } diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java index 59d25934..b5941252 100644 --- a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java @@ -46,7 +46,7 @@ public class ParentsByWeightHolder extends CachedPrimaryGroupHolder { } Set groups = new LinkedHashSet<>(); - for (Node node : this.user.filterNodes(contexts.getContexts())) { + for (Node node : this.user.getOwnNodes(contexts.getContexts())) { if (!node.getValuePrimitive() || !node.isGroupNode()) { continue; }