me.lucko.luckperms.api.Node Maven / Gradle / Ivy
Show all versions of luckperms-api Show documentation
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck)
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import com.google.common.base.Preconditions;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.nodetype.NodeType;
import me.lucko.luckperms.api.nodetype.NodeTypeKey;
import me.lucko.luckperms.api.nodetype.types.DisplayNameType;
import me.lucko.luckperms.api.nodetype.types.InheritanceType;
import me.lucko.luckperms.api.nodetype.types.MetaType;
import me.lucko.luckperms.api.nodetype.types.PrefixType;
import me.lucko.luckperms.api.nodetype.types.SuffixType;
import me.lucko.luckperms.api.nodetype.types.WeightType;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
* Represents a LuckPerms "node".
*
* The {@link Node} class encapsulates more than just permission assignments.
* Nodes are used to store data about inherited groups, as well as assigned
* prefixes, suffixes and meta values.
*
* Combining these various states into one object (a "node") means that a
* holder only has to have one type of data set (a set of nodes) in order to
* take on various properties.
*
* It is recommended that users of the API make use of {@link Stream}s
* to manipulate data and obtain the required information.
*
* This interface provides a number of methods to read the attributes of the
* node, as well as methods to query and extract additional state and properties
* from these settings.
*
* Nodes have the following attributes:
*
*
* - {@link #getPermission() permission} - the actual permission string
* - {@link #getValue() value} - the value of the node (false for negated)
* - {@link #isOverride() override} - if the node is marked as having special priority over other nodes
* - {@link #getServer() server} - the specific server where this node should apply
* - {@link #getWorld() world} - the specific world where this node should apply
* - {@link #getContexts() context} - the additional contexts required for this node to apply
* - {@link #getExpiry() expiry} - the time when this node should expire
*
*
* The 'permission' property of a {@link Node} is also used in some cases to represent state
* beyond a granted permission. This state is encapsulated by extra {@link NodeType} data which
* can be obtained from this instance using {@link #getTypeData(NodeTypeKey)}.
*
* Type data is mapped by {@link NodeTypeKey}s, which are usually stored as static members of the
* corresponding {@link NodeType} class under the KEY
field.
*
* The current types are:
*
*
* - normal - just a regular permission
* - {@link InheritanceType} - an "inheritance node" marks that the holder should inherit data from another group
* - {@link PrefixType} - represents an assigned prefix
* - {@link SuffixType} - represents an assigned suffix
* - {@link MetaType} - represents an assigned meta option
* - {@link WeightType} - marks the weight of the object holding this node
* - {@link DisplayNameType} - marks the display name of the object holding this node
*
*
* The core node state must be immutable in all implementations.
*
* @see NodeFactory for obtaining and constructing instances.
* @since 2.6
*/
public interface Node {
/**
* Gets the permission string this node encapsulates.
*
* The exact value of this string may vary for nodes which aren't regular
* permission settings.
*
* @return the actual permission node
*/
@NonNull String getPermission();
/**
* Gets the value of the node.
*
* A negated setting would result in a value of false
.
*
* @return the nodes value
*/
boolean getValue();
/**
* Gets the value of this node as a {@link Tristate}.
*
* @return the value of this node as a Tristate
*/
default @NonNull Tristate getTristate() {
return Tristate.fromBoolean(getValue());
}
/**
* Gets if the node is negated.
*
* This is the inverse of the {@link #getValue() value}.
*
* @return true if the node is negated
*/
default boolean isNegated() {
return !getValue();
}
/**
* Gets if this node is set to override explicitly.
*
* This value does not persist across saves, and is therefore only
* useful for transient nodes.
*
* @return true if this node is set to override explicitly
*/
boolean isOverride();
/**
* Gets the server this node applies on, if the node is server specific.
*
* @return an {@link Optional} containing the server, if one is defined
*/
@NonNull Optional getServer();
/**
* Gets the world this node applies on, if the node is world specific.
*
* @return an {@link Optional} containing the world, if one is defined
*/
@NonNull Optional getWorld();
/**
* Gets if this node is server specific.
*
* @return true if this node is server specific
*/
boolean isServerSpecific();
/**
* Gets if this node is server specific.
*
* @return true if this node is server specific
*/
boolean isWorldSpecific();
/**
* Gets if this node applies globally, and therefore has no specific context.
*
* @return true if this node applies globally, and has no specific context
* @since 3.1
*/
boolean appliesGlobally();
/**
* Gets if this node has any specific context in order for it to apply.
*
* @return true if this node has specific context
* @since 3.1
*/
boolean hasSpecificContext();
/**
* Gets if this node should apply in the given context
*
* @param contextSet the context set
* @return true if the node should apply
* @since 2.13
*/
boolean shouldApplyWithContext(@NonNull ContextSet contextSet);
/**
* Resolves any shorthand parts of this node and returns the full list of
* resolved nodes.
*
* The list will not contain the exact permission itself.
*
* @return a list of full nodes
*/
@NonNull List resolveShorthand();
/**
* Gets if this node is assigned temporarily.
*
* @return true if this node will expire in the future
*/
boolean isTemporary();
/**
* Gets if this node is permanent (will not expire).
*
* @return true if this node will not expire
*/
default boolean isPermanent() {
return !isTemporary();
}
/**
* Gets the unix timestamp (in seconds) when this node will expire.
*
* @return the time in Unix time when this node will expire
* @throws IllegalStateException if the node is not temporary
*/
long getExpiryUnixTime() throws IllegalStateException;
/**
* Gets the date when this node will expire.
*
* @return the {@link Date} when this node will expire
* @throws IllegalStateException if the node is not temporary
*/
@NonNull Date getExpiry() throws IllegalStateException;
/**
* Gets the number of seconds until this permission will expire.
*
* Will return a negative value if the node has already expired.
*
* @return the number of seconds until this permission will expire
* @throws IllegalStateException if the node is not temporary
*/
long getSecondsTilExpiry() throws IllegalStateException;
/**
* Gets if the node has expired.
*
* This returns false if the node is not temporary.
*
* @return true if this node has expired
*/
boolean hasExpired();
/**
* Gets the extra contexts required for this node to apply.
*
* @return the extra contexts required for this node to apply
* @since 2.13
*/
@NonNull ContextSet getContexts();
/**
* The same as {@link #getContexts()}, but also includes context pairs for
* "server" and "world" keys if present.
*
* @return the full contexts required for this node to apply
* @see Contexts#SERVER_KEY
* @see Contexts#WORLD_KEY
* @since 3.1
*/
@NonNull ContextSet getFullContexts();
/**
* Gets if this node is a wildcard permission.
*
* @return true if this node is a wildcard permission
*/
boolean isWildcard();
/**
* Gets the level of this wildcard.
*
* The node luckperms.*
has a wildcard level of 1.
* The node luckperms.user.permission.*
has a wildcard level of 3.
*
* Nodes with a higher wildcard level are more specific and have priority over
* less specific nodes (nodes with a lower wildcard level).
*
* @return the wildcard level
* @throws IllegalStateException if this is not a wildcard
*/
int getWildcardLevel() throws IllegalStateException;
/**
* Gets if this node has any extra {@link NodeType} data attached to it.
*
* @return if this node has any type data
* @since 4.2
*/
boolean hasTypeData();
/**
* Gets the type data corresponding to the given key
, if present.
*
* @param key the key
* @param the {@link NodeType} type
* @return the data, if present
* @since 4.2
*/
Optional getTypeData(NodeTypeKey key);
/**
* Gets the type data corresponding to the given key
, throwing an exception
* if no data is present.
*
* @param key the key
* @param the {@link NodeType} type
* @return the data
* @throws IllegalStateException if data isn't present
* @since 4.2
*/
default T typeData(NodeTypeKey key) throws IllegalStateException {
return getTypeData(key).orElseThrow(() -> new IllegalStateException("Node '" + getPermission() + "' does not have the '" + key.getTypeName() + "' type."));
}
/**
* Gets if this node has {@link InheritanceType} type data.
*
* @return true if this is a inheritance (group) node.
*/
default boolean isGroupNode() {
return getTypeData(InheritanceType.KEY).isPresent();
}
/**
* Gets the name of the inherited group if this node has {@link InheritanceType} type data,
* throwing an exception if the data is not present.
*
* @return the name of the group
* @throws IllegalStateException if this node doesn't have {@link InheritanceType} data
*/
default @NonNull String getGroupName() throws IllegalStateException {
return typeData(InheritanceType.KEY).getGroupName();
}
/**
* Gets if this node has {@link MetaType} type data.
*
* @return true if this is a meta node.
*/
default boolean isMeta() {
return getTypeData(MetaType.KEY).isPresent();
}
/**
* Gets the meta entry if this node has {@link MetaType} type data,
* throwing an exception if the data is not present.
*
* @return the meta entry
* @throws IllegalStateException if this node doesn't have {@link MetaType} data
*/
default Map.@NonNull Entry getMeta() throws IllegalStateException {
return typeData(MetaType.KEY);
}
/**
* Gets if this node has {@link PrefixType} type data.
*
* @return true if this node is a prefix node
*/
default boolean isPrefix() {
return getTypeData(PrefixType.KEY).isPresent();
}
/**
* Gets the prefix entry if this node has {@link PrefixType} type data,
* throwing an exception if the data is not present.
*
* @return the meta entry
* @throws IllegalStateException if this node doesn't have {@link PrefixType} data
*/
default Map.@NonNull Entry getPrefix() throws IllegalStateException {
return typeData(PrefixType.KEY).getAsEntry();
}
/**
* Gets if this node has {@link SuffixType} type data.
*
* @return true if this node is a suffix node
*/
default boolean isSuffix() {
return getTypeData(SuffixType.KEY).isPresent();
}
/**
* Gets the suffix entry if this node has {@link SuffixType} type data,
* throwing an exception if the data is not present.
*
* @return the meta entry
* @throws IllegalStateException if this node doesn't have {@link SuffixType} data
*/
default Map.@NonNull Entry getSuffix() throws IllegalStateException {
return typeData(SuffixType.KEY).getAsEntry();
}
/**
* Gets if this Node is equal to another node.
*
* @param obj the other node
* @return true if this node is equal to the other provided
* @see StandardNodeEquality#EXACT
*/
@Override
boolean equals(Object obj);
/**
* Gets if this Node is equal to another node as defined by the given
* {@link NodeEqualityPredicate}.
*
* @param other the other node
* @param equalityPredicate the predicate
* @return true if this node is considered equal
* @since 4.1
*/
boolean equals(Node other, NodeEqualityPredicate equalityPredicate);
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the
* value.
*
* @param other the other node
* @return true if the two nodes are almost equal
* @see StandardNodeEquality#IGNORE_VALUE
* @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)}
*/
@Deprecated
default boolean equalsIgnoringValue(@NonNull Node other) {
return equals(other, StandardNodeEquality.IGNORE_VALUE);
}
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the
* expiry time or value.
*
* @param other the other node
* @return true if the two nodes are almost equal
* @see StandardNodeEquality#IGNORE_EXPIRY_TIME_AND_VALUE
* @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)}
*/
@Deprecated
default boolean almostEquals(@NonNull Node other) {
return equals(other, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
}
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the
* value or if the node is temporary.
*
* @param other the other node
* @return true if the two nodes are almost equal
* @see StandardNodeEquality#IGNORE_VALUE_OR_IF_TEMPORARY
* @since 2.8
* @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)}
*/
@Deprecated
default boolean equalsIgnoringValueOrTemp(@NonNull Node other) {
return equals(other, StandardNodeEquality.IGNORE_VALUE_OR_IF_TEMPORARY);
}
/**
* Constructs a new builder initially containing the current properties of
* this node.
*
* @return a new builder
* @since 4.1
*/
Builder toBuilder();
/**
* Builds a Node instance
*/
interface Builder {
/**
* Copies the attributes from the given node and applies them to this
* builder.
*
* Note that this copies all attributes except the
* permission itself.
*
* @param node the node to copy from
* @return the builder
* @since 4.2
*/
Builder copyFrom(@NonNull Node node);
/**
* Sets the value of negated for the node.
*
* @param negated the value
* @return the builder
* @see Node#isNegated()
*/
@NonNull Builder setNegated(boolean negated);
/**
* Sets the value of the node.
*
* @param value the value
* @return the builder
* @see Node#getValue()
*/
@NonNull Builder setValue(boolean value);
/**
* Sets the override property for the node.
*
* Warning: this value does not persist, and disappears when the holder is re-loaded.
* It is therefore only useful for transient nodes.
*
* @param override the override state
* @return the builder
* @see Node#isOverride()
*/
@NonNull Builder setOverride(boolean override);
/**
* Sets the time when the node should expire.
*
* The parameter passed to this method must be the unix timestamp
* (in seconds) when the node should expire.
*
* @param expiryUnixTimestamp the expiry timestamp (unix seconds)
* @return the builder
* @see Node#getExpiryUnixTime()
*/
@NonNull Builder setExpiry(long expiryUnixTimestamp);
/**
* Sets the time when the node should expire.
*
* The expiry timestamp is calculated relative to the current
* system time.
*
* @param duration how long the node should be added for
* @param unit the unit duration
is measured in
* @return the builder
* @since 4.2
*/
default @NonNull Builder setExpiry(long duration, TimeUnit unit) {
Preconditions.checkArgument(duration > 0, "duration must be positive");
long seconds = Objects.requireNonNull(unit, "unit").toSeconds(duration);
long timeNow = System.currentTimeMillis() / 1000L;
return setExpiry(timeNow + seconds);
}
/**
* Marks that the node being built should never expire.
*
* @return the builder
* @since 4.2
*/
@NonNull Builder clearExpiry();
/**
* Sets the world value for the node.
*
* @param world the world value
* @return the builder
* @see Node#getWorld()
*/
@NonNull Builder setWorld(@Nullable String world);
/**
* Sets the server value for the node.
*
* @param server the world value
* @return the builder
* @see Node#getServer()
*/
@NonNull Builder setServer(@Nullable String server);
/**
* Appends an extra context onto the node.
*
* @param key the context key
* @param value the context value
* @return the builder
* @see ContextSet
* @see Node#getContexts()
*/
@NonNull Builder withExtraContext(@NonNull String key, @NonNull String value);
/**
* Appends extra contexts onto the node.
*
* @param map a map of contexts
* @return the builder
* @see ContextSet
* @see Node#getContexts()
*/
@NonNull Builder withExtraContext(@NonNull Map map);
/**
* Appends extra contexts onto the node.
*
* @param context a set of contexts
* @return the builder
* @see ContextSet
* @see Node#getContexts()
*/
@NonNull Builder withExtraContext(@NonNull Set> context);
/**
* Appends an extra context onto the node.
*
* @param entry the context
* @return the builder
* @see ContextSet
* @see Node#getContexts()
*/
@NonNull Builder withExtraContext(Map.@NonNull Entry entry);
/**
* Appends extra contexts onto the node.
*
* @param contextSet a context set
* @return the builder
* @see ContextSet
* @see Node#getContexts()
*/
@NonNull Builder withExtraContext(@NonNull ContextSet contextSet);
/**
* Sets the extra contexts for the node.
*
* @param contextSet a context set
* @return the builder
* @see ContextSet
* @see Node#getContexts()
* @since 4.2
*/
@NonNull Builder setExtraContext(@NonNull ContextSet contextSet);
/**
* Creates a {@link Node} instance from the builder.
*
* @return a new node instance
*/
@NonNull Node build();
}
}