Remove the locks in NodeMap to ease thread contention when lots of processes are resolving inheritance & refactor the way LocalizedNodes are created (#734)

This commit is contained in:
Luck 2018-05-04 16:16:12 +01:00
parent 55d59bb1c4
commit 2dbbea4993
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
13 changed files with 219 additions and 320 deletions

View File

@ -556,7 +556,7 @@ public interface Node {
* *
* @param value the value * @param value the value
* @return the builder * @return the builder
* @see Node#getValuePrimitive() * @see Node#getValue()
*/ */
@Nonnull @Nonnull
Builder setValue(boolean value); Builder setValue(boolean value);

View File

@ -44,7 +44,7 @@ public enum StandardNodeEquality implements NodeEqualityPredicate {
/** /**
* All attributes must match, except for * All attributes must match, except for
* {@link Node#getValuePrimitive() value}, which is ignored. * {@link Node#getValue() value}, which is ignored.
*/ */
IGNORE_VALUE, IGNORE_VALUE,
@ -59,14 +59,14 @@ public enum StandardNodeEquality implements NodeEqualityPredicate {
/** /**
* All attributes must match, except for * All attributes must match, except for
* {@link Node#getValuePrimitive() value} and the * {@link Node#getValue() value} and the
* {@link Node#getExpiry() expiry time}, which are ignored. * {@link Node#getExpiry() expiry time}, which are ignored.
*/ */
IGNORE_EXPIRY_TIME_AND_VALUE, IGNORE_EXPIRY_TIME_AND_VALUE,
/** /**
* All attributes must match, except for * All attributes must match, except for
* {@link Node#getValuePrimitive() value} and the if the node is * {@link Node#getValue() value} and the if the node is
* {@link Node#isTemporary() temporary}, which are ignored. * {@link Node#isTemporary() temporary}, which are ignored.
*/ */
IGNORE_VALUE_OR_IF_TEMPORARY; IGNORE_VALUE_OR_IF_TEMPORARY;

View File

@ -46,6 +46,7 @@ import me.lucko.luckperms.common.model.NodeMapType;
import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator; import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator;
import me.lucko.luckperms.common.node.factory.NodeFactory;
import me.lucko.luckperms.common.node.utils.MetaType; import me.lucko.luckperms.common.node.utils.MetaType;
import me.lucko.luckperms.common.node.utils.NodeTools; import me.lucko.luckperms.common.node.utils.NodeTools;
import me.lucko.luckperms.common.utils.ImmutableCollectors; import me.lucko.luckperms.common.utils.ImmutableCollectors;
@ -104,13 +105,15 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
@Nonnull @Nonnull
@Override @Override
public ImmutableSetMultimap<ImmutableContextSet, Node> getNodes() { public ImmutableSetMultimap<ImmutableContextSet, Node> getNodes() {
return this.handle.enduringData().immutable(); //noinspection unchecked
return (ImmutableSetMultimap) this.handle.enduringData().immutable();
} }
@Nonnull @Nonnull
@Override @Override
public ImmutableSetMultimap<ImmutableContextSet, Node> getTransientNodes() { public ImmutableSetMultimap<ImmutableContextSet, Node> getTransientNodes() {
return this.handle.transientData().immutable(); //noinspection unchecked
return (ImmutableSetMultimap) this.handle.transientData().immutable();
} }
@Nonnull @Nonnull
@ -183,7 +186,7 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
@Override @Override
public Map<String, Boolean> exportNodes(@Nonnull Contexts contexts, boolean lowerCase) { public Map<String, Boolean> exportNodes(@Nonnull Contexts contexts, boolean lowerCase) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
return ImmutableMap.copyOf(this.handle.exportNodesAndShorthand(contexts, lowerCase)); return ImmutableMap.copyOf(this.handle.exportPermissions(contexts, lowerCase, true));
} }
@Nonnull @Nonnull
@ -234,14 +237,26 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
@Override @Override
public boolean inheritsGroup(@Nonnull me.lucko.luckperms.api.Group group) { public boolean inheritsGroup(@Nonnull me.lucko.luckperms.api.Group group) {
Objects.requireNonNull(group, "group"); Objects.requireNonNull(group, "group");
return this.handle.inheritsGroup(ApiGroup.cast(group));
Group g = ApiGroup.cast(group);
if (this.handle.getType().isGroup() && g.getName().equals(this.handle.getObjectName())) {
return true;
}
return this.handle.hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(g.getName()).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
} }
@Override @Override
public boolean inheritsGroup(@Nonnull me.lucko.luckperms.api.Group group, @Nonnull ContextSet contextSet) { public boolean inheritsGroup(@Nonnull me.lucko.luckperms.api.Group group, @Nonnull ContextSet contextSet) {
Objects.requireNonNull(group, "group"); Objects.requireNonNull(group, "group");
Objects.requireNonNull(contextSet, "contextSet"); Objects.requireNonNull(contextSet, "contextSet");
return this.handle.inheritsGroup(ApiGroup.cast(group), contextSet);
Group g = ApiGroup.cast(group);
if (this.handle.getType().isGroup() && g.getName().equals(this.handle.getObjectName())) {
return true;
}
return this.handle.hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(g.getName()).withExtraContext(contextSet).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
} }
@Nonnull @Nonnull

View File

@ -190,7 +190,7 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
//noinspection ConstantConditions //noinspection ConstantConditions
return this.permission.get(contexts).join(); return this.permission.synchronous().get(contexts);
} }
@Nonnull @Nonnull
@ -199,7 +199,7 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
//noinspection ConstantConditions //noinspection ConstantConditions
return this.meta.get(contexts).join(); return this.meta.synchronous().get(contexts);
} }
@Nonnull @Nonnull

