diff --git a/api/src/main/java/me/lucko/luckperms/api/Node.java b/api/src/main/java/me/lucko/luckperms/api/Node.java index 38af3a2b..f6bb02d7 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Node.java +++ b/api/src/main/java/me/lucko/luckperms/api/Node.java @@ -318,39 +318,81 @@ public interface Node extends Map.Entry { Map.Entry getSuffix() throws IllegalStateException; /** - * Checks if this Node is equal to another node + * Returns 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 #equalsIgnoringValue(Node) for a less strict implementation of this method + * @see StandardNodeEquality#EXACT */ @Override boolean equals(Object obj); /** - * Similar to {@link Node#equals(Object)}, except doesn't take note of the value + * Returns if this Node is equal to another node as defined by the given + * {@link StandardNodeEquality} predicate. + * + * @param other the other node + * @param equalityPredicate the predicate + * @return true if this node is considered equal + * @since 4.1 + */ + boolean standardEquals(Node other, StandardNodeEquality equalityPredicate); + + /** + * Returns 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 + */ + default boolean equals(Node other, NodeEqualityPredicate equalityPredicate) { + return equalityPredicate.areEqual(this, other); + } + + /** + * 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 + * @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)} + * @see StandardNodeEquality#IGNORE_VALUE */ - boolean equalsIgnoringValue(@Nonnull Node other); + @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 + * 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 + * @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)} + * @see StandardNodeEquality#IGNORE_EXPIRY_TIME_AND_VALUE */ - boolean almostEquals(@Nonnull Node other); + @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 + * 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 * @since 2.8 + * @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)} + * @see StandardNodeEquality#IGNORE_VALUE_OR_IF_TEMPORARY */ - boolean equalsIgnoringValueOrTemp(@Nonnull Node other); + @Deprecated + default boolean equalsIgnoringValueOrTemp(@Nonnull Node other) { + return equals(other, StandardNodeEquality.IGNORE_VALUE_OR_IF_TEMPORARY); + } /** * Builds a Node instance diff --git a/api/src/main/java/me/lucko/luckperms/api/NodeEqualityPredicate.java b/api/src/main/java/me/lucko/luckperms/api/NodeEqualityPredicate.java new file mode 100644 index 00000000..0312bf12 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/NodeEqualityPredicate.java @@ -0,0 +1,53 @@ +/* + * 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 javax.annotation.Nonnull; + +/** + * A rule for determining if two nodes are equal. + * + *

