Allow data query order to be specified via the API

This commit is contained in:
Luck 2019-08-20 21:44:43 +01:00
parent bafada4f17
commit 4667ffc681
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
31 changed files with 345 additions and 280 deletions

View File

@ -28,8 +28,10 @@ package me.lucko.luckperms.api.context;
/**
* Some default context keys used by the plugin.
*/
public enum DefaultContextKeys {
;
public final class DefaultContextKeys {
private DefaultContextKeys() {
throw new AssertionError();
}
/**
* The context key used to denote the subjects server.

View File

@ -57,34 +57,64 @@ import java.util.function.Predicate;
public interface PermissionHolder {
/**
* Gets the objects generic name.
*
* <p>The result of this method is guaranteed to be a unique identifier for distinct instances
* of the same type of object.</p>
*
* <p>For {@link User}s, this method returns a {@link UUID#toString() string} representation of
* the users {@link User#getUniqueId() unique id}.</p>
*
* <p>For {@link Group}s, this method returns the {@link Group#getName() group name}.</p>
*
* <p>The {@link User#getUniqueId()}, {@link User#getUsername()} and {@link Group#getName()} methods
* define a "tighter" specification for obtaining object identifiers.</p>
*
* @return the identifier for this object. Either a uuid string or name.
* Represents a way to identify distinct {@link PermissionHolder}s.
*/
@NonNull String getObjectName();
interface Identifier {
/**
* The {@link #getType() type} of {@link User} permission holders.
*/
String USER_TYPE = "user";
/**
* The {@link #getType() type} of {@link Group} permission holders.
*/
String GROUP_TYPE = "group";
/**
* Gets the {@link PermissionHolder}s generic name.
*
* <p>The result of this method is guaranteed to be a unique identifier for distinct instances
* of the same type of object.</p>
*
* <p>For {@link User}s, this method returns a {@link UUID#toString() string} representation of
* the users {@link User#getUniqueId() unique id}.</p>
*
* <p>For {@link Group}s, this method returns the {@link Group#getName() group name}.</p>
*
* <p>The {@link User#getUniqueId()}, {@link User#getUsername()} and {@link Group#getName()} methods
* define a "tighter" specification for obtaining object identifiers.</p>
*
* @return the identifier for this object. Either a uuid string or name.
*/
@NonNull String getName();
/**
* Gets the type of the {@link PermissionHolder}.
*
* @return the type
*/
@NonNull String getType();
}
/**
* Gets the identifier of the holder.
*
* @return the identifier
*/
@NonNull Identifier getIdentifier();
/**
* Gets a friendly name for this holder, to be displayed in command output, etc.
*
* <p>This will <strong>always</strong> return a value, eventually falling back to
* {@link #getObjectName()} if no other "friendlier" identifiers are present.</p>
* {@link Identifier#getName()} if no other "friendlier" identifiers are present.</p>
*
* <p>For {@link User}s, this method will attempt to return the {@link User#getUsername() username},
* before falling back to {@link #getObjectName()}.</p>
* before falling back to {@link Identifier#getName()}.</p>
*
* <p>For {@link Group}s, this method will attempt to return the groups display name, before
* falling back to {@link #getObjectName()}.</p>
* falling back to {@link Identifier#getName()}.</p>
*
* @return a friendly identifier for this holder
*/

View File

@ -28,7 +28,6 @@ package me.lucko.luckperms.api.node;
import me.lucko.luckperms.api.LuckPermsProvider;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.DisplayNameNode;
import me.lucko.luckperms.api.node.types.InheritanceNode;
@ -188,21 +187,21 @@ public interface Node {
* Gets the metadata corresponding to the given <code>key</code>, if present.
*
* @param key the key
* @param <T> the {@link NodeMetadata} type
* @param <T> the metadata type
* @return the data, if present
*/
<T extends NodeMetadata> Optional<T> getMetadata(NodeMetadataKey<T> key);
<T> Optional<T> getMetadata(NodeMetadataKey<T> key);
/**
* Gets the metadata corresponding to the given <code>key</code>, throwing an exception
* if no data is present.
*
* @param key the key
* @param <T> the {@link NodeMetadata} type
* @param <T> the metadata type
* @return the data
* @throws IllegalStateException if data isn't present
*/
default <T extends NodeMetadata> T metadata(NodeMetadataKey<T> key) throws IllegalStateException {
default <T> T metadata(NodeMetadataKey<T> key) throws IllegalStateException {
return getMetadata(key).orElseThrow(() -> new IllegalStateException("Node '" + getKey() + "' does not have '" + key.name() + "' attached."));
}

View File

@ -26,7 +26,6 @@
package me.lucko.luckperms.api.node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -138,7 +137,7 @@ public interface NodeBuilder<N extends ScopedNode<N, B>, B extends NodeBuilder<N
* @param <T> the metadata type
* @return the builder
*/
@NonNull <T extends NodeMetadata> B withMetadata(@NonNull NodeMetadataKey<T> key, @Nullable T metadata);
@NonNull <T> B withMetadata(@NonNull NodeMetadataKey<T> key, @Nullable T metadata);
/**
* Creates a {@link Node} instance from the builder.

View File

@ -30,7 +30,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
/**
* Represents a key for a specific type of {@link NodeMetadata}.
* Represents a key for a specific type of node metadata.
*
* <p>Metadata keys are compared using reference equality, the
* {@link #equals(Object)} method should not be implemented.</p>
@ -43,7 +43,7 @@ import java.util.Objects;
*
* @param <T> the metadata type
*/
public interface NodeMetadataKey<T extends NodeMetadata> {
public interface NodeMetadataKey<T> {
/**
* Creates a new {@link NodeMetadataKey} for the given name and type.
@ -55,7 +55,7 @@ public interface NodeMetadataKey<T extends NodeMetadata> {
* @param <T> the type parameter
* @return the key
*/
static <T extends NodeMetadata> @NonNull NodeMetadataKey<T> of(@NonNull String name, @NonNull Class<T> type) {
static <T> @NonNull NodeMetadataKey<T> of(@NonNull String name, @NonNull Class<T> type) {
Objects.requireNonNull(name, "name");
Objects.requireNonNull(type, "type");
return new SimpleNodeMetadataKey<>(name, type);

View File

@ -27,7 +27,7 @@ package me.lucko.luckperms.api.node.metadata;
import org.checkerframework.checker.nullness.qual.NonNull;
final class SimpleNodeMetadataKey<T extends NodeMetadata> implements NodeMetadataKey<T> {
final class SimpleNodeMetadataKey<T> implements NodeMetadataKey<T> {
private final String name;
private final Class<T> type;

View File

@ -27,15 +27,14 @@ package me.lucko.luckperms.api.node.metadata.types;
import me.lucko.luckperms.api.model.PermissionHolder;
import me.lucko.luckperms.api.node.Node;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* {@link NodeMetadata} indicating where a node was inherited from.
* Node metadata indicating where a node was inherited from.
*/
public interface InheritanceOriginMetadata extends NodeMetadata {
public interface InheritanceOriginMetadata {
/**
* The {@link NodeMetadataKey} for {@link InheritanceOriginMetadata}.
@ -45,10 +44,10 @@ public interface InheritanceOriginMetadata extends NodeMetadata {
/**
* Gets the location where the {@link Node} is inherited from.
*
* <p>The resultant string is the {@link PermissionHolder#getObjectName() object name} of the
* <p>The resultant string is the {@link PermissionHolder.Identifier#getName() object name} of the
* permission holder the node was inherited from.</p>
*
* <p>If the node was not inherited, the {@link PermissionHolder#getObjectName() object name}
* <p>If the node was not inherited, the {@link PermissionHolder.Identifier#getName() object name}
* of the permission holder itself (the one that defined the node) will be returned.</p>
*
* @return where the node was inherited from.

View File

@ -32,8 +32,10 @@ import java.util.EnumSet;
/**
* Some default {@link QueryOptions} instances.
*/
enum DefaultQueryOptions {
;
final class DefaultQueryOptions {
private DefaultQueryOptions() {
throw new AssertionError();
}
private static final EnumSet<Flag> DEFAULT_FLAGS = EnumSet.allOf(Flag.class);

View File

@ -0,0 +1,121 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.query.dataorder;
import me.lucko.luckperms.api.model.DataType;
import me.lucko.luckperms.api.query.QueryOptions;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
/**
* Represents the order in which to query different {@link DataType}s.
*
* <p>The {@link DataQueryOrder} enum simply represents some default
* implementations of the {@link Comparator} required by {@link QueryOptions}
* and the {@link #queryInOrder(Comparator, Consumer)} or
* {@link #order(Comparator)} methods.</p>
*
* <p>Users are free to implement their own comparator. However, be aware that
* it is possible that more {@link DataType}s may be added in the future.
* Ideally the {@link Comparator} implementations should be able to handle these
* smoothly.</p>
*/
public enum DataQueryOrder implements Comparator<DataType> {
/**
* A data query order indicating that {@link DataType#TRANSIENT} should be queried first.
*/
TRANSIENT_FIRST {
@Override
public int compare(DataType o1, DataType o2) {
if (o1 == o2) {
return 0;
}
return Boolean.compare(
o1 == DataType.TRANSIENT,
o2 == DataType.TRANSIENT
);
}
},
/**
* A data query order indicating that {@link DataType#TRANSIENT} should be queried last.
*/
TRANSIENT_LAST {
@Override
public int compare(DataType o1, DataType o2) {
if (o1 == o2) {
return 0;
}
return -Boolean.compare(
o1 == DataType.TRANSIENT,
o2 == DataType.TRANSIENT
);
}
};
private static final List<DataType> TRANSIENT_FIRST_LIST = Collections.unmodifiableList(Arrays.asList(DataType.TRANSIENT, DataType.NORMAL));
private static final List<DataType> TRANSIENT_LAST_LIST = Collections.unmodifiableList(Arrays.asList(DataType.NORMAL, DataType.TRANSIENT));
/**
* Gets a {@link List} of all {@link DataType}s, in the order defined by
* the {@code comparator}.
*
* @param comparator the comparator
* @return the ordered data types
*/
public static List<DataType> order(@NonNull Comparator<? super DataType> comparator) {
int compare = comparator.compare(DataType.TRANSIENT, DataType.NORMAL);
if (compare > 0) {
// transient first
return TRANSIENT_FIRST_LIST;
} else if (compare < 0) {
// transient last
return TRANSIENT_LAST_LIST;
} else {
// ??? - no defined order
throw new IllegalStateException("Comparator " + comparator + " does not define an order between DataType.NORMAL and DataType.TRANSIENT!");
}
}
/**
* Calls the {@code action} {@link Consumer} for each {@link DataType}, in
* the order defined by the {@code comparator}.
*
* @param comparator the comparator
* @param action the action
*/
public static void queryInOrder(@NonNull Comparator<? super DataType> comparator, @NonNull Consumer<? super DataType> action) {
order(comparator).forEach(action);
}
}

View File

@ -23,21 +23,34 @@
* SOFTWARE.
*/
package me.lucko.luckperms.sponge.service.model;
package me.lucko.luckperms.api.query.dataorder;
import me.lucko.luckperms.api.model.DataType;
import me.lucko.luckperms.api.model.PermissionHolder;
import me.lucko.luckperms.api.query.OptionKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Comparator;
/**
* Defines in what order data should be resolved.
* A function that generates a {@link DataQueryOrder} comparator for
* {@link PermissionHolder}s as required during inheritance.
*/
public enum ResolutionOrder {
public interface DataQueryOrderFunction {
/**
* Marks that transient data should be considered before enduring data
* The {@link OptionKey} for {@link DataQueryOrderFunction}.
*/
TRANSIENT_FIRST,
OptionKey<DataQueryOrderFunction> KEY = new OptionKey<DataQueryOrderFunction>(){};
/**
* Marks that transient data should be considered after enduring data
* Gets the {@link DataQueryOrder} comparator for the given
* {@link PermissionHolder.Identifier holder identifier}.
*
* @param holderIdentifier the holder identifier
* @return the comparator to use
*/
TRANSIENT_LAST
@NonNull Comparator<DataType> getOrderComparator(PermissionHolder.@NonNull Identifier holderIdentifier);
}

View File

@ -29,7 +29,6 @@ import com.google.common.base.Preconditions;
import me.lucko.luckperms.api.model.DataType;
import me.lucko.luckperms.api.node.Node;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.bukkit.inject.dummy.DummyPlugin;
import me.lucko.luckperms.common.config.ConfigKeys;
@ -53,7 +52,7 @@ import java.util.Set;
*
* Applies all permissions directly to the backing user instance via transient nodes.
*/
public class LPPermissionAttachment extends PermissionAttachment implements NodeMetadata {
public class LPPermissionAttachment extends PermissionAttachment {
public static final NodeMetadataKey<LPPermissionAttachment> TRANSIENT_SOURCE_KEY = NodeMetadataKey.of("TransientSource", LPPermissionAttachment.class);

View File

@ -25,8 +25,8 @@
package me.lucko.luckperms.common.api;
import me.lucko.luckperms.api.LuckPermsProvider;
import me.lucko.luckperms.api.LuckPerms;
import me.lucko.luckperms.api.LuckPermsProvider;
import java.lang.reflect.Method;

View File

@ -76,9 +76,8 @@ public class ApiGroup extends ApiPermissionHolder implements me.lucko.luckperms.
return this.handle.getWeight();
}
@NonNull
@Override
public GroupCachedDataManager getCachedData() {
public @NonNull GroupCachedDataManager getCachedData() {
return this.handle.getCachedData();
}

View File

@ -72,8 +72,8 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.model.Permiss
}
@Override
public @NonNull String getObjectName() {
return this.handle.getObjectName();
public @NonNull Identifier getIdentifier() {
return this.handle.getIdentifier();
}
@Override

View File

@ -87,9 +87,8 @@ public class ApiUser extends ApiPermissionHolder implements me.lucko.luckperms.a
return DataMutateResult.SUCCESS;
}
@NonNull
@Override
public UserCachedDataManager getCachedData() {
public @NonNull UserCachedDataManager getCachedData() {
return this.handle.getCachedData();
}

View File

@ -26,7 +26,6 @@
package me.lucko.luckperms.common.model;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.model.DataType;
import me.lucko.luckperms.api.node.Node;
import me.lucko.luckperms.api.node.types.DisplayNameNode;
import me.lucko.luckperms.common.api.implementation.ApiGroup;
@ -116,7 +115,7 @@ public class Group extends PermissionHolder implements Identifiable<String> {
* @return the display name
*/
public Optional<String> getDisplayName(ContextSet contextSet) {
for (Node n : getData(DataType.NORMAL).immutable().get(contextSet.immutableCopy())) {
for (Node n : normalData().immutable().get(contextSet.immutableCopy())) {
if (n instanceof DisplayNameNode) {
return Optional.of(((DisplayNameNode) n).getDisplayName());
}

View File

@ -43,6 +43,8 @@ import me.lucko.luckperms.api.node.Tristate;
import me.lucko.luckperms.api.node.types.InheritanceNode;
import me.lucko.luckperms.api.query.Flag;
import me.lucko.luckperms.api.query.QueryOptions;
import me.lucko.luckperms.api.query.dataorder.DataQueryOrder;
import me.lucko.luckperms.api.query.dataorder.DataQueryOrderFunction;
import me.lucko.luckperms.common.cacheddata.HolderCachedDataManager;
import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator;
import me.lucko.luckperms.common.inheritance.InheritanceComparator;
@ -175,6 +177,10 @@ public abstract class PermissionHolder {
return this.transientNodes;
}
public PermissionHolderIdentifier getIdentifier() {
return new PermissionHolderIdentifier(getType(), getObjectName());
}
/**
* Gets the unique name of this holder object.
*
@ -234,22 +240,43 @@ public abstract class PermissionHolder {
public List<Node> getOwnNodes(QueryOptions queryOptions) {
List<Node> ret = new ArrayList<>();
this.transientNodes.copyTo(ret, queryOptions);
this.normalNodes.copyTo(ret, 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(ret, queryOptions);
}
return ret;
}
public SortedSet<Node> getOwnNodesSorted(QueryOptions queryOptions) {
SortedSet<Node> ret = new TreeSet<>(NodeWithContextComparator.reverse());
this.transientNodes.copyTo(ret, queryOptions);
this.normalNodes.copyTo(ret, 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(ret, queryOptions);
}
return ret;
}
public List<InheritanceNode> getOwnGroupNodes(QueryOptions queryOptions) {
List<InheritanceNode> ret = new ArrayList<>();
this.transientNodes.copyInheritanceNodesTo(ret, queryOptions);
this.normalNodes.copyInheritanceNodesTo(ret, 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(ret, queryOptions);
}
return ret;
}

View File

@ -23,14 +23,49 @@
* SOFTWARE.
*/
package me.lucko.luckperms.api.node.metadata;
package me.lucko.luckperms.common.model;
import me.lucko.luckperms.api.node.Node;
import me.lucko.luckperms.api.model.PermissionHolder.Identifier;
/**
* Generic interface for an {@link Object} which can be attached to a
* {@link Node} as metadata.
*/
public interface NodeMetadata {
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
public final class PermissionHolderIdentifier implements Identifier {
private final String type;
private final String name;
public PermissionHolderIdentifier(String type, String name) {
this.type = type;
this.name = name;
}
public PermissionHolderIdentifier(HolderType type, String name) {
this.type = type == HolderType.USER ? Identifier.USER_TYPE : Identifier.GROUP_TYPE;
this.name = name;
}
@Override
public @NonNull String getName() {
return this.name;
}
@Override
public @NonNull String getType() {
return this.type;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Identifier)) return false;
Identifier that = (Identifier) o;
return getType().equals(that.getType()) &&
getName().equals(that.getName());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getName());
}
}

View File

@ -33,7 +33,6 @@ import me.lucko.luckperms.api.node.Node;
import me.lucko.luckperms.api.node.NodeBuilder;
import me.lucko.luckperms.api.node.NodeEqualityPredicate;
import me.lucko.luckperms.api.node.ScopedNode;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.RegexPermissionNode;
import me.lucko.luckperms.common.node.utils.ShorthandParser;
@ -63,14 +62,14 @@ public abstract class AbstractNode<N extends ScopedNode<N, B>, B extends NodeBui
protected final boolean value;
protected final long expireAt; // 0L for no expiry
protected final ImmutableContextSet contexts;
protected final Map<NodeMetadataKey<?>, NodeMetadata> metadata;
protected final Map<NodeMetadataKey<?>, Object> metadata;
private final List<String> resolvedShorthand;
// this class is immutable, so we can cache the hashcode calculation
private final int hashCode;
protected AbstractNode(String key, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
protected AbstractNode(String key, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
this.key = key;
this.value = value;
this.expireAt = expireAt;
@ -98,7 +97,7 @@ public abstract class AbstractNode<N extends ScopedNode<N, B>, B extends NodeBui
}
@Override
public <T extends NodeMetadata> Optional<T> getMetadata(NodeMetadataKey<T> key) {
public <T> Optional<T> getMetadata(NodeMetadataKey<T> key) {
//noinspection unchecked
T value = (T) this.metadata.get(key);
return Optional.ofNullable(value);

View File

@ -30,7 +30,6 @@ import me.lucko.luckperms.api.context.DefaultContextKeys;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.NodeBuilder;
import me.lucko.luckperms.api.node.ScopedNode;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -44,7 +43,7 @@ public abstract class AbstractNodeBuilder<N extends ScopedNode<N, B>, B extends
protected boolean value;
protected long expireAt;
protected ImmutableContextSet.Builder context;
protected Map<NodeMetadataKey<?>, NodeMetadata> metadata;
protected Map<NodeMetadataKey<?>, Object> metadata;
protected AbstractNodeBuilder() {
this.value = true;
@ -53,7 +52,7 @@ public abstract class AbstractNodeBuilder<N extends ScopedNode<N, B>, B extends
this.metadata = new IdentityHashMap<>();
}
protected AbstractNodeBuilder(boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
protected AbstractNodeBuilder(boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
this.value = value;
this.expireAt = expireAt;
this.context = ImmutableContextSet.builder().addAll(context);
@ -108,7 +107,7 @@ public abstract class AbstractNodeBuilder<N extends ScopedNode<N, B>, B extends
}
@Override
public @NonNull <T extends NodeMetadata> B withMetadata(@NonNull NodeMetadataKey<T> key, @Nullable T metadata) {
public @NonNull <T> B withMetadata(@NonNull NodeMetadataKey<T> key, @Nullable T metadata) {
Objects.requireNonNull(key, "key");
if (metadata == null) {
this.metadata.remove(key);

View File

@ -26,7 +26,6 @@
package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.DisplayNameNode;
import me.lucko.luckperms.common.node.AbstractNode;
@ -41,7 +40,7 @@ import java.util.Objects;
public class DisplayName extends AbstractNode<DisplayNameNode, DisplayNameNode.Builder> implements DisplayNameNode {
private final String displayName;
public DisplayName(String displayName, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public DisplayName(String displayName, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(NodeFactory.displayName(displayName), value, expireAt, contexts, metadata);
this.displayName = displayName;
}
@ -63,7 +62,7 @@ public class DisplayName extends AbstractNode<DisplayNameNode, DisplayNameNode.B
this.displayName = null;
}
public Builder(String displayName, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(String displayName, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.displayName = displayName;
}

View File

@ -27,7 +27,6 @@ package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.model.group.Group;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.InheritanceNode;
import me.lucko.luckperms.common.node.AbstractNode;
@ -42,7 +41,7 @@ import java.util.Objects;
public class Inheritance extends AbstractNode<InheritanceNode, InheritanceNode.Builder> implements InheritanceNode {
private final String groupName;
public Inheritance(String groupName, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Inheritance(String groupName, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(NodeFactory.groupNode(groupName), value, expireAt, contexts, metadata);
this.groupName = groupName;
}
@ -64,7 +63,7 @@ public class Inheritance extends AbstractNode<InheritanceNode, InheritanceNode.B
this.groupName = null;
}
public Builder(String groupName, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(String groupName, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.groupName = groupName;
}

View File

@ -26,7 +26,6 @@
package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.MetaNode;
import me.lucko.luckperms.common.node.AbstractNode;
@ -42,7 +41,7 @@ public class Meta extends AbstractNode<MetaNode, MetaNode.Builder> implements Me
private final String metaKey;
private final String metaValue;
public Meta(String metaKey, String metaValue, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Meta(String metaKey, String metaValue, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(NodeFactory.metaNode(metaKey, metaValue), value, expireAt, contexts, metadata);
this.metaKey = metaKey;
this.metaValue = metaValue;
@ -72,7 +71,7 @@ public class Meta extends AbstractNode<MetaNode, MetaNode.Builder> implements Me
this.metaValue = null;
}
public Builder(String metaKey, String metaValue, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(String metaKey, String metaValue, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.metaKey = metaKey;
this.metaValue = metaValue;

View File

@ -27,7 +27,6 @@ package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.NodeBuilder;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.PermissionNode;
import me.lucko.luckperms.common.calculator.processor.WildcardProcessor;
@ -44,7 +43,7 @@ import java.util.OptionalInt;
public class Permission extends AbstractNode<PermissionNode, PermissionNode.Builder> implements PermissionNode {
private final int wildcardLevel;
public Permission(String permission, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Permission(String permission, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(permission, value, expireAt, contexts, metadata);
this.wildcardLevel = permission.endsWith(WildcardProcessor.WILDCARD_SUFFIX) ? permission.chars().filter(num -> num == NODE_SEPARATOR_CODE).sum() : -1;
}
@ -76,7 +75,7 @@ public class Permission extends AbstractNode<PermissionNode, PermissionNode.Buil
this.permission = null;
}
public Builder(String permission, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(String permission, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.permission = permission;
}

View File

@ -27,7 +27,6 @@ package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.ChatMetaType;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.PrefixNode;
import me.lucko.luckperms.common.node.AbstractNode;
@ -43,7 +42,7 @@ public class Prefix extends AbstractNode<PrefixNode, PrefixNode.Builder> impleme
private final String prefix;
private final int priority;
public Prefix(String prefix, int priority, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Prefix(String prefix, int priority, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(NodeFactory.prefixNode(priority, prefix), value, expireAt, contexts, metadata);
this.prefix = prefix;
this.priority = priority;
@ -78,7 +77,7 @@ public class Prefix extends AbstractNode<PrefixNode, PrefixNode.Builder> impleme
this.priority = null;
}
public Builder(String prefix, int priority, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(String prefix, int priority, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.prefix = prefix;
this.priority = priority;

View File

@ -26,7 +26,6 @@
package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.RegexPermissionNode;
import me.lucko.luckperms.common.cache.Cache;
@ -52,7 +51,7 @@ public class RegexPermission extends AbstractNode<RegexPermissionNode, RegexPerm
}
};
public RegexPermission(String pattern, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public RegexPermission(String pattern, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(NodeFactory.regexNode(pattern), value, expireAt, contexts, metadata);
this.pattern = pattern;
}
@ -79,7 +78,7 @@ public class RegexPermission extends AbstractNode<RegexPermissionNode, RegexPerm
this.pattern = null;
}
public Builder(String pattern, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(String pattern, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.pattern = pattern;
}

View File

@ -27,7 +27,6 @@ package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.ChatMetaType;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.SuffixNode;
import me.lucko.luckperms.common.node.AbstractNode;
@ -43,7 +42,7 @@ public class Suffix extends AbstractNode<SuffixNode, SuffixNode.Builder> impleme
private final String suffix;
private final int priority;
public Suffix(String suffix, int priority, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Suffix(String suffix, int priority, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(NodeFactory.suffixNode(priority, suffix), value, expireAt, contexts, metadata);
this.suffix = suffix;
this.priority = priority;
@ -78,7 +77,7 @@ public class Suffix extends AbstractNode<SuffixNode, SuffixNode.Builder> impleme
this.priority = null;
}
public Builder(String suffix, int priority, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(String suffix, int priority, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.suffix = suffix;
this.priority = priority;

View File

@ -26,7 +26,6 @@
package me.lucko.luckperms.common.node.types;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.node.types.WeightNode;
import me.lucko.luckperms.common.node.AbstractNode;
@ -41,7 +40,7 @@ import java.util.Objects;
public class Weight extends AbstractNode<WeightNode, WeightNode.Builder> implements WeightNode {
private final int weight;
public Weight(int weight, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Weight(int weight, boolean value, long expireAt, ImmutableContextSet contexts, Map<NodeMetadataKey<?>, Object> metadata) {
super(NodeFactory.weightNode(weight), value, expireAt, contexts, metadata);
this.weight = weight;
}
@ -63,7 +62,7 @@ public class Weight extends AbstractNode<WeightNode, WeightNode.Builder> impleme
this.weight = null;
}
public Builder(int weight, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, NodeMetadata> metadata) {
public Builder(int weight, boolean value, long expireAt, ImmutableContextSet context, Map<NodeMetadataKey<?>, Object> metadata) {
super(value, expireAt, context, metadata);
this.weight = weight;
}

View File

@ -29,7 +29,6 @@ import com.google.common.base.Preconditions;
import me.lucko.luckperms.api.model.DataType;
import me.lucko.luckperms.api.node.Node;
import me.lucko.luckperms.api.node.metadata.NodeMetadata;
import me.lucko.luckperms.api.node.metadata.NodeMetadataKey;
import me.lucko.luckperms.api.util.Result;
import me.lucko.luckperms.common.config.ConfigKeys;
@ -56,7 +55,7 @@ import java.util.Set;
*
* Applies all permissions directly to the backing user instance via transient nodes.
*/
public class LPPermissionAttachment extends PermissionAttachment implements NodeMetadata {
public class LPPermissionAttachment extends PermissionAttachment {
public static final NodeMetadataKey<LPPermissionAttachment> TRANSIENT_SOURCE_KEY = NodeMetadataKey.of("TransientSource", LPPermissionAttachment.class);

View File

@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.query.dataorder.DataQueryOrder;
import org.spongepowered.api.service.permission.SubjectCollection;
@ -52,11 +53,11 @@ public interface LPSubjectCollection {
Predicate<String> getIdentifierValidityPredicate();
// transient has priority for all collections except default
default ResolutionOrder getResolutionOrder() {
default DataQueryOrder getResolutionOrder() {
if (isDefaultsCollection()) {
return ResolutionOrder.TRANSIENT_LAST;
return DataQueryOrder.TRANSIENT_LAST;
} else {
return ResolutionOrder.TRANSIENT_FIRST;
return DataQueryOrder.TRANSIENT_FIRST;
}
}

View File

@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.model.DataMutateResult;
import me.lucko.luckperms.api.model.DataType;
import me.lucko.luckperms.api.node.ChatMetaType;
import me.lucko.luckperms.api.node.Node;
@ -58,11 +57,9 @@ import org.spongepowered.api.service.permission.SubjectData;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PermissionHolderSubjectData implements LPSubjectData {
@ -129,54 +126,20 @@ public class PermissionHolderSubjectData implements LPSubjectData {
if (tristate == Tristate.UNDEFINED) {
// Unset
Node node = NodeFactory.builder(permission).withContext(contexts).build();
switch (this.type) {
case NORMAL:
this.holder.unsetPermission(DataType.NORMAL, node);
break;
case TRANSIENT:
this.holder.unsetPermission(DataType.TRANSIENT, node);
break;
default:
throw new AssertionError();
}
this.holder.unsetPermission(this.type, node);
return save(this.holder).thenApply(v -> true);
}
Node node = NodeFactory.builder(permission).value(tristate.asBoolean()).withContext(contexts).build();
// unset the inverse, to allow false -> true, true -> false overrides.
// unset the inverse, to allow false -> true, true -> false overrides.
switch (this.type) {
case NORMAL:
// unset the inverse, to allow false -> true, true -> false overrides.
this.holder.unsetPermission(DataType.NORMAL, node);
this.holder.setPermission(DataType.NORMAL, node, true);
break;
case TRANSIENT:
// unset the inverse, to allow false -> true, true -> false overrides.
this.holder.unsetPermission(DataType.TRANSIENT, node);
this.holder.setPermission(DataType.TRANSIENT, node, true);
break;
default:
throw new AssertionError();
}
this.holder.unsetPermission(this.type, node);
this.holder.setPermission(this.type, node, true);
return save(this.holder).thenApply(v -> true);
}
@Override
public CompletableFuture<Boolean> clearPermissions() {
boolean ret;
switch (this.type) {
case NORMAL:
ret = this.holder.clearNodes(DataType.NORMAL, null);
break;
case TRANSIENT:
ret = this.holder.clearNodes(DataType.TRANSIENT, null);
break;
default:
throw new AssertionError();
}
if (!ret) {
if (!this.holder.clearNodes(this.type, null)) {
return CompletableFuture.completedFuture(false);
}
@ -190,24 +153,7 @@ public class PermissionHolderSubjectData implements LPSubjectData {
@Override
public CompletableFuture<Boolean> clearPermissions(ImmutableContextSet contexts) {
Objects.requireNonNull(contexts, "contexts");
boolean ret;
switch (this.type) {
case NORMAL:
ret = this.holder.clearNodes(DataType.NORMAL, contexts);
break;
case TRANSIENT:
List<Node> toRemove = streamNodes()
.filter(n -> n.getContexts().equals(contexts))
.collect(Collectors.toList());
toRemove.forEach(node -> this.holder.unsetPermission(DataType.TRANSIENT, node));
ret = !toRemove.isEmpty();
break;
default:
throw new AssertionError();
}
if (!ret) {
if (!this.holder.clearNodes(this.type, contexts)) {
return CompletableFuture.completedFuture(false);
}
@ -257,19 +203,7 @@ public class PermissionHolderSubjectData implements LPSubjectData {
.withContext(contexts)
.build();
DataMutateResult result;
switch (this.type) {
case NORMAL:
result = this.holder.setPermission(DataType.NORMAL, node, true);
break;
case TRANSIENT:
result = this.holder.setPermission(DataType.TRANSIENT, node, true);
break;
default:
throw new AssertionError();
}
if (!result.wasSuccessful()) {
if (!this.holder.setPermission(this.type, node, true).wasSuccessful()) {
return CompletableFuture.completedFuture(false);
}
@ -289,19 +223,7 @@ public class PermissionHolderSubjectData implements LPSubjectData {
.withContext(contexts)
.build();
DataMutateResult result;
switch (this.type) {
case NORMAL:
result = this.holder.unsetPermission(DataType.NORMAL, node);
break;
case TRANSIENT:
result = this.holder.unsetPermission(DataType.TRANSIENT, node);
break;
default:
throw new AssertionError();
}
if (!result.wasSuccessful()) {
if (!this.holder.unsetPermission(this.type, node).wasSuccessful()) {
return CompletableFuture.completedFuture(false);
}
@ -316,12 +238,10 @@ public class PermissionHolderSubjectData implements LPSubjectData {
ret = this.holder.clearNormalParents(null, true);
break;
case TRANSIENT:
List<Node> toRemove = streamNodes()
ret = streamNodes()
.filter(n -> n instanceof InheritanceNode)
.collect(Collectors.toList());
toRemove.forEach(node -> this.holder.unsetPermission(DataType.TRANSIENT, node));
ret = !toRemove.isEmpty();
.peek(n -> this.holder.unsetPermission(DataType.TRANSIENT, n))
.findAny().isPresent();
break;
default:
throw new AssertionError();
@ -343,13 +263,11 @@ public class PermissionHolderSubjectData implements LPSubjectData {
ret = this.holder.clearNormalParents(contexts, true);
break;
case TRANSIENT:
List<Node> toRemove = streamNodes()
ret = streamNodes()
.filter(n -> n instanceof InheritanceNode)
.filter(n -> n.getContexts().equals(contexts))
.collect(Collectors.toList());
toRemove.forEach(node -> this.holder.unsetPermission(DataType.TRANSIENT, node));
ret = !toRemove.isEmpty();
.peek(n -> this.holder.unsetPermission(DataType.TRANSIENT, n))
.findAny().isPresent();
break;
default:
throw new AssertionError();
@ -425,20 +343,9 @@ public class PermissionHolderSubjectData implements LPSubjectData {
// remove all prefixes/suffixes from the user
streamNodes()
.filter(node1 -> type.nodeType().matches(node1))
.filter(n -> type.nodeType().matches(n))
.filter(n -> n.getContexts().equals(contexts))
.forEach(n -> {
switch (this.type) {
case NORMAL:
this.holder.unsetPermission(DataType.NORMAL, n);
break;
case TRANSIENT:
this.holder.unsetPermission(DataType.TRANSIENT, n);
break;
default:
throw new AssertionError();
}
});
.forEach(n -> this.holder.unsetPermission(this.type, n));
MetaAccumulator metaAccumulator = this.holder.accumulateMeta(null, QueryOptions.defaultContextualOptions().toBuilder().context(contexts).build());
metaAccumulator.complete();
@ -451,32 +358,12 @@ public class PermissionHolderSubjectData implements LPSubjectData {
streamNodes()
.filter(n -> n instanceof MetaNode && ((MetaNode) n).getMetaKey().equals(key))
.filter(n -> n.getContexts().equals(contexts))
.forEach(n -> {
switch (this.type) {
case NORMAL:
this.holder.unsetPermission(DataType.NORMAL, n);
break;
case TRANSIENT:
this.holder.unsetPermission(DataType.TRANSIENT, n);
break;
default:
throw new AssertionError();
}
});
.forEach(n -> this.holder.unsetPermission(this.type, n));
node = NodeFactory.buildMetaNode(key, value).withContext(contexts).build();
}
switch (this.type) {
case NORMAL:
this.holder.setPermission(DataType.NORMAL, node, true);
break;
case TRANSIENT:
this.holder.setPermission(DataType.TRANSIENT, node, true);
break;
default:
throw new AssertionError();
}
this.holder.setPermission(this.type, node, true);
return save(this.holder).thenApply(v -> true);
}
@ -496,18 +383,7 @@ public class PermissionHolderSubjectData implements LPSubjectData {
}
})
.filter(n -> n.getContexts().equals(contexts))
.forEach(node -> {
switch (this.type) {
case NORMAL:
this.holder.unsetPermission(DataType.NORMAL, node);
break;
case TRANSIENT:
this.holder.unsetPermission(DataType.TRANSIENT, node);
break;
default:
throw new AssertionError();
}
});
.forEach(n -> this.holder.unsetPermission(this.type, n));
return save(this.holder).thenApply(v -> true);
}
@ -516,25 +392,13 @@ public class PermissionHolderSubjectData implements LPSubjectData {
public CompletableFuture<Boolean> clearOptions(ImmutableContextSet contexts) {
Objects.requireNonNull(contexts, "contexts");
List<Node> toRemove = streamNodes()
boolean success = streamNodes()
.filter(NodeType.META_OR_CHAT_META::matches)
.filter(n -> n.getContexts().equals(contexts))
.collect(Collectors.toList());
.peek(n -> this.holder.unsetPermission(this.type, n))
.findAny().isPresent();
toRemove.forEach(node -> {
switch (this.type) {
case NORMAL:
this.holder.unsetPermission(DataType.NORMAL, node);
break;
case TRANSIENT:
this.holder.unsetPermission(DataType.TRANSIENT, node);
break;
default:
throw new AssertionError();
}
});
if (toRemove.isEmpty()) {
if (!success) {
return CompletableFuture.completedFuture(false);
}
@ -543,24 +407,12 @@ public class PermissionHolderSubjectData implements LPSubjectData {
@Override
public CompletableFuture<Boolean> clearOptions() {
List<Node> toRemove = streamNodes()
boolean success = streamNodes()
.filter(NodeType.META_OR_CHAT_META::matches)
.collect(Collectors.toList());
.peek(n -> this.holder.unsetPermission(this.type, n))
.findAny().isPresent();
toRemove.forEach(node -> {
switch (this.type) {
case NORMAL:
this.holder.unsetPermission(DataType.NORMAL, node);
break;
case TRANSIENT:
this.holder.unsetPermission(DataType.TRANSIENT, node);
break;
default:
throw new AssertionError();
}
});
if (toRemove.isEmpty()) {
if (!success) {
return CompletableFuture.completedFuture(false);
}