View File

@ -29,6 +29,7 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.caching.MetaContexts; import me.lucko.luckperms.api.caching.MetaContexts;
import me.lucko.luckperms.common.caching.type.MetaAccumulator; import me.lucko.luckperms.common.caching.type.MetaAccumulator;
import me.lucko.luckperms.common.calculators.CalculatorFactory; import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.PermissionHolder;
import java.util.Map; import java.util.Map;
@ -60,12 +61,12 @@ public abstract class HolderCachedData<T extends PermissionHolder> extends Abstr
@Override @Override
protected Map<String, Boolean> resolvePermissions() { protected Map<String, Boolean> resolvePermissions() {
return this.holder.exportNodesAndShorthand(true); return this.holder.exportPermissions(true, this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND));
} }
@Override @Override
protected Map<String, Boolean> resolvePermissions(Contexts contexts) { protected Map<String, Boolean> resolvePermissions(Contexts contexts) {
return this.holder.exportNodesAndShorthand(contexts, true); return this.holder.exportPermissions(contexts, true, this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND));
} }
@Override @Override

View File

@ -25,6 +25,8 @@
package me.lucko.luckperms.common.commands.generic.parent; package me.lucko.luckperms.common.commands.generic.parent;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.StandardNodeEquality;
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry; import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.command.CommandResult; import me.lucko.luckperms.common.command.CommandResult;
import me.lucko.luckperms.common.command.abstraction.SharedSubCommand; import me.lucko.luckperms.common.command.abstraction.SharedSubCommand;
@ -36,6 +38,7 @@ import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.command.CommandSpec; import me.lucko.luckperms.common.locale.command.CommandSpec;
import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.locale.message.Message;
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.PermissionHolder; import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.factory.NodeFactory; import me.lucko.luckperms.common.node.factory.NodeFactory;
@ -80,9 +83,10 @@ public class UserSwitchPrimaryGroup extends SharedSubCommand {
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;
} }
if (!user.inheritsGroup(group)) { Node node = NodeFactory.buildGroupNode(group.getName()).build();
if (!user.hasPermission(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_VALUE).asBoolean()) {
Message.USER_PRIMARYGROUP_ERROR_NOTMEMBER.send(sender, user.getFriendlyName(), group.getName()); Message.USER_PRIMARYGROUP_ERROR_NOTMEMBER.send(sender, user.getFriendlyName(), group.getName());
user.setPermission(NodeFactory.buildGroupNode(group.getName()).build()); user.setPermission(node);
} }
user.getPrimaryGroup().setStoredValue(group.getName()); user.getPrimaryGroup().setStoredValue(group.getName());

View File

@ -54,7 +54,7 @@ public class LogNotify extends SubCommand<Log> {
return false; return false;
} }
Optional<Node> ret = user.getOwnNodes().stream() Optional<? extends Node> ret = user.getOwnNodes().stream()
.filter(n -> n.getPermission().equalsIgnoreCase("luckperms.log.notify.ignoring")) .filter(n -> n.getPermission().equalsIgnoreCase("luckperms.log.notify.ignoring"))
.findFirst(); .findFirst();

View File

