Add getInheritedGroups and getNodes filtered by type methods to the API (#1926)

This commit is contained in:
Luck 2020-05-10 01:19:43 +01:00
parent 7845d89f10
commit 52731fe68e
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
4 changed files with 283 additions and 69 deletions

View File

@ -31,13 +31,17 @@ import net.luckperms.api.model.data.NodeMap;
import net.luckperms.api.model.group.Group; import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User; import net.luckperms.api.model.user.User;
import net.luckperms.api.node.Node; import net.luckperms.api.node.Node;
import net.luckperms.api.node.NodeType;
import net.luckperms.api.query.QueryOptions; import net.luckperms.api.query.QueryOptions;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
/** /**
* Generic superinterface for an object which holds permissions. * Generic superinterface for an object which holds permissions.
@ -121,7 +125,7 @@ public interface PermissionHolder {
* @param dataType the data type * @param dataType the data type
* @return the data * @return the data
*/ */
NodeMap getData(@NonNull DataType dataType); @NonNull NodeMap getData(@NonNull DataType dataType);
/** /**
* Gets the holders {@link DataType#NORMAL} data. * Gets the holders {@link DataType#NORMAL} data.
@ -149,35 +153,60 @@ public interface PermissionHolder {
@NonNull NodeMap transientData(); @NonNull NodeMap transientData();
/** /**
* Gets a flattened/squashed view of the holders permissions. * Gets a flattened view of the holders own {@link Node}s.
* *
* <p>This list is constructed using the values * <p>This list is constructed using the values of both the {@link #data() normal}
* of both the transient and enduring backing multimaps.</p> * and {@link #transientData() transient} backing node maps.</p>
* *
* <p>This means that it <b>may contain</b> duplicate entries.</p> * <p>It <b>may contain</b> duplicate entries if the same node is added to both the normal
* * and transient node maps. You can use {@link #getDistinctNodes()} for a view without
* <p>Use {@link #getDistinctNodes()} for a view without duplicates.</p> * duplicates.</p>
* *
* <p>This method <b>does not</b> resolve inheritance rules.</p> * <p>This method <b>does not</b> resolve inheritance rules.</p>
* *
* @return a list of the holders own nodes. * @return a collection of the holders own nodes.
*/ */
@NonNull Collection<Node> getNodes(); default @NonNull Collection<Node> getNodes() {
/* This default method is overridden in the implementation, and is just here
to demonstrate what this method does in the API sources. */
List<Node> nodes = new ArrayList<>();
nodes.addAll(data().toCollection());
nodes.addAll(transientData().toCollection());
return nodes;
}
/** /**
* Gets a sorted set of all held nodes. * Gets a flattened view of the holders own {@link Node}s of the given {@code type}.
*
* @param type the type of node to filter by
* @param <T> the node type
* @return a filtered collection of the holders own nodes
* @see #getNodes()
* @since 5.1
*/
default <T extends Node> @NonNull Collection<T> getNodes(@NonNull NodeType<T> type) {
/* This default method is overridden in the implementation, and is just here
to demonstrate what this method does in the API sources. */
return getNodes().stream()
.filter(type::matches)
.map(type::cast)
.collect(Collectors.toList());
}
/**
* Gets a flattened and sorted view of the holders own distinct {@link Node}s.
* *
* <p>Effectively a sorted version of {@link #getNodes()}, without duplicates. Use the * <p>Effectively a sorted version of {@link #getNodes()}, without duplicates. Use the
* aforementioned method if you don't require either of these attributes.</p> * aforementioned method if you don't require either of these attributes.</p>
* *
* <p>This method <b>does not</b> resolve inheritance rules.</p> * <p>This method <b>does not</b> resolve inheritance rules.</p>
* *
* @return an immutable set of permissions in priority order * @return a sorted set of the holders own distinct nodes
*/ */
@NonNull SortedSet<Node> getDistinctNodes(); @NonNull SortedSet<Node> getDistinctNodes();
/** /**
* Recursively resolves this holders permissions. * Gets a resolved view of the holders own and inherited {@link Node}s.
* *
* <p>The returned list will contain every inherited * <p>The returned list will contain every inherited
* node the holder has, in the order that they were inherited in.</p> * node the holder has, in the order that they were inherited in.</p>
@ -189,22 +218,62 @@ public interface PermissionHolder {
* with the entries from the end of the inheritance tree appearing last.</p> * with the entries from the end of the inheritance tree appearing last.</p>
* *
* @param queryOptions the query options * @param queryOptions the query options
* @return a list of nodes * @return a list of the holders inherited nodes
*/ */
@NonNull Collection<Node> resolveInheritedNodes(@NonNull QueryOptions queryOptions); @NonNull Collection<Node> resolveInheritedNodes(@NonNull QueryOptions queryOptions);
/** /**
* Gets a mutable sorted set of the nodes that this object has and inherits, filtered by context * Gets a resolved view of the holders own and inherited {@link Node}s of a given {@code type}.
* *
* <p>Nodes are sorted into priority order. The order of inheritance is only important during * @param type the type of node to filter by
* the process of flattening inherited entries.</p> * @param queryOptions the query options
* @param <T> the node type
* @return a filtered list of the holders inherited nodes
* @see #resolveInheritedNodes(QueryOptions)
* @since 5.1
*/
default <T extends Node> @NonNull Collection<T> resolveInheritedNodes(@NonNull NodeType<T> type, @NonNull QueryOptions queryOptions) {
/* This default method is overridden in the implementation, and is just here
to demonstrate what this method does in the API sources. */
return resolveInheritedNodes(queryOptions).stream()
.filter(type::matches)
.map(type::cast)
.collect(Collectors.toList());
}
/**
* Gets a resolved and sorted view of the holders own and inherited distinct {@link Node}s.
*
* <p>Effectively a sorted version of {@link #resolveInheritedNodes(QueryOptions)},
* without duplicates. Use the aforementioned method if you don't require either of these
* attributes.</p>
*
* <p>Inheritance is performed according to the platforms rules, and the order will vary
* depending on the accumulation order. By default, the holders own nodes are first in the list,
* with the entries from the end of the inheritance tree appearing last.</p>
* *
* @param queryOptions the query options * @param queryOptions the query options
* @return an immutable sorted set of permissions * @return a sorted set of the holders distinct inherited nodes
* @throws NullPointerException if the context is null
*/ */
@NonNull SortedSet<Node> resolveDistinctInheritedNodes(@NonNull QueryOptions queryOptions); @NonNull SortedSet<Node> resolveDistinctInheritedNodes(@NonNull QueryOptions queryOptions);
/**
* Gets a collection of the {@link Group}s this holder inherits nodes from, both directly
* and indirectly (through directly inherited groups).
*
* <p>It effectively resolves the whole "inheritance tree".</p>
*
* <p>The collection will be ordered according to the platforms inheritance rules. The groups
* which are inherited from first will appear earlier in the list.</p>
*
* <p>The list will not contain the holder.</p>
*
* @param queryOptions the query options
* @return a collection of the groups the holder inherits from
* @since 5.1
*/
@NonNull Collection<Group> getInheritedGroups(@NonNull QueryOptions queryOptions);
/** /**
* Removes any temporary permissions that have expired. * Removes any temporary permissions that have expired.
* *

View File

@ -27,6 +27,7 @@ package me.lucko.luckperms.common.api.implementation;
import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.query.QueryOptionsImpl;
import me.lucko.luckperms.common.util.ImmutableCollectors;
import net.luckperms.api.cacheddata.CachedDataManager; import net.luckperms.api.cacheddata.CachedDataManager;
import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ContextSet;
@ -35,8 +36,10 @@ import net.luckperms.api.model.data.DataMutateResult;
import net.luckperms.api.model.data.DataType; import net.luckperms.api.model.data.DataType;
import net.luckperms.api.model.data.NodeMap; import net.luckperms.api.model.data.NodeMap;
import net.luckperms.api.model.data.TemporaryNodeMergeStrategy; import net.luckperms.api.model.data.TemporaryNodeMergeStrategy;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.node.Node; import net.luckperms.api.node.Node;
import net.luckperms.api.node.NodeEqualityPredicate; import net.luckperms.api.node.NodeEqualityPredicate;
import net.luckperms.api.node.NodeType;
import net.luckperms.api.query.QueryOptions; import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.util.Tristate; import net.luckperms.api.util.Tristate;
@ -88,7 +91,7 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
} }
@Override @Override
public NodeMap getData(@NonNull DataType dataType) { public @NonNull NodeMap getData(@NonNull DataType dataType) {
switch (dataType) { switch (dataType) {
case NORMAL: case NORMAL:
return this.normalData; return this.normalData;
@ -114,6 +117,12 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
return this.handle.getOwnNodes(QueryOptionsImpl.DEFAULT_NON_CONTEXTUAL); return this.handle.getOwnNodes(QueryOptionsImpl.DEFAULT_NON_CONTEXTUAL);
} }
@Override
public @NonNull <T extends Node> Collection<T> getNodes(@NonNull NodeType<T> type) {
Objects.requireNonNull(type, "type");
return this.handle.getOwnNodes(type, QueryOptionsImpl.DEFAULT_NON_CONTEXTUAL);
}
@Override @Override
public @NonNull SortedSet<Node> getDistinctNodes() { public @NonNull SortedSet<Node> getDistinctNodes() {
return this.handle.getOwnNodesSorted(QueryOptionsImpl.DEFAULT_NON_CONTEXTUAL); return this.handle.getOwnNodesSorted(QueryOptionsImpl.DEFAULT_NON_CONTEXTUAL);
@ -121,14 +130,31 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
@Override @Override
public @NonNull List<Node> resolveInheritedNodes(@NonNull QueryOptions queryOptions) { public @NonNull List<Node> resolveInheritedNodes(@NonNull QueryOptions queryOptions) {
Objects.requireNonNull(queryOptions, "queryOptions");
return this.handle.resolveInheritedNodes(queryOptions); return this.handle.resolveInheritedNodes(queryOptions);
} }
@Override
public @NonNull <T extends Node> Collection<T> resolveInheritedNodes(@NonNull NodeType<T> type, @NonNull QueryOptions queryOptions) {
Objects.requireNonNull(type, "type");
Objects.requireNonNull(queryOptions, "queryOptions");
return this.handle.resolveInheritedNodes(type, queryOptions);
}
@Override @Override
public @NonNull SortedSet<Node> resolveDistinctInheritedNodes(@NonNull QueryOptions queryOptions) { public @NonNull SortedSet<Node> resolveDistinctInheritedNodes(@NonNull QueryOptions queryOptions) {
Objects.requireNonNull(queryOptions, "queryOptions");
return this.handle.resolveInheritedNodesSorted(queryOptions); return this.handle.resolveInheritedNodesSorted(queryOptions);
} }
@Override
public @NonNull Collection<Group> getInheritedGroups(@NonNull QueryOptions queryOptions) {
Objects.requireNonNull(queryOptions, "queryOptions");
return this.handle.resolveInheritanceTree(queryOptions).stream()
.map(me.lucko.luckperms.common.model.Group::getApiProxy)
.collect(ImmutableCollectors.toList());
}
@Override @Override
public void auditTemporaryNodes() { public void auditTemporaryNodes() {
this.handle.auditTemporaryNodes(); this.handle.auditTemporaryNodes();
@ -153,11 +179,14 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
@Override @Override
public @NonNull Tristate contains(@NonNull Node node, @NonNull NodeEqualityPredicate equalityPredicate) { public @NonNull Tristate contains(@NonNull Node node, @NonNull NodeEqualityPredicate equalityPredicate) {
Objects.requireNonNull(node, "node");
Objects.requireNonNull(equalityPredicate, "equalityPredicate");
return ApiPermissionHolder.this.handle.hasNode(this.dataType, node, equalityPredicate); return ApiPermissionHolder.this.handle.hasNode(this.dataType, node, equalityPredicate);
} }
@Override @Override
public @NonNull DataMutateResult add(@NonNull Node node) { public @NonNull DataMutateResult add(@NonNull Node node) {
Objects.requireNonNull(node, "node");
DataMutateResult result = ApiPermissionHolder.this.handle.setNode(this.dataType, node, true); DataMutateResult result = ApiPermissionHolder.this.handle.setNode(this.dataType, node, true);
if (result.wasSuccessful()) { if (result.wasSuccessful()) {
onNodeChange(); onNodeChange();
@ -167,6 +196,8 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
@Override @Override
public DataMutateResult.@NonNull WithMergedNode add(@NonNull Node node, @NonNull TemporaryNodeMergeStrategy temporaryNodeMergeStrategy) { public DataMutateResult.@NonNull WithMergedNode add(@NonNull Node node, @NonNull TemporaryNodeMergeStrategy temporaryNodeMergeStrategy) {
Objects.requireNonNull(node, "node");
Objects.requireNonNull(temporaryNodeMergeStrategy, "temporaryNodeMergeStrategy");
DataMutateResult.WithMergedNode result = ApiPermissionHolder.this.handle.setNode(this.dataType, node, temporaryNodeMergeStrategy); DataMutateResult.WithMergedNode result = ApiPermissionHolder.this.handle.setNode(this.dataType, node, temporaryNodeMergeStrategy);
if (result.getResult().wasSuccessful()) { if (result.getResult().wasSuccessful()) {
onNodeChange(); onNodeChange();
@ -176,6 +207,7 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
@Override @Override
public @NonNull DataMutateResult remove(@NonNull Node node) { public @NonNull DataMutateResult remove(@NonNull Node node) {
Objects.requireNonNull(node, "node");
DataMutateResult result = ApiPermissionHolder.this.handle.unsetNode(this.dataType, node); DataMutateResult result = ApiPermissionHolder.this.handle.unsetNode(this.dataType, node);
if (result.wasSuccessful()) { if (result.wasSuccessful()) {
onNodeChange(); onNodeChange();
@ -192,6 +224,7 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
@Override @Override
public void clear(@NonNull Predicate<? super Node> test) { public void clear(@NonNull Predicate<? super Node> test) {
Objects.requireNonNull(test, "test");
if (ApiPermissionHolder.this.handle.removeIf(this.dataType, null, test, true)) { if (ApiPermissionHolder.this.handle.removeIf(this.dataType, null, test, true)) {
onNodeChange(); onNodeChange();
} }
@ -200,6 +233,7 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
@Override @Override
public void clear(@NonNull ContextSet contextSet) { public void clear(@NonNull ContextSet contextSet) {
Objects.requireNonNull(contextSet, "contextSet");
if (ApiPermissionHolder.this.handle.clearNodes(this.dataType, contextSet, true)) { if (ApiPermissionHolder.this.handle.clearNodes(this.dataType, contextSet, true)) {
onNodeChange(); onNodeChange();
} }
@ -207,6 +241,8 @@ public class ApiPermissionHolder implements net.luckperms.api.model.PermissionHo
@Override @Override
public void clear(@NonNull ContextSet contextSet, @NonNull Predicate<? super Node> test) { public void clear(@NonNull ContextSet contextSet, @NonNull Predicate<? super Node> test) {
Objects.requireNonNull(contextSet, "contextSet");
Objects.requireNonNull(test, "test");
if (ApiPermissionHolder.this.handle.removeIf(this.dataType, contextSet, test, true)) { if (ApiPermissionHolder.this.handle.removeIf(this.dataType, contextSet, test, true)) {
onNodeChange(); onNodeChange();
} }

View File

@ -41,6 +41,7 @@ import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.node.Node; import net.luckperms.api.node.Node;
import net.luckperms.api.node.NodeEqualityPredicate; import net.luckperms.api.node.NodeEqualityPredicate;
import net.luckperms.api.node.NodeType;
import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata; import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata;
import net.luckperms.api.node.types.InheritanceNode; import net.luckperms.api.node.types.InheritanceNode;
import net.luckperms.api.query.Flag; import net.luckperms.api.query.Flag;
@ -63,6 +64,7 @@ import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -152,19 +154,69 @@ public final class NodeMap {
!flagExcludeTest(Flag.APPLY_INHERITANCE_NODES_WITHOUT_WORLD_CONTEXT, DefaultContextKeys.WORLD_KEY, filter, contextSet); !flagExcludeTest(Flag.APPLY_INHERITANCE_NODES_WITHOUT_WORLD_CONTEXT, DefaultContextKeys.WORLD_KEY, filter, contextSet);
} }
public void forEach(QueryOptions filter, Consumer<? super Node> consumer) {
for (Map.Entry<ImmutableContextSet, SortedSet<Node>> e : this.map.entrySet()) {
if (!filter.satisfies(e.getKey())) {
continue;
}
if (normalNodesExcludeTest(filter, e.getKey())) {
if (inheritanceNodesIncludeTest(filter, e.getKey())) {
// only copy inheritance nodes.
SortedSet<InheritanceNode> inheritanceNodes = this.inheritanceMap.get(e.getKey());
if (inheritanceNodes != null) {
inheritanceNodes.forEach(consumer);
}
}
} else {
e.getValue().forEach(consumer);
}
}
}
public void copyTo(Collection<? super Node> collection, QueryOptions filter) { public void copyTo(Collection<? super Node> collection, QueryOptions filter) {
for (Map.Entry<ImmutableContextSet, SortedSet<Node>> e : this.map.entrySet()) { for (Map.Entry<ImmutableContextSet, SortedSet<Node>> e : this.map.entrySet()) {
if (filter.satisfies(e.getKey())) { if (!filter.satisfies(e.getKey())) {
if (normalNodesExcludeTest(filter, e.getKey())) { continue;
if (inheritanceNodesIncludeTest(filter, e.getKey())) { }
// only copy inheritance nodes.
if (normalNodesExcludeTest(filter, e.getKey())) {
if (inheritanceNodesIncludeTest(filter, e.getKey())) {
// only copy inheritance nodes.
SortedSet<InheritanceNode> inheritanceNodes = this.inheritanceMap.get(e.getKey());
if (inheritanceNodes != null) {
collection.addAll(inheritanceNodes);
}
}
} else {
collection.addAll(e.getValue());
}
}
}
public <T extends Node> void copyTo(Collection<? super T> collection, NodeType<T> type, QueryOptions filter) {
for (Map.Entry<ImmutableContextSet, SortedSet<Node>> e : this.map.entrySet()) {
if (!filter.satisfies(e.getKey())) {
continue;
}
if (normalNodesExcludeTest(filter, e.getKey())) {
if (inheritanceNodesIncludeTest(filter, e.getKey())) {
// only copy inheritance nodes.
if (type == NodeType.INHERITANCE) {
SortedSet<InheritanceNode> inheritanceNodes = this.inheritanceMap.get(e.getKey()); SortedSet<InheritanceNode> inheritanceNodes = this.inheritanceMap.get(e.getKey());
if (inheritanceNodes != null) { if (inheritanceNodes != null) {
collection.addAll(inheritanceNodes); for (InheritanceNode node : inheritanceNodes) {
collection.add(type.cast(node));
}
} }
} }
} else { }
collection.addAll(e.getValue()); } else {
for (Node node : e.getValue()) {
if (type.matches(node)) {
collection.add(type.cast(node));
}
} }
} }
} }
@ -172,10 +224,12 @@ public final class NodeMap {
public void copyInheritanceNodesTo(Collection<? super InheritanceNode> collection, QueryOptions filter) { public void copyInheritanceNodesTo(Collection<? super InheritanceNode> collection, QueryOptions filter) {
for (Map.Entry<ImmutableContextSet, SortedSet<InheritanceNode>> e : this.inheritanceMap.entrySet()) { for (Map.Entry<ImmutableContextSet, SortedSet<InheritanceNode>> e : this.inheritanceMap.entrySet()) {
if (filter.satisfies(e.getKey())) { if (!filter.satisfies(e.getKey())) {
if (inheritanceNodesIncludeTest(filter, e.getKey())) { continue;
collection.addAll(e.getValue()); }
}
if (inheritanceNodesIncludeTest(filter, e.getKey())) {
collection.addAll(e.getValue());
} }
} }
} }

View File

@ -27,6 +27,7 @@ package me.lucko.luckperms.common.model;
import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import me.lucko.luckperms.common.cacheddata.HolderCachedDataManager; import me.lucko.luckperms.common.cacheddata.HolderCachedDataManager;
@ -59,6 +60,7 @@ import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -249,73 +251,124 @@ public abstract class PermissionHolder {
invalidateCache(); invalidateCache();
} }
public List<Node> getOwnNodes(QueryOptions queryOptions) { private List<DataType> queryOrder(QueryOptions queryOptions) {
List<Node> nodes = new ArrayList<>();
Comparator<DataType> comparator = queryOptions.option(DataQueryOrderFunction.KEY) Comparator<DataType> comparator = queryOptions.option(DataQueryOrderFunction.KEY)
.map(func -> func.getOrderComparator(getIdentifier())) .map(func -> func.getOrderComparator(getIdentifier()))
.orElse(DataQueryOrder.TRANSIENT_FIRST); .orElse(DataQueryOrder.TRANSIENT_FIRST);
for (DataType dataType : DataQueryOrder.order(comparator)) { return DataQueryOrder.order(comparator);
}
public List<Node> getOwnNodes(QueryOptions queryOptions) {
List<Node> nodes = new ArrayList<>();
for (DataType dataType : queryOrder(queryOptions)) {
getData(dataType).copyTo(nodes, queryOptions); getData(dataType).copyTo(nodes, queryOptions);
} }
return nodes; return nodes;
} }
public SortedSet<Node> getOwnNodesSorted(QueryOptions queryOptions) { public SortedSet<Node> getOwnNodesSorted(QueryOptions queryOptions) {
SortedSet<Node> nodes = new TreeSet<>(NodeWithContextComparator.reverse()); SortedSet<Node> nodes = new TreeSet<>(NodeWithContextComparator.reverse());
for (DataType dataType : queryOrder(queryOptions)) {
Comparator<DataType> comparator = queryOptions.option(DataQueryOrderFunction.KEY)
.map(func -> func.getOrderComparator(getIdentifier()))
.orElse(DataQueryOrder.TRANSIENT_FIRST);
for (DataType dataType : DataQueryOrder.order(comparator)) {
getData(dataType).copyTo(nodes, queryOptions); getData(dataType).copyTo(nodes, queryOptions);
} }
return nodes; return nodes;
} }
public List<InheritanceNode> getOwnInheritanceNodes(QueryOptions queryOptions) { public List<InheritanceNode> getOwnInheritanceNodes(QueryOptions queryOptions) {
List<InheritanceNode> nodes = new ArrayList<>(); List<InheritanceNode> nodes = new ArrayList<>();
for (DataType dataType : queryOrder(queryOptions)) {
Comparator<DataType> comparator = queryOptions.option(DataQueryOrderFunction.KEY)
.map(func -> func.getOrderComparator(getIdentifier()))
.orElse(DataQueryOrder.TRANSIENT_FIRST);
for (DataType dataType : DataQueryOrder.order(comparator)) {
getData(dataType).copyInheritanceNodesTo(nodes, queryOptions); getData(dataType).copyInheritanceNodesTo(nodes, queryOptions);
} }
return nodes; return nodes;
} }
private void accumulateInheritedNodesTo(Collection<Node> accumulator, QueryOptions queryOptions) { public <T extends Node> List<T> getOwnNodes(NodeType<T> type, QueryOptions queryOptions) {
if (queryOptions.flag(Flag.RESOLVE_INHERITANCE)) { List<T> nodes = new ArrayList<>();
InheritanceGraph graph = this.plugin.getInheritanceGraphFactory().getGraph(queryOptions); for (DataType dataType : queryOrder(queryOptions)) {
Iterable<PermissionHolder> traversal = graph.traverse(this); getData(dataType).copyTo(nodes, type, queryOptions);
for (PermissionHolder holder : traversal) {
List<? extends Node> nodes = holder.getOwnNodes(queryOptions);
accumulator.addAll(nodes);
}
} else {
accumulator.addAll(getOwnNodes(queryOptions));
} }
return nodes;
} }
public List<Node> resolveInheritedNodes(QueryOptions queryOptions) { public List<Node> resolveInheritedNodes(QueryOptions queryOptions) {
if (!queryOptions.flag(Flag.RESOLVE_INHERITANCE)) {
return getOwnNodes(queryOptions);
}
List<Node> nodes = new ArrayList<>(); List<Node> nodes = new ArrayList<>();
accumulateInheritedNodesTo(nodes, queryOptions); InheritanceGraph graph = this.plugin.getInheritanceGraphFactory().getGraph(queryOptions);
for (PermissionHolder holder : graph.traverse(this)) {
for (DataType dataType : holder.queryOrder(queryOptions)) {
holder.getData(dataType).copyTo(nodes, queryOptions);
}
}
return nodes; return nodes;
} }
public SortedSet<Node> resolveInheritedNodesSorted(QueryOptions queryOptions) { public SortedSet<Node> resolveInheritedNodesSorted(QueryOptions queryOptions) {
if (!queryOptions.flag(Flag.RESOLVE_INHERITANCE)) {
return getOwnNodesSorted(queryOptions);
}
SortedSet<Node> nodes = new TreeSet<>(NodeWithContextComparator.reverse()); SortedSet<Node> nodes = new TreeSet<>(NodeWithContextComparator.reverse());
accumulateInheritedNodesTo(nodes, queryOptions); InheritanceGraph graph = this.plugin.getInheritanceGraphFactory().getGraph(queryOptions);
for (PermissionHolder holder : graph.traverse(this)) {
for (DataType dataType : holder.queryOrder(queryOptions)) {
holder.getData(dataType).copyTo(nodes, queryOptions);
}
}
return nodes; return nodes;
} }
public <T extends Node> List<T> resolveInheritedNodes(NodeType<T> type, QueryOptions queryOptions) {
if (!queryOptions.flag(Flag.RESOLVE_INHERITANCE)) {
return getOwnNodes(type, queryOptions);
}
List<T> nodes = new ArrayList<>();
InheritanceGraph graph = this.plugin.getInheritanceGraphFactory().getGraph(queryOptions);
for (PermissionHolder holder : graph.traverse(this)) {
for (DataType dataType : holder.queryOrder(queryOptions)) {
holder.getData(dataType).copyTo(nodes, type, queryOptions);
}
}
return nodes;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public List<Group> resolveInheritanceTree(QueryOptions queryOptions) {
if (!queryOptions.flag(Flag.RESOLVE_INHERITANCE)) {
return Collections.emptyList();
}
InheritanceGraph graph = this.plugin.getInheritanceGraphFactory().getGraph(queryOptions);
// perform a full traversal of the inheritance tree
List<PermissionHolder> traversal = new ArrayList<>();
Iterables.addAll(traversal, graph.traverse(this));
// remove 'this' (the start node) - will usually be at traversal[0],
// but not always due to the possibility of post-traversal sorts!
if (traversal.get(0) == this) {
traversal.remove(0);
} else {
traversal.remove(this);
}
// ensure our traversal now only consists of groups
for (PermissionHolder permissionHolder : traversal) {
if (!(permissionHolder instanceof Group)) {
throw new IllegalStateException("Non-group object in inheritance tree: " + permissionHolder);
}
}
// cast List<PermissionHolder> to List<Group>
// this feels a bit dirty but it works & avoids needless copying!
return (List) traversal;
}
public Map<String, Boolean> exportPermissions(QueryOptions queryOptions, boolean convertToLowercase, boolean resolveShorthand) { public Map<String, Boolean> exportPermissions(QueryOptions queryOptions, boolean convertToLowercase, boolean resolveShorthand) {
List<Node> entries = resolveInheritedNodes(queryOptions); List<Node> entries = resolveInheritedNodes(queryOptions);
return processExportedPermissions(entries, convertToLowercase, resolveShorthand); return processExportedPermissions(entries, convertToLowercase, resolveShorthand);
@ -353,22 +406,24 @@ public abstract class PermissionHolder {
public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, QueryOptions queryOptions) { public MetaAccumulator accumulateMeta(MetaAccumulator accumulator, QueryOptions queryOptions) {
InheritanceGraph graph = this.plugin.getInheritanceGraphFactory().getGraph(queryOptions); InheritanceGraph graph = this.plugin.getInheritanceGraphFactory().getGraph(queryOptions);
Iterable<PermissionHolder> traversal = graph.traverse(this); for (PermissionHolder holder : graph.traverse(this)) {
for (PermissionHolder holder : traversal) { // accumulate nodes
List<? extends Node> nodes = holder.getOwnNodes(queryOptions); for (DataType dataType : holder.queryOrder(queryOptions)) {
for (Node node : nodes) { holder.getData(dataType).forEach(queryOptions, node -> {
if (!node.getValue()) continue; if (node.getValue() && NodeType.META_OR_CHAT_META.matches(node)) {
if (!NodeType.META_OR_CHAT_META.matches(node)) continue; accumulator.accumulateNode(node);
}
accumulator.accumulateNode(node); });
} }
// accumulate weight
OptionalInt w = holder.getWeight(); OptionalInt w = holder.getWeight();
if (w.isPresent()) { if (w.isPresent()) {
accumulator.accumulateWeight(w.getAsInt()); accumulator.accumulateWeight(w.getAsInt());
} }
} }
// accumulate primary group
if (this instanceof User) { if (this instanceof User) {
String primaryGroup = ((User) this).getPrimaryGroup().calculateValue(queryOptions); String primaryGroup = ((User) this).getPrimaryGroup().calculateValue(queryOptions);
accumulator.setPrimaryGroup(primaryGroup); accumulator.setPrimaryGroup(primaryGroup);