Generally, individual instances of this interface should fulfil the same + * requirements as the {@link Object#equals(Object)} contract.

+ * + *

Some standard implementations are provided by + * {@link StandardNodeEquality}.

+ * + * @since 4.1 + */ +@FunctionalInterface +public interface NodeEqualityPredicate { + + /** + * Returns if the two nodes are equal + * + * @param o1 the first node + * @param o2 the second node + * @return true if equal + */ + boolean areEqual(@Nonnull Node o1, @Nonnull Node o2); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java index 18892079..5572a1f4 100644 --- a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java +++ b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java @@ -271,6 +271,42 @@ public interface PermissionHolder { */ void auditTemporaryPermissions(); + /** + * Checks to see if the object has a certain permission + * + * @param node the node to check for + * @param equalityPredicate how to determine if a node matches + * @return a Tristate for the holders permission status for the node + * @throws NullPointerException if the node is null + * @since 4.1 + */ + @Nonnull + Tristate hasPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate); + + /** + * Checks to see if the object has a certain permission + * + * @param node the node to check for + * @param equalityPredicate how to determine if a node matches + * @return a Tristate for the holders permission status for the node + * @throws NullPointerException if the node is null + * @since 4.1 + */ + @Nonnull + Tristate hasTransientPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate); + + /** + * Checks to see if the object inherits a certain permission + * + * @param node the node to check for + * @param equalityPredicate how to determine if a node matches + * @return a Tristate for the holders inheritance status for the node + * @throws NullPointerException if the node is null + * @since 4.1 + */ + @Nonnull + Tristate inheritsPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate); + /** * Checks to see if the object has a certain permission * diff --git a/api/src/main/java/me/lucko/luckperms/api/StandardNodeEquality.java b/api/src/main/java/me/lucko/luckperms/api/StandardNodeEquality.java new file mode 100644 index 00000000..b84b32ab --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/StandardNodeEquality.java @@ -0,0 +1,78 @@ +/* + * 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 javax.annotation.Nonnull; + +/** + * Standard {@link NodeEqualityPredicate}s. + * + * @since 4.1 + */ +public enum StandardNodeEquality implements NodeEqualityPredicate { + + /** + * Represents an exact match. + * + *

All attributes of the nodes must match for them to be considered + * equal.

+ */ + EXACT, + + /** + * All attributes must match, except for + * {@link Node#getValuePrimitive() value}, which is ignored. + */ + IGNORE_VALUE, + + /** + * All attributes must match, except for the + * {@link Node#getExpiry() expiry time}, which is ignored. + * + *

Note that with this setting, whether a node is temporary or not is + * still considered.

+ */ + IGNORE_EXPIRY_TIME, + + /** + * All attributes must match, except for + * {@link Node#getValuePrimitive() value} and the + * {@link Node#getExpiry() expiry time}, which are ignored. + */ + IGNORE_EXPIRY_TIME_AND_VALUE, + + /** + * All attributes must match, except for + * {@link Node#getValuePrimitive() value} and the if the node is + * {@link Node#isTemporary() temporary}, which are ignored. + */ + IGNORE_VALUE_OR_IF_TEMPORARY; + + @Override + public boolean areEqual(@Nonnull Node o1, @Nonnull Node o2) { + return o1.standardEquals(o2, this); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java index 88e9ff22..589f7723 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java @@ -35,6 +35,7 @@ import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.DataMutateResult; import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.NodeEqualityPredicate; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.caching.CachedData; import me.lucko.luckperms.api.context.ContextSet; @@ -158,18 +159,42 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol return ImmutableMap.copyOf(this.handle.exportNodesAndShorthand(contexts, lowerCase)); } + @Nonnull + @Override + public Tristate hasPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate) { + Objects.requireNonNull(node, "node"); + Objects.requireNonNull(equalityPredicate, "equalityPredicate"); + return this.handle.hasPermission(NodeMapType.ENDURING, node, equalityPredicate); + } + + @Nonnull + @Override + public Tristate hasTransientPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate) { + Objects.requireNonNull(node, "node"); + Objects.requireNonNull(equalityPredicate, "equalityPredicate"); + return this.handle.hasPermission(NodeMapType.TRANSIENT, node, equalityPredicate); + } + + @Nonnull + @Override + public Tristate inheritsPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate) { + Objects.requireNonNull(node, "node"); + Objects.requireNonNull(equalityPredicate, "equalityPredicate"); + return this.handle.inheritsPermission(node, equalityPredicate); + } + @Nonnull @Override public Tristate hasPermission(@Nonnull Node node) { Objects.requireNonNull(node, "node"); - return this.handle.hasPermission(node, NodeMapType.ENDURING); + return this.handle.hasPermission(NodeMapType.ENDURING, node); } @Nonnull @Override public Tristate hasTransientPermission(@Nonnull Node node) { Objects.requireNonNull(node, "node"); - return this.handle.hasPermission(node, NodeMapType.TRANSIENT); + return this.handle.hasPermission(NodeMapType.TRANSIENT, node); } @Nonnull diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheck.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheck.java index 128ab22e..3724906a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheck.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheck.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.common.commands.impl.generic.permission; +import me.lucko.luckperms.api.StandardNodeEquality; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.common.commands.ArgumentPermissions; @@ -62,7 +63,7 @@ public class PermissionCheck extends SharedSubCommand { String node = ArgumentUtils.handleString(0, args); MutableContextSet context = ArgumentUtils.handleContext(1, args, plugin); - Tristate result = holder.hasPermission(NodeFactory.builder(node).withExtraContext(context).build()); + Tristate result = holder.hasPermission(NodeFactory.builder(node).withExtraContext(context).build(), StandardNodeEquality.IGNORE_VALUE_OR_IF_TEMPORARY); String s = CommandUtils.formatTristate(result); Message.CHECK_PERMISSION.send(sender, holder.getFriendlyName(), node, s, CommandUtils.contextSetToString(context)); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheckInherits.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheckInherits.java index 88906b37..70a7a379 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheckInherits.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionCheckInherits.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.common.commands.impl.generic.permission; +import me.lucko.luckperms.api.StandardNodeEquality; import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.common.commands.ArgumentPermissions; import me.lucko.luckperms.common.commands.CommandException; @@ -62,7 +63,7 @@ public class PermissionCheckInherits extends SharedSubCommand { String node = ArgumentUtils.handleString(0, args); MutableContextSet context = ArgumentUtils.handleContext(1, args, plugin); - InheritanceInfo result = holder.inheritsPermissionInfo(NodeFactory.builder(node).withExtraContext(context).build()); + InheritanceInfo result = holder.searchForInheritedMatch(NodeFactory.builder(node).withExtraContext(context).build(), StandardNodeEquality.IGNORE_VALUE_OR_IF_TEMPORARY); String location = result.getLocation().orElse(null); if (location == null || location.equalsIgnoreCase(holder.getObjectName())) { diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 356e57bb..5f33ba29 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -35,6 +35,8 @@ import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.DataMutateResult; import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.NodeEqualityPredicate; +import me.lucko.luckperms.api.StandardNodeEquality; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; @@ -578,44 +580,53 @@ public abstract class PermissionHolder { return transientWork || enduringWork; } - private Optional getAlmostEquals(Node node, NodeMapType type) { + private Optional searchForMatch(NodeMapType type, Node node, NodeEqualityPredicate equalityPredicate) { for (Node n : getData(type).immutable().values()) { - if (n.almostEquals(node)) { + if (n.equals(node, equalityPredicate)) { return Optional.of(n); } } - return Optional.empty(); } /** * Check if the holder has a permission node * - * @param node the node to check * @param type which backing map to check - * @return a tristate + * @param node the node to check + * @param equalityPredicate how to match + * @return a tristate, returns undefined if no match */ - public Tristate hasPermission(Node node, NodeMapType type) { + public Tristate hasPermission(NodeMapType type, Node node, NodeEqualityPredicate equalityPredicate) { if (this.getType().isGroup() && node.isGroupNode() && node.getGroupName().equalsIgnoreCase(getObjectName())) { return Tristate.TRUE; } - return getAlmostEquals(node, type).map(Node::getTristate).orElse(Tristate.UNDEFINED); + return searchForMatch(type, node, equalityPredicate).map(Node::getTristate).orElse(Tristate.UNDEFINED); + } + + public Tristate hasPermission(NodeMapType type, Node node) { + return hasPermission(type, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE); + } + + public Tristate hasPermission(Node node, NodeEqualityPredicate equalityPredicate) { + return hasPermission(NodeMapType.ENDURING, node, equalityPredicate); } public Tristate hasPermission(Node node) { - return hasPermission(node, NodeMapType.ENDURING); + return hasPermission(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE); } /** * Check if the holder inherits a node * * @param node the node to check + * @param equalityPredicate how to match * @return the result of the lookup */ - public InheritanceInfo inheritsPermissionInfo(Node node) { + public InheritanceInfo searchForInheritedMatch(Node node, NodeEqualityPredicate equalityPredicate) { for (LocalizedNode n : resolveInheritances()) { - if (n.getNode().almostEquals(node)) { + if (n.getNode().equals(node, equalityPredicate)) { return InheritanceInfo.of(n); } } @@ -627,10 +638,15 @@ public abstract class PermissionHolder { * Check if the holder inherits a node * * @param node the node to check + * @param equalityPredicate how to match * @return the Tristate result */ + public Tristate inheritsPermission(Node node, NodeEqualityPredicate equalityPredicate) { + return searchForInheritedMatch(node, equalityPredicate).getResult(); + } + public Tristate inheritsPermission(Node node) { - return inheritsPermissionInfo(node).getResult(); + return inheritsPermission(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE); } /** @@ -639,7 +655,7 @@ public abstract class PermissionHolder { * @param node the node to set */ public DataMutateResult setPermission(Node node) { - if (hasPermission(node, NodeMapType.ENDURING) != Tristate.UNDEFINED) { + if (hasPermission(NodeMapType.ENDURING, node) != Tristate.UNDEFINED) { return DataMutateResult.ALREADY_HAS; } @@ -663,7 +679,7 @@ public abstract class PermissionHolder { if (node.isTemporary()) { if (modifier == TemporaryModifier.ACCUMULATE) { // Try to accumulate with an existing node - Optional existing = getAlmostEquals(node, NodeMapType.ENDURING); + Optional existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE); // An existing node was found if (existing.isPresent()) { @@ -684,7 +700,7 @@ public abstract class PermissionHolder { } else if (modifier == TemporaryModifier.REPLACE) { // Try to replace an existing node - Optional existing = getAlmostEquals(node, NodeMapType.ENDURING); + Optional existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE); // An existing node was found if (existing.isPresent()) { @@ -717,7 +733,7 @@ public abstract class PermissionHolder { * @param node the node to set */ public DataMutateResult setTransientPermission(Node node) { - if (hasPermission(node, NodeMapType.TRANSIENT) != Tristate.UNDEFINED) { + if (hasPermission(NodeMapType.TRANSIENT, node) != Tristate.UNDEFINED) { return DataMutateResult.ALREADY_HAS; } @@ -732,7 +748,7 @@ public abstract class PermissionHolder { * @param node the node to unset */ public DataMutateResult unsetPermission(Node node) { - if (hasPermission(node, NodeMapType.ENDURING) == Tristate.UNDEFINED) { + if (hasPermission(NodeMapType.ENDURING, node) == Tristate.UNDEFINED) { return DataMutateResult.LACKS; } @@ -751,7 +767,7 @@ public abstract class PermissionHolder { * @param node the node to unset */ public DataMutateResult unsetTransientPermission(Node node) { - if (hasPermission(node, NodeMapType.TRANSIENT) == Tristate.UNDEFINED) { + if (hasPermission(NodeMapType.TRANSIENT, node) == Tristate.UNDEFINED) { return DataMutateResult.LACKS; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ForwardingNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ForwardingNode.java index 4a943638..74c859b4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/ForwardingNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ForwardingNode.java @@ -26,6 +26,7 @@ package me.lucko.luckperms.common.node; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.StandardNodeEquality; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.ContextSet; @@ -224,18 +225,8 @@ public abstract class ForwardingNode implements Node { } @Override - public boolean equalsIgnoringValue(@Nonnull Node other) { - return delegate().equalsIgnoringValue(other); - } - - @Override - public boolean almostEquals(@Nonnull Node other) { - return delegate().almostEquals(other); - } - - @Override - public boolean equalsIgnoringValueOrTemp(@Nonnull Node other) { - return delegate().equalsIgnoringValueOrTemp(other); + public boolean standardEquals(Node other, StandardNodeEquality equalityPredicate) { + return delegate().standardEquals(other, equalityPredicate); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java index f390cc19..4b3e3c35 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java @@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.StandardNodeEquality; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet; @@ -320,27 +321,41 @@ public final class ImmutableNode implements Node { return this.resolvedShorthand; } - @SuppressWarnings("StringEquality") @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Node)) return false; - final Node other = (Node) o; - if (this.permission != other.getPermission()) return false; - if (this.value != other.getValuePrimitive()) return false; - if (this.override != other.isOverride()) return false; + Node other = (Node) o; + while (other instanceof ForwardingNode) { + other = ((ForwardingNode) other).delegate(); + } + return other instanceof ImmutableNode && Equality.EXACT.areEqual(this, (ImmutableNode) other); + } - final String thisServer = this.server; - final String otherServer = other.getServer().orElse(null); - if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; - - final String thisWorld = this.world; - final String otherWorld = other.getWorld().orElse(null); - if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false; - - final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L; - return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts()); + @Override + public boolean standardEquals(Node o, StandardNodeEquality equalityPredicate) { + while (o instanceof ForwardingNode) { + o = ((ForwardingNode) o).delegate(); + } + if (!(o instanceof ImmutableNode)) { + return false; + } + ImmutableNode other = (ImmutableNode) o; + switch (equalityPredicate) { + case EXACT: + return Equality.EXACT.areEqual(this, other); + case IGNORE_VALUE: + return Equality.IGNORE_VALUE.areEqual(this, other); + case IGNORE_EXPIRY_TIME: + return Equality.IGNORE_EXPIRY_TIME.areEqual(this, other); + case IGNORE_EXPIRY_TIME_AND_VALUE: + return Equality.IGNORE_EXPIRY_TIME_AND_VALUE.areEqual(this, other); + case IGNORE_VALUE_OR_IF_TEMPORARY: + return Equality.IGNORE_VALUE_OR_IF_TEMPORARY.areEqual(this, other); + default: + throw new AssertionError(); + } } @Override @@ -362,57 +377,70 @@ public final class ImmutableNode implements Node { } @SuppressWarnings("StringEquality") - @Override - public boolean equalsIgnoringValue(@Nonnull Node other) { - if (this.permission != other.getPermission()) return false; - if (this.override != other.isOverride()) return false; + private enum Equality { + EXACT { + @Override + public boolean areEqual(@Nonnull ImmutableNode o1, @Nonnull ImmutableNode o2) { + return o1 == o2 || + o1.permission == o2.permission && + o1.value == o2.value && + o1.override == o2.override && + (o1.server == null ? o2.server == null : o1.server.equals(o2.server)) && + (o1.world == null ? o2.world == null : o1.world.equals(o2.world)) && + o1.expireAt == o2.expireAt && + o1.getContexts().equals(o2.getContexts()); + } + }, + IGNORE_VALUE { + @Override + public boolean areEqual(@Nonnull ImmutableNode o1, @Nonnull ImmutableNode o2) { + return o1 == o2 || + o1.permission == o2.permission && + o1.override == o2.override && + (o1.server == null ? o2.server == null : o1.server.equals(o2.server)) && + (o1.world == null ? o2.world == null : o1.world.equals(o2.world)) && + o1.expireAt == o2.expireAt && + o1.getContexts().equals(o2.getContexts()); + } + }, + IGNORE_EXPIRY_TIME { + @Override + public boolean areEqual(@Nonnull ImmutableNode o1, @Nonnull ImmutableNode o2) { + return o1 == o2 || + o1.permission == o2.permission && + o1.value == o2.value && + o1.override == o2.override && + (o1.server == null ? o2.server == null : o1.server.equals(o2.server)) && + (o1.world == null ? o2.world == null : o1.world.equals(o2.world)) && + o1.isTemporary() == o2.isTemporary() && + o1.getContexts().equals(o2.getContexts()); + } + }, + IGNORE_EXPIRY_TIME_AND_VALUE { + @Override + public boolean areEqual(@Nonnull ImmutableNode o1, @Nonnull ImmutableNode o2) { + return o1 == o2 || + o1.permission == o2.permission && + o1.override == o2.override && + (o1.server == null ? o2.server == null : o1.server.equals(o2.server)) && + (o1.world == null ? o2.world == null : o1.world.equals(o2.world)) && + o1.isTemporary() == o2.isTemporary() && + o1.getContexts().equals(o2.getContexts()); + } + }, + IGNORE_VALUE_OR_IF_TEMPORARY { + @Override + public boolean areEqual(@Nonnull ImmutableNode o1, @Nonnull ImmutableNode o2) { + return o1 == o2 || + o1.permission == o2.permission && + o1.override == o2.override && + (o1.server == null ? o2.server == null : o1.server.equals(o2.server)) && + (o1.world == null ? o2.world == null : o1.world.equals(o2.world)) && + o1.getContexts().equals(o2.getContexts()); + } + }; - final String thisServer = this.server; - final String otherServer = other.getServer().orElse(null); - if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; - - final String thisWorld = this.world; - final String otherWorld = other.getWorld().orElse(null); - if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false; - - final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L; - return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts()); - } - - @SuppressWarnings("StringEquality") - @Override - public boolean almostEquals(@Nonnull Node other) { - if (this.permission != other.getPermission()) return false; - if (this.override != other.isOverride()) return false; - - final String thisServer = this.server; - final String otherServer = other.getServer().orElse(null); - if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) - return false; - - final String thisWorld = this.world; - final String otherWorld = other.getWorld().orElse(null); - return (thisWorld == null ? otherWorld == null : thisWorld.equals(otherWorld)) && - this.isTemporary() == other.isTemporary() && - this.getContexts().equals(other.getContexts()); - - } - - @SuppressWarnings("StringEquality") - @Override - public boolean equalsIgnoringValueOrTemp(@Nonnull Node other) { - if (this.permission != other.getPermission()) return false; - if (this.override != other.isOverride()) return false; - - final String thisServer = this.server; - final String otherServer = other.getServer().orElse(null); - if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) - return false; - - final String thisWorld = this.world; - final String otherWorld = other.getWorld().orElse(null); - return (thisWorld == null ? otherWorld == null : thisWorld.equals(otherWorld)) && - this.getContexts().equals(other.getContexts()); + public abstract boolean areEqual(@Nonnull ImmutableNode o1, @Nonnull ImmutableNode o2); } @Override @@ -443,6 +471,13 @@ public final class ImmutableNode implements Node { @Override public String toString() { - return "ImmutableNode(permission=" + this.permission + ", value=" + this.value + ", override=" + this.override + ", server=" + this.getServer() + ", world=" + this.getWorld() + ", expireAt=" + this.expireAt + ", contexts=" + this.contexts + ")"; + return "ImmutableNode(" + + "permission=" + this.permission + ", " + + "value=" + this.value + ", " + + "override=" + this.override + ", " + + "server=" + this.getServer() + ", " + + "world=" + this.getWorld() + ", " + + "expireAt=" + this.expireAt + ", " + + "contexts=" + this.contexts + ")"; } }