@ -170,17 +170,17 @@ public final class EventFactory {
fireEventAsync(event); fireEventAsync(event);
} }
public void handleNodeAdd(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) { public void handleNodeAdd(Node node, PermissionHolder target, Collection<? extends Node> before, Collection<? extends Node> after) {
EventNodeAdd event = new EventNodeAdd(node, getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); EventNodeAdd event = new EventNodeAdd(node, getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEventAsync(event); fireEventAsync(event);
} }
public void handleNodeClear(PermissionHolder target, Collection<Node> before, Collection<Node> after) { public void handleNodeClear(PermissionHolder target, Collection<? extends Node> before, Collection<? extends Node> after) {
EventNodeClear event = new EventNodeClear(getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); EventNodeClear event = new EventNodeClear(getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEventAsync(event); fireEventAsync(event);
} }
public void handleNodeRemove(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) { public void handleNodeRemove(Node node, PermissionHolder target, Collection<? extends Node> before, Collection<? extends Node> after) {
EventNodeRemove event = new EventNodeRemove(node, getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); EventNodeRemove event = new EventNodeRemove(node, getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEventAsync(event); fireEventAsync(event);
} }

View File

@ -82,7 +82,7 @@ public class InheritanceHandler {
@Override @Override
public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) { public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) {
Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator()); Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator());
List<Node> nodes = holder.getOwnGroupNodes(); List<? extends Node> nodes = holder.getOwnGroupNodes();
for (Node n : nodes) { for (Node n : nodes) {
Group g = this.plugin.getGroupManager().getIfLoaded(n.getGroupName()); Group g = this.plugin.getGroupManager().getIfLoaded(n.getGroupName());
if (g != null) { if (g != null) {
@ -109,7 +109,7 @@ public class InheritanceHandler {
@Override @Override
public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) { public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) {
Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator()); Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator());
List<Node> nodes = holder.getOwnGroupNodes(this.context.getContexts()); List<? extends Node> nodes = holder.getOwnGroupNodes(this.context.getContexts());
for (Node n : nodes) { for (Node n : nodes) {
// effectively: if not (we're applying global groups or it's specific anyways) // effectively: if not (we're applying global groups or it's specific anyways)
if (!((this.context.hasSetting(LookupSetting.APPLY_PARENTS_SET_WITHOUT_SERVER) || n.isServerSpecific()) && (this.context.hasSetting(LookupSetting.APPLY_PARENTS_SET_WITHOUT_WORLD) || n.isWorldSpecific()))) { if (!((this.context.hasSetting(LookupSetting.APPLY_PARENTS_SET_WITHOUT_SERVER) || n.isServerSpecific()) && (this.context.hasSetting(LookupSetting.APPLY_PARENTS_SET_WITHOUT_WORLD) || n.isWorldSpecific()))) {

View File

@ -25,9 +25,10 @@
package me.lucko.luckperms.common.model; package me.lucko.luckperms.common.model;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder; import com.google.common.collect.Multimaps;
import com.google.common.collect.SortedSetMultimap; import com.google.common.collect.SortedSetMultimap;
import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.LocalizedNode;
@ -50,7 +51,8 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Predicate; import java.util.function.Predicate;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -66,6 +68,8 @@ import javax.annotation.Nullable;
* <p>Each holder has two of these maps, one for enduring and transient nodes.</p> * <p>Each holder has two of these maps, one for enduring and transient nodes.</p>
*/ */
public final class NodeMap { public final class NodeMap {
@SuppressWarnings("Guava")
private static final Supplier<SortedSet<LocalizedNode>> VALUE_SET_SUPPLIER = () -> new ConcurrentSkipListSet<>(NodeComparator.reverse());
/** /**
* The holder which this map is for * The holder which this map is for
@ -80,24 +84,19 @@ public final class NodeMap {
* key, and finally by the overall size of the set. Nodes are ordered according to the priority rules * key, and finally by the overall size of the set. Nodes are ordered according to the priority rules
* defined in {@link NodeComparator}.</p> * defined in {@link NodeComparator}.</p>
*/ */
private final SortedSetMultimap<ImmutableContextSet, Node> map = MultimapBuilder private final SortedSetMultimap<ImmutableContextSet, LocalizedNode> map = Multimaps.newSortedSetMultimap(
.treeKeys(ContextSetComparator.reverse()) new ConcurrentSkipListMap<>(ContextSetComparator.reverse()),
.treeSetValues(NodeComparator.reverse()) VALUE_SET_SUPPLIER
.build(); );
/** /**
* Copy of {@link #map} which only contains group nodes * Copy of {@link #map} which only contains group nodes
* @see Node#isGroupNode() * @see Node#isGroupNode()
*/ */
private final SortedSetMultimap<ImmutableContextSet, Node> inheritanceMap = MultimapBuilder private final SortedSetMultimap<ImmutableContextSet, LocalizedNode> inheritanceMap = Multimaps.newSortedSetMultimap(
.treeKeys(ContextSetComparator.reverse()) new ConcurrentSkipListMap<>(ContextSetComparator.reverse()),
.treeSetValues(NodeComparator.reverse()) VALUE_SET_SUPPLIER
.build(); );
/**
* The lock which synchronizes the instance
*/
private final ReentrantLock lock = new ReentrantLock();
/** /**
* A cache which holds an immutable copy of the backing map * A cache which holds an immutable copy of the backing map
@ -108,83 +107,42 @@ public final class NodeMap {
this.holder = holder; this.holder = holder;
} }
public List<Node> asList() { public List<LocalizedNode> asList() {
this.lock.lock();
try {
return new ArrayList<>(this.map.values()); return new ArrayList<>(this.map.values());
} finally {
this.lock.unlock();
}
} }
public LinkedHashSet<Node> asSet() { public LinkedHashSet<LocalizedNode> asSet() {
this.lock.lock();
try {
return new LinkedHashSet<>(this.map.values()); return new LinkedHashSet<>(this.map.values());
} finally {
this.lock.unlock();
}
} }
public SortedSet<LocalizedNode> asSortedSet() { public SortedSet<LocalizedNode> asSortedSet() {
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse()); SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
copyToLocalized(ret); copyTo(ret);
return ret; return ret;
} }
public void copyTo(Collection<? super Node> collection) { public void copyTo(Collection<? super LocalizedNode> collection) {
this.lock.lock();
try {
collection.addAll(this.map.values()); collection.addAll(this.map.values());
} finally {
this.lock.unlock();
}
} }
public void copyTo(Collection<? super Node> collection, ContextSet filter) { public void copyTo(Collection<? super LocalizedNode> collection, ContextSet filter) {
this.lock.lock(); for (Map.Entry<ImmutableContextSet, Collection<LocalizedNode>> e : this.map.asMap().entrySet()) {
try {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.map.asMap().entrySet()) {
if (e.getKey().isSatisfiedBy(filter)) { if (e.getKey().isSatisfiedBy(filter)) {
collection.addAll(e.getValue()); collection.addAll(e.getValue());
} }
} }
} finally {
this.lock.unlock();
}
} }
public void copyGroupNodesTo(Collection<? super Node> collection) { public void copyGroupNodesTo(Collection<? super LocalizedNode> collection) {
this.lock.lock();
try {
collection.addAll(this.inheritanceMap.values()); collection.addAll(this.inheritanceMap.values());
} finally {
this.lock.unlock();
}
} }
public void copyGroupNodesTo(Collection<? super Node> collection, ContextSet filter) { public void copyGroupNodesTo(Collection<? super LocalizedNode> collection, ContextSet filter) {
this.lock.lock(); for (Map.Entry<ImmutableContextSet, Collection<LocalizedNode>> e : this.inheritanceMap.asMap().entrySet()) {
try {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.inheritanceMap.asMap().entrySet()) {
if (e.getKey().isSatisfiedBy(filter)) { if (e.getKey().isSatisfiedBy(filter)) {
collection.addAll(e.getValue()); 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();
}
} }
/** /**
@ -192,7 +150,7 @@ public final class NodeMap {
* *
* @return an immutable copy * @return an immutable copy
*/ */
public ImmutableSetMultimap<ImmutableContextSet, Node> immutable() { public ImmutableSetMultimap<ImmutableContextSet, LocalizedNode> immutable() {
return this.cache.get(); return this.cache.get();
} }
@ -203,140 +161,90 @@ public final class NodeMap {
this.cache.invalidate(); this.cache.invalidate();
} }
void add(Node node) { private LocalizedNode localise(Node node) {
this.lock.lock(); if (node instanceof LocalizedNode) {
try { LocalizedNode localizedNode = (LocalizedNode) node;
ImmutableContextSet context = node.getFullContexts().makeImmutable(); if (this.holder.getObjectName().equals(localizedNode.getLocation())) {
this.map.put(context, node); return localizedNode;
if (node.isGroupNode() && node.getValue()) {
this.inheritanceMap.put(context, node);
} }
} finally { }
this.lock.unlock();
// localise
return ImmutableLocalizedNode.of(node, this.holder.getObjectName());
}
void add(Node node) {
ImmutableContextSet context = node.getFullContexts().makeImmutable();
LocalizedNode n = localise(node);
this.map.put(context, n);
if (node.isGroupNode() && node.getValue()) {
this.inheritanceMap.put(context, n);
} }
} }
void remove(Node node) { void remove(Node node) {
this.lock.lock();
try {
ImmutableContextSet context = node.getFullContexts().makeImmutable(); ImmutableContextSet context = node.getFullContexts().makeImmutable();
this.map.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); this.map.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE));
if (node.isGroupNode()) { if (node.isGroupNode()) {
this.inheritanceMap.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE)); this.inheritanceMap.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE));
} }
} finally {
this.lock.unlock();
}
} }
private void removeExact(Node node) { private void removeExact(Node node) {
this.lock.lock();
try {
ImmutableContextSet context = node.getFullContexts().makeImmutable(); ImmutableContextSet context = node.getFullContexts().makeImmutable();
this.map.remove(context, node); this.map.remove(context, node);
if (node.isGroupNode() && node.getValue()) { if (node.isGroupNode() && node.getValue()) {
this.inheritanceMap.remove(context, node); this.inheritanceMap.remove(context, node);
} }
} finally {
this.lock.unlock();
}
} }
void replace(Node node, Node previous) { void replace(Node node, Node previous) {
this.lock.lock();
try {
removeExact(previous); removeExact(previous);
add(node); add(node);
} finally {
this.lock.unlock();
}
} }
void clear() { void clear() {
this.lock.lock();
try {
this.map.clear(); this.map.clear();
this.inheritanceMap.clear(); this.inheritanceMap.clear();
} finally {
this.lock.unlock();
}
} }
void clear(ContextSet contextSet) { void clear(ContextSet contextSet) {
this.lock.lock();
try {
ImmutableContextSet context = contextSet.makeImmutable(); ImmutableContextSet context = contextSet.makeImmutable();
this.map.removeAll(context); this.map.removeAll(context);
this.inheritanceMap.removeAll(context); this.inheritanceMap.removeAll(context);
} finally {
this.lock.unlock();
}
} }
void setContent(Set<Node> set) { void setContent(Collection<? extends Node> set) {
this.lock.lock();
try {
this.map.clear(); this.map.clear();
this.inheritanceMap.clear(); this.inheritanceMap.clear();
for (Node n : set) { for (Node n : set) {
add(n); add(n);
} }
} finally {
this.lock.unlock();
}
} }
void setContent(Multimap<ImmutableContextSet, Node> multimap) { void setContent(Multimap<ImmutableContextSet, ? extends Node> multimap) {
this.lock.lock(); setContent(multimap.values());
try {
this.map.clear();
this.inheritanceMap.clear();
this.map.putAll(multimap);
for (Map.Entry<ImmutableContextSet, Node> entry : this.map.entries()) {
if (entry.getValue().isGroupNode() && entry.getValue().getValue()) {
this.inheritanceMap.put(entry.getKey(), entry.getValue());
}
}
} finally {
this.lock.unlock();
}
} }
boolean removeIf(Predicate<? super Node> predicate) { boolean removeIf(Predicate<? super Node> predicate) {
this.lock.lock();
try {
boolean ret = this.map.values().removeIf(predicate); boolean ret = this.map.values().removeIf(predicate);
if (ret) {
this.inheritanceMap.values().removeIf(predicate); this.inheritanceMap.values().removeIf(predicate);
}
return ret; return ret;
} finally {
this.lock.unlock();
}
} }
boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate) { boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate) {
this.lock.lock();
try {
ImmutableContextSet context = contextSet.makeImmutable(); ImmutableContextSet context = contextSet.makeImmutable();
SortedSet<Node> nodes = this.map.get(context); SortedSet<LocalizedNode> nodes = this.map.get(context);
boolean ret = nodes.removeIf(predicate); boolean ret = nodes.removeIf(predicate);
if (ret) {
this.inheritanceMap.get(context).removeIf(predicate); this.inheritanceMap.get(context).removeIf(predicate);
}
return ret; return ret;
} finally {
this.lock.unlock();
}
} }
boolean auditTemporaryNodes(@Nullable Set<Node> removed) { boolean auditTemporaryNodes(@Nullable Set<Node> removed) {
boolean work = false; boolean work = false;
this.lock.lock(); Iterator<? extends Node> it = this.map.values().iterator();
try {
Iterator<Node> it = this.map.values().iterator();
while (it.hasNext()) { while (it.hasNext()) {
Node entry = it.next(); Node entry = it.next();
if (entry.hasExpired()) { if (entry.hasExpired()) {
@ -350,14 +258,11 @@ public final class NodeMap {
it.remove(); it.remove();
} }
} }
} finally {
this.lock.unlock();
}
return work; return work;
} }
private static final class NodeMapCache extends Cache<ImmutableSetMultimap<ImmutableContextSet, Node>> { private static final class NodeMapCache extends Cache<ImmutableSetMultimap<ImmutableContextSet, LocalizedNode>> {
private final NodeMap handle; private final NodeMap handle;
private NodeMapCache(NodeMap handle) { private NodeMapCache(NodeMap handle) {
@ -366,13 +271,8 @@ public final class NodeMap {
@Nonnull @Nonnull
@Override @Override
protected ImmutableSetMultimap<ImmutableContextSet, Node> supply() { protected ImmutableSetMultimap<ImmutableContextSet, LocalizedNode> supply() {
this.handle.lock.lock();
try {
return ImmutableSetMultimap.copyOf(this.handle.map); return ImmutableSetMultimap.copyOf(this.handle.map);
} finally {
this.handle.lock.unlock();
}
} }
} }

View File

@ -46,8 +46,6 @@ import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.inheritance.InheritanceComparator; import me.lucko.luckperms.common.inheritance.InheritanceComparator;
import me.lucko.luckperms.common.inheritance.InheritanceGraph; import me.lucko.luckperms.common.inheritance.InheritanceGraph;
import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator; import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator;
import me.lucko.luckperms.common.node.factory.NodeFactory;
import me.lucko.luckperms.common.node.model.ImmutableLocalizedNode;
import me.lucko.luckperms.common.node.utils.InheritanceInfo; import me.lucko.luckperms.common.node.utils.InheritanceInfo;
import me.lucko.luckperms.common.node.utils.MetaType; import me.lucko.luckperms.common.node.utils.MetaType;
import me.lucko.luckperms.common.node.utils.NodeTools; import me.lucko.luckperms.common.node.utils.NodeTools;
@ -218,39 +216,39 @@ public abstract class PermissionHolder {
reloadCachedData(); reloadCachedData();
} }
public void setNodes(NodeMapType type, Set<Node> set) { public void setNodes(NodeMapType type, Set<? extends Node> set) {
getData(type).setContent(set); getData(type).setContent(set);
invalidateCache(); invalidateCache();
} }
public void replaceNodes(NodeMapType type, Multimap<ImmutableContextSet, Node> multimap) { public void replaceNodes(NodeMapType type, Multimap<ImmutableContextSet, ? extends Node> multimap) {
getData(type).setContent(multimap); getData(type).setContent(multimap);
invalidateCache(); invalidateCache();
} }
public List<Node> getOwnNodes() { public List<LocalizedNode> getOwnNodes() {
List<Node> ret = new ArrayList<>(); List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyTo(ret); this.transientNodes.copyTo(ret);
this.enduringNodes.copyTo(ret); this.enduringNodes.copyTo(ret);
return ret; return ret;
} }
public List<Node> getOwnNodes(ContextSet filter) { public List<LocalizedNode> getOwnNodes(ContextSet filter) {
List<Node> ret = new ArrayList<>(); List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyTo(ret, filter); this.transientNodes.copyTo(ret, filter);
this.enduringNodes.copyTo(ret, filter); this.enduringNodes.copyTo(ret, filter);
return ret; return ret;
} }
public List<Node> getOwnGroupNodes() { public List<LocalizedNode> getOwnGroupNodes() {
List<Node> ret = new ArrayList<>(); List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyGroupNodesTo(ret); this.transientNodes.copyGroupNodesTo(ret);
this.enduringNodes.copyGroupNodesTo(ret); this.enduringNodes.copyGroupNodesTo(ret);
return ret; return ret;
} }
public List<Node> getOwnGroupNodes(ContextSet filter) { public List<LocalizedNode> getOwnGroupNodes(ContextSet filter) {
List<Node> ret = new ArrayList<>(); List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyGroupNodesTo(ret, filter); this.transientNodes.copyGroupNodesTo(ret, filter);
this.enduringNodes.copyGroupNodesTo(ret, filter); this.enduringNodes.copyGroupNodesTo(ret, filter);
return ret; return ret;
@ -258,17 +256,17 @@ public abstract class PermissionHolder {
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.transientNodes.copyTo(ret);
this.enduringNodes.copyToLocalized(ret); this.enduringNodes.copyTo(ret);
return ret; return ret;
} }
public boolean removeIf(Predicate<Node> predicate) { public boolean removeIf(Predicate<? super Node> predicate) {
return removeIf(predicate, null); return removeIf(predicate, null);
} }
public boolean removeIf(Predicate<Node> predicate, Runnable taskIfSuccess) { public boolean removeIf(Predicate<? super Node> predicate, Runnable taskIfSuccess) {
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
if (!this.enduringNodes.removeIf(predicate)) { if (!this.enduringNodes.removeIf(predicate)) {
return false; return false;
} }
@ -276,18 +274,18 @@ public abstract class PermissionHolder {
taskIfSuccess.run(); taskIfSuccess.run();
} }
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeClear(this, before, after); this.plugin.getEventFactory().handleNodeClear(this, before, after);
return true; return true;
} }
public boolean removeIf(ContextSet contextSet, Predicate<Node> predicate) { public boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate) {
return removeIf(contextSet, predicate, null); return removeIf(contextSet, predicate, null);
} }
public boolean removeIf(ContextSet contextSet, Predicate<Node> predicate, Runnable taskIfSuccess) { public boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate, Runnable taskIfSuccess) {
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
if (!this.enduringNodes.removeIf(contextSet, predicate)) { if (!this.enduringNodes.removeIf(contextSet, predicate)) {
return false; return false;
} }
@ -295,13 +293,13 @@ public abstract class PermissionHolder {
taskIfSuccess.run(); taskIfSuccess.run();
} }
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().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<? super Node> predicate) {
boolean result = this.transientNodes.removeIf(predicate); boolean result = this.transientNodes.removeIf(predicate);
if (result) { if (result) {
invalidateCache(); invalidateCache();
@ -309,15 +307,12 @@ public abstract class PermissionHolder {
return result; return result;
} }
public void accumulateInheritancesTo(List<LocalizedNode> accumulator, Contexts context) { public void accumulateInheritancesTo(List<? super LocalizedNode> accumulator, Contexts context) {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context); InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context);
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this); Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) { for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes(context.getContexts()); List<? extends LocalizedNode> nodes = holder.getOwnNodes(context.getContexts());
for (Node node : nodes) { accumulator.addAll(nodes);
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, holder.getObjectName());
accumulator.add(localizedNode);
}
} }
} }
@ -327,15 +322,12 @@ public abstract class PermissionHolder {
return accumulator; return accumulator;
} }
public void accumulateInheritancesTo(List<LocalizedNode> accumulator) { public void accumulateInheritancesTo(List<? super LocalizedNode> accumulator) {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(); InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph();
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this); Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) { for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes(); List<? extends LocalizedNode> nodes = holder.getOwnNodes();
for (Node node : nodes) { accumulator.addAll(nodes);
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, holder.getObjectName());
accumulator.add(localizedNode);
}
} }
} }
@ -350,10 +342,7 @@ public abstract class PermissionHolder {
if (context.hasSetting(LookupSetting.RESOLVE_INHERITANCE)) { if (context.hasSetting(LookupSetting.RESOLVE_INHERITANCE)) {
accumulateInheritancesTo(entries, context); accumulateInheritancesTo(entries, context);
} else { } else {
for (Node n : getOwnNodes(context.getContexts())) { entries.addAll(getOwnNodes(context.getContexts()));
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
entries.add(localizedNode);
}
} }
if (!context.hasSetting(LookupSetting.INCLUDE_NODES_SET_WITHOUT_SERVER)) { if (!context.hasSetting(LookupSetting.INCLUDE_NODES_SET_WITHOUT_SERVER)) {
@ -366,42 +355,34 @@ public abstract class PermissionHolder {
return entries; return entries;
} }
public Map<String, Boolean> exportNodesAndShorthand(Contexts context, boolean lowerCase) { public Map<String, Boolean> exportPermissions(Contexts context, boolean convertToLowercase, boolean resolveShorthand) {
List<LocalizedNode> entries = getAllEntries(context); List<LocalizedNode> entries = getAllEntries(context);
return processExportedPermissions(entries, convertToLowercase, resolveShorthand);
Map<String, Boolean> perms = new HashMap<>();
boolean applyShorthand = this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND);
for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission();
if (perms.putIfAbsent(perm, node.getValue()) == null) {
if (applyShorthand) {
List<String> shorthand = node.resolveShorthand();
if (!shorthand.isEmpty()) {
for (String s : shorthand) {
perms.putIfAbsent(lowerCase ? s.toLowerCase() : s, node.getValue());
}
}
}
}
} }
return ImmutableMap.copyOf(perms); public Map<String, Boolean> exportPermissions(boolean convertToLowercase, boolean resolveShorthand) {
}
public Map<String, Boolean> exportNodesAndShorthand(boolean lowerCase) {
List<LocalizedNode> entries = resolveInheritances(); List<LocalizedNode> entries = resolveInheritances();
return processExportedPermissions(entries, convertToLowercase, resolveShorthand);
}
Map<String, Boolean> perms = new HashMap<>(); private static ImmutableMap<String, Boolean> processExportedPermissions(List<LocalizedNode> entries, boolean convertToLowercase, boolean resolveShorthand) {
boolean applyShorthand = this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND); Map<String, Boolean> perms = new HashMap<>(entries.size());
for (Node node : entries) { for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase().intern() : node.getPermission(); if (convertToLowercase) {
perms.putIfAbsent(node.getPermission().toLowerCase(), node.getValue());
} else {
perms.putIfAbsent(node.getPermission(), node.getValue());
}
}
if (perms.putIfAbsent(perm, node.getValue()) == null && applyShorthand) { if (resolveShorthand) {
for (Node node : entries) {
List<String> shorthand = node.resolveShorthand(); List<String> shorthand = node.resolveShorthand();
if (!shorthand.isEmpty()) {
for (String s : shorthand) { for (String s : shorthand) {
perms.putIfAbsent((lowerCase ? s.toLowerCase() : s).intern(), node.getValue()); if (convertToLowercase) {
perms.putIfAbsent(s.toLowerCase(), node.getValue());
} else {
perms.putIfAbsent(s, node.getValue());
} }
} }
} }
@ -418,8 +399,8 @@ public abstract class PermissionHolder {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context); InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context);
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this); Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) { for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes(context.getContexts()); List<? extends LocalizedNode> nodes = holder.getOwnNodes(context.getContexts());
for (Node node : nodes) { for (LocalizedNode node : nodes) {
if (!node.getValue()) continue; if (!node.getValue()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue; if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
@ -427,7 +408,7 @@ public abstract class PermissionHolder {
continue; continue;
} }
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, holder.getObjectName())); accumulator.accumulateNode(node);
} }
OptionalInt w = holder.getWeight(); OptionalInt w = holder.getWeight();
@ -447,12 +428,12 @@ public abstract class PermissionHolder {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(); InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph();
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this); Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) { for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes(); List<? extends LocalizedNode> nodes = holder.getOwnNodes();
for (Node node : nodes) { for (LocalizedNode node : nodes) {
if (!node.getValue()) continue; if (!node.getValue()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue; if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, holder.getObjectName())); accumulator.accumulateNode(node);
} }
OptionalInt w = getWeight(); OptionalInt w = getWeight();
@ -474,7 +455,7 @@ public abstract class PermissionHolder {
// we don't call events for transient nodes // we don't call events for transient nodes
boolean transientWork = this.transientNodes.auditTemporaryNodes(null); boolean transientWork = this.transientNodes.auditTemporaryNodes(null);
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
Set<Node> removed = new HashSet<>(); Set<Node> removed = new HashSet<>();
boolean enduringWork = this.enduringNodes.auditTemporaryNodes(removed); boolean enduringWork = this.enduringNodes.auditTemporaryNodes(removed);
@ -483,7 +464,7 @@ public abstract class PermissionHolder {
invalidateCache(); invalidateCache();
// call event // call event
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().values();
for (Node r : removed) { for (Node r : removed) {
this.plugin.getEventFactory().handleNodeRemove(r, this, before, after); this.plugin.getEventFactory().handleNodeRemove(r, this, before, after);
} }
@ -496,8 +477,8 @@ public abstract class PermissionHolder {
return transientWork || enduringWork; return transientWork || enduringWork;
} }
private Optional<Node> searchForMatch(NodeMapType type, Node node, NodeEqualityPredicate equalityPredicate) { private Optional<LocalizedNode> searchForMatch(NodeMapType type, Node node, NodeEqualityPredicate equalityPredicate) {
for (Node n : getData(type).immutable().values()) { for (LocalizedNode n : getData(type).immutable().values()) {
if (n.equals(node, equalityPredicate)) { if (n.equals(node, equalityPredicate)) {
return Optional.of(n); return Optional.of(n);
} }
@ -559,10 +540,10 @@ public abstract class PermissionHolder {
return DataMutateResult.ALREADY_HAS; return DataMutateResult.ALREADY_HAS;
} }
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.add(node); this.enduringNodes.add(node);
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after); this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
return DataMutateResult.SUCCESS; return DataMutateResult.SUCCESS;
@ -579,7 +560,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 = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE); Optional<? extends Node> existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
// An existing node was found // An existing node was found
if (existing.isPresent()) { if (existing.isPresent()) {
@ -589,10 +570,10 @@ public abstract class PermissionHolder {
Node newNode = node.toBuilder().setExpiry(previous.getExpiryUnixTime() + node.getSecondsTilExpiry()).build(); Node newNode = node.toBuilder().setExpiry(previous.getExpiryUnixTime() + node.getSecondsTilExpiry()).build();
// Remove the old node & add the new one. // Remove the old node & add the new one.
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.replace(newNode, previous); this.enduringNodes.replace(newNode, previous);
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().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);
@ -600,7 +581,7 @@ public abstract class PermissionHolder {
} 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 = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE); Optional<? extends Node> existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
// An existing node was found // An existing node was found
if (existing.isPresent()) { if (existing.isPresent()) {
@ -609,10 +590,10 @@ public abstract class PermissionHolder {
// Only replace if the new expiry time is greater than the old one. // Only replace if the new expiry time is greater than the old one.
if (node.getExpiryUnixTime() > previous.getExpiryUnixTime()) { if (node.getExpiryUnixTime() > previous.getExpiryUnixTime()) {
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.replace(node, previous); this.enduringNodes.replace(node, previous);
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().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);
@ -652,10 +633,10 @@ public abstract class PermissionHolder {
return DataMutateResult.LACKS; return DataMutateResult.LACKS;
} }
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.remove(node); this.enduringNodes.remove(node);
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeRemove(node, this, before, after); this.plugin.getEventFactory().handleNodeRemove(node, this, before, after);
return DataMutateResult.SUCCESS; return DataMutateResult.SUCCESS;
@ -676,22 +657,14 @@ public abstract class PermissionHolder {
return DataMutateResult.SUCCESS; return DataMutateResult.SUCCESS;
} }
public boolean inheritsGroup(Group group) {
return group.getName().equalsIgnoreCase(this.getObjectName()) || hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(group.getName()).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
}
public boolean inheritsGroup(Group group, ContextSet contextSet) {
return group.getName().equalsIgnoreCase(this.getObjectName()) || hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(group.getName()).withExtraContext(contextSet).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
}
/** /**
* Clear all of the holders permission nodes * Clear all of the holders permission nodes
*/ */
public boolean clearNodes() { public boolean clearNodes() {
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.clear(); this.enduringNodes.clear();
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().values();
if (before.size() == after.size()) { if (before.size() == after.size()) {
return false; return false;
@ -702,10 +675,10 @@ public abstract class PermissionHolder {
} }
public boolean clearNodes(ContextSet contextSet) { public boolean clearNodes(ContextSet contextSet) {
ImmutableCollection<Node> before = enduringData().immutable().values(); ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.clear(contextSet); this.enduringNodes.clear(contextSet);
invalidateCache(); invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values(); ImmutableCollection<? extends Node> after = enduringData().immutable().values();
if (before.size() == after.size()) { if (before.size() == after.size()) {
return false; return false;

View File

@ -39,6 +39,12 @@ public final class ImmutableLocalizedNode extends ForwardingNode implements Loca
public static ImmutableLocalizedNode of(Node node, String location) { public static ImmutableLocalizedNode of(Node node, String location) {
Objects.requireNonNull(node, "node"); Objects.requireNonNull(node, "node");
Objects.requireNonNull(location, "location"); Objects.requireNonNull(location, "location");
// unwrap
while (node instanceof LocalizedNode) {
node = ((LocalizedNode) node).getNode();
}
return new ImmutableLocalizedNode(node, location); return new ImmutableLocalizedNode(node, location);
} }

View File

@ -72,7 +72,7 @@ public class HolderSubjectData implements LPSubjectData {
this.parentSubject = parentSubject; this.parentSubject = parentSubject;
} }
private Stream<Node> streamNodes() { private Stream<? extends Node> streamNodes() {
return this.holder.getData(this.type).immutable().values().stream(); return this.holder.getData(this.type).immutable().values().stream();
} }
@ -94,7 +94,7 @@ public class HolderSubjectData implements LPSubjectData {
@Override @Override
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() { public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() {
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> ret = ImmutableMap.builder(); ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> ret = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Collection<Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) { for (Map.Entry<ImmutableContextSet, ? extends Collection<? extends Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) {
ImmutableMap.Builder<String, Boolean> builder = ImmutableMap.builder(); ImmutableMap.Builder<String, Boolean> builder = ImmutableMap.builder();
for (Node n : entry.getValue()) { for (Node n : entry.getValue()) {
builder.put(n.getPermission(), n.getValue()); builder.put(n.getPermission(), n.getValue());
@ -183,7 +183,7 @@ public class HolderSubjectData implements LPSubjectData {
@Override @Override
public ImmutableMap<ImmutableContextSet, ImmutableList<LPSubjectReference>> getAllParents() { public ImmutableMap<ImmutableContextSet, ImmutableList<LPSubjectReference>> getAllParents() {
ImmutableMap.Builder<ImmutableContextSet, ImmutableList<LPSubjectReference>> ret = ImmutableMap.builder(); ImmutableMap.Builder<ImmutableContextSet, ImmutableList<LPSubjectReference>> ret = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Collection<Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) { for (Map.Entry<ImmutableContextSet, ? extends Collection<? extends Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) {
ImmutableList.Builder<LPSubjectReference> builder = ImmutableList.builder(); ImmutableList.Builder<LPSubjectReference> builder = ImmutableList.builder();
for (Node n : entry.getValue()) { for (Node n : entry.getValue()) {
if (n.isGroupNode()) { if (n.isGroupNode()) {