Skip to content

Commit

Permalink
feat: PermissionContainer improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
phinner committed Dec 2, 2024
1 parent e744a30 commit b598759
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ tasks.withType<JavaCompile> {
options.compilerArgs.add("-Xlint:-serial")
options.errorprone {
disableWarningsInGeneratedCode.set(true)
disable("MissingSummary", "FutureReturnValueIgnored", "InlineMeSuggester", "EmptyCatch")
disable("FutureReturnValueIgnored", "InlineMeSuggester", "EmptyCatch")
check("NullAway", if (name.contains("test", ignoreCase = true)) CheckSeverity.OFF else CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", "com.xpdustry.distributor")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,35 @@ default void setPermission(final String permission, final boolean state) {
setPermission(permission, state, false);
}

/**
* Sets the permission with the given state.
*
* @param permission the permission
* @param state the state
*/
default void setPermission(final String permission, final TriState state) {
setPermission(permission, state, false);
}

/**
* Sets the permission with the given state.
*
* @param permission the permission
* @param state the state
* @param override whether to override the child permissions with the new state
*/
void setPermission(final String permission, final boolean state, final boolean override);
default void setPermission(final String permission, final boolean state, final boolean override) {
setPermission(permission, TriState.of(state), override);
}

/**
* Sets the permission with the given state.
*
* @param permission the permission
* @param state the state
* @param override whether to override the child permissions with the new state
*/
void setPermission(final String permission, final TriState state, final boolean override);

/**
* Sets the permissions from the given permission tree.
Expand All @@ -71,28 +92,4 @@ default void setPermissions(final PermissionTree tree, final boolean override) {
setPermission(entry.getKey(), entry.getValue(), override);
}
}

/**
* Removes the given permission from the tree. Essentially making it {@link TriState#UNDEFINED}.
*
* @param permission the permission
*/
default void removePermission(final String permission) {
removePermission(permission, false);
}

/**
* Removes the given permission from the tree. Essentially making it {@link TriState#UNDEFINED}.
*
* @param permission the permission
* @param all whether to remove all child permissions
*/
void removePermission(final String permission, final boolean all);

/**
* Clears all permissions.
*/
default void clearPermissions() {
removePermission("*", true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
import com.xpdustry.distributor.api.util.TriState;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;

final class MutablePermissionTreeImpl implements MutablePermissionTree {

private final @Nullable MutablePermissionTreeImpl parent;
private final Map<String, MutablePermissionTreeImpl> children = new HashMap<>();
final @Nullable MutablePermissionTreeImpl parent;
final Map<String, MutablePermissionTreeImpl> children = new HashMap<>();
private TriState value = TriState.UNDEFINED;

MutablePermissionTreeImpl() {
Expand All @@ -40,7 +41,8 @@ private MutablePermissionTreeImpl(final @Nullable MutablePermissionTreeImpl pare
}

@Override
public TriState getPermission(final String permission) {
public TriState getPermission(String permission) {
permission = permission.toLowerCase(Locale.ROOT);
checkPermission(permission);
var state = TriState.UNDEFINED;
var node = this;
Expand Down Expand Up @@ -74,68 +76,59 @@ public Map<String, Boolean> getPermissions() {
}

@Override
public void setPermission(final String permission, final boolean state, final boolean override) {
public void setPermission(String permission, final TriState state, boolean override) {
permission = permission.toLowerCase(Locale.ROOT);
checkPermission(permission);
final var parts = permission.split("\\.", -1);
var node = this;
for (final var part : parts) {
final var parent = node;
node = node.children.computeIfAbsent(part, k -> new MutablePermissionTreeImpl(parent));
}
node.value = TriState.of(state);
if (override) {
node.children.clear();
}
}

@Override
public void removePermission(final String permission, final boolean all) {
checkPermission(permission);
final var parts = permission.split("\\.", -1);
var node = this;
for (final var part : parts) {
node = node.children.get(part);
if (node == null) {
return;
if (state != TriState.UNDEFINED) {
for (final var part : parts) {
final var parent = node;
node = node.children.computeIfAbsent(part, k -> new MutablePermissionTreeImpl(parent));
}
node.value = state;
if (override) {
node.children.clear();
}
} else {
for (final var part : parts) {
node = node.children.get(part);
if (node == null) {
return;
}
}
node.value = TriState.UNDEFINED;
if (override) {
node.children.clear();
}
var index = parts.length - 1;
while (node.parent != null && node.children.isEmpty()) {
node = node.parent;
node.children.remove(parts[index--]);
}
}
node.value = TriState.UNDEFINED;
if (all) {
node.children.clear();
}
var index = parts.length - 1;
while (node.parent != null && node.children.isEmpty()) {
node = node.parent;
node.children.remove(parts[index--]);
}
}

@Override
public void clearPermissions() {
this.children.clear();
}

@Override
public boolean equals(final @Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof final MutablePermissionTreeImpl that)) {
return false;
}
if (!this.children.equals(that.children)) {
return false;
}
return this.value == that.value;
return this == o
|| (o instanceof MutablePermissionTreeImpl that
&& this.children.equals(that.children)
&& this.value == that.value);
}

@Override
public int hashCode() {
return Objects.hash(this.children, this.value);
}

@Override
public String toString() {
return "MutablePermissionTreeImpl{children=" + this.children + ", value=" + this.value + '}';
}

private void checkPermission(final String permission) {
if (!PERMISSION_PATTERN.matcher(permission).matches()) {
if (!PermissionContainer.isValidPermission(permission)) {
throw new IllegalArgumentException("The permission is not valid: " + permission);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package com.xpdustry.distributor.api.permission;

import com.xpdustry.distributor.api.util.TriState;
import java.util.regex.Pattern;

/**
* A generic permission container.
Expand All @@ -28,12 +27,12 @@
public interface PermissionContainer {

/**
* Regex pattern used to validate permission strings.
* A method used to validate permission strings.
* <p>
* <strong>Notes:</strong>
* <blockquote>
* Permission strings are composed of a series of nodes separated by dots with alphanumeric characters and minus
* signs, such as {@code "plugin.command"}.
* Permission strings are composed of a series of nodes separated by dots with alphanumeric characters, underscores and minus
* signs, such as {@code "plugin.my-command"}, {@code "some_value}, etc.
* <br>
* A parent node is always overridden by a child node, such as {@code "plugin.command"} overriding {@code "plugin"}.
* <br>
Expand All @@ -43,7 +42,25 @@ public interface PermissionContainer {
* It allows you to set a default value for all permissions.
* </blockquote>
*/
Pattern PERMISSION_PATTERN = Pattern.compile("^(\\*|\\w+)(\\.(\\*|\\w+))*$");
static boolean isValidPermission(final String permission) {
var wildcard = false;
var last = '.';
for (int i = 0; i < permission.length(); i++) {
final var ch = permission.charAt(i);
if (ch == '*') {
if (wildcard || last != '.') return false;
wildcard = true;
} else if (ch == '.') {
if (wildcard || last == '.') return false;
} else if (Character.isLetterOrDigit(ch) || ch == '-' || ch == '_') {
if (wildcard) return false;
} else {
return false;
}
last = ch;
}
return last != '.';
}

/**
* Returns an empty permission container. Always returns {@link TriState#UNDEFINED}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static PermissionTree empty() {
* @param tree the tree
* @return the immutable tree
*/
static PermissionTree from(final PermissionTree tree) {
static PermissionTree immutable(final PermissionTree tree) {
if (tree instanceof ImmutablePermissionTree) return tree;
final var mutable = MutablePermissionTree.create();
mutable.setPermissions(tree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,19 @@
import mindustry.gen.Player;
import mindustry.net.Administration;

/**
* A simple player lookup implementation. Supporting {@link Field#ENTITY_ID}, {@link Field#UUID}, and {@link Field#NAME}.
*/
public class SimplePlayerLookup implements PlayerLookup {

@Override
public List<Player> findOnlinePlayers(final Collection<Player> players, final Query query) {
final List<Player> result = new ArrayList<>();
this.findOnlinePlayers0(players, query, result);
this.findOnlinePlayers(players, query, result);
return Collections.unmodifiableList(result);
}

protected void findOnlinePlayers0(final Collection<Player> players, final Query query, final List<Player> result) {
protected void findOnlinePlayers(final Collection<Player> players, final Query query, final List<Player> result) {
if (query.getFields().contains(Field.ENTITY_ID) && query.getInput().startsWith("#")) {
try {
final var id = Integer.parseInt(query.getInput().substring(1));
Expand Down

This file was deleted.

Loading

0 comments on commit b598759

Please sign in to comment.