summaryrefslogtreecommitdiff
path: root/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java
diff options
context:
space:
mode:
Diffstat (limited to 'dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java')
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java322
1 files changed, 322 insertions, 0 deletions
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java
new file mode 100644
index 0000000..a60c34e
--- /dev/null
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java
@@ -0,0 +1,322 @@
+package io.dico.dicore.command;
+
+import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
+import org.bukkit.command.CommandSender;
+
+import java.util.List;
+import java.util.Objects;
+
+public interface IContextFilter extends Comparable<IContextFilter> {
+
+ /**
+ * Filter the given context by this filter's criteria.
+ * If the context does not match the criteria, an exception is thrown describing the problem.
+ *
+ * @param context the context to match
+ * @throws CommandException if it doesn't match
+ */
+ void filterContext(ExecutionContext context) throws CommandException;
+
+ /**
+ * Filter an execution context for a direct or indirect sub command of the command that registered this filter.
+ *
+ * @param subContext the context for the execution
+ * @param path the path traversed from the command that registered this filter to the executed command
+ */
+ default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
+ filterContext(subContext);
+ }
+
+ /**
+ * Get the priority of this context filter.
+ * The priorities determine the order in which a command's context filters are executed.
+ *
+ * @return the priority
+ */
+ Priority getPriority();
+
+ default boolean allowsContext(ExecutionContext context) {
+ try {
+ filterContext(context);
+ return true;
+ } catch (CommandException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()}
+ *
+ * @param o compared filter
+ * @return comparison value
+ */
+ @Override
+ default int compareTo(IContextFilter o) {
+ return getPriority().compareTo(o.getPriority());
+ }
+
+ default boolean isInheritable() {
+ return false;
+ }
+
+ default IContextFilter inherit(String... components) {
+ if (!isInheritable()) {
+ throw new IllegalStateException("This IContextFilter cannot be inherited");
+ }
+
+ return this;
+ }
+
+ /**
+ * IContextFilter priorities. Executes from top to bottom.
+ */
+ enum Priority {
+ /**
+ * This priority should have checks on the sender type.
+ * Any filters on this priority are tested before permissions are.
+ * This is the highest priority.
+ */
+ VERY_EARLY, // sender type check
+
+ /**
+ * This priority is specific to permissions.
+ */
+ PERMISSION,
+
+ /**
+ * Early priority. Post permissions, pre parameter-parsing.
+ */
+ EARLY,
+
+ /**
+ * Normal priority. Post permissions, pre parameter-parsing.
+ */
+ NORMAL,
+
+ /**
+ * Late priority. Post permissions, pre parameter-parsing.
+ */
+ LATE,
+
+ /**
+ * Very late priority. Post permissions, pre parameter-parsing.
+ */
+ VERY_LATE,
+
+ /**
+ * Post parameters priority. Post permissions, post parameter-parsing.
+ * This is the lowest priority.
+ */
+ POST_PARAMETERS;
+
+ /**
+ * Get the context filter that inherits context filters from the parent of the same priority.
+ * If this filter is also present at the parent, it will do the same for the parent's parent, and so on.
+ *
+ * @return the inheritor
+ */
+ public IContextFilter getInheritor() {
+ return inheritor;
+ }
+
+ private static String[] addParent(String[] path, String parent) {
+ String[] out = new String[path.length + 1];
+ System.arraycopy(path, 0, out, 0, path.length);
+ out[0] = parent;
+ return out;
+ }
+
+ final IContextFilter inheritor = new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ ICommandAddress address = context.getAddress();
+
+ String[] traversedPath = new String[0];
+ do {
+ traversedPath = addParent(traversedPath, address.getMainKey());
+ address = address.getParent();
+
+ if (address != null && address.hasCommand()) {
+ boolean doBreak = true;
+
+ Command command = address.getCommand();
+ List<IContextFilter> contextFilterList = command.getContextFilters();
+ for (IContextFilter filter : contextFilterList) {
+ if (filter.getPriority() == Priority.this) {
+ if (filter == this) {
+ // do the same for next parent
+ // this method is necessary to keep traversedPath information
+ doBreak = false;
+ } else {
+ filter.filterSubContext(context, traversedPath);
+ }
+ }
+ }
+
+ if (doBreak) {
+ break;
+ }
+ }
+ } while (address != null);
+ }
+
+ @Override
+ public Priority getPriority() {
+ return Priority.this;
+ }
+ };
+
+ }
+
+ /**
+ * Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command.
+ */
+ IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer);
+
+ /**
+ * Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command.
+ */
+ IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole);
+
+ /**
+ * This filter is not working as intended.
+ * <p>
+ * There is supposed to be a permission filter that takes a base, and appends the command's address to the base, and checks that permission.
+ */
+ IContextFilter INHERIT_PERMISSIONS = Priority.PERMISSION.getInheritor();
+
+ static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable<? extends CommandException> runnable) {
+ return new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ runnable.checkedRun();
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+ static IContextFilter filterSender(Priority priority, CheckedConsumer<? super CommandSender, ? extends CommandException> consumer) {
+ return new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ consumer.checkedAccept(context.getSender());
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+ static IContextFilter permission(String permission) {
+ Objects.requireNonNull(permission);
+ return filterSender(Priority.PERMISSION, sender -> Validate.isAuthorized(sender, permission));
+ }
+
+ static IContextFilter permission(String permission, String failMessage) {
+ Objects.requireNonNull(permission);
+ Objects.requireNonNull(failMessage);
+ return filterSender(Priority.PERMISSION, sender -> Validate.isAuthorized(sender, permission, failMessage));
+ }
+
+ /**
+ * Produce an inheritable permission context filter.
+ * A permission component is an element in {@code permission.split("\\.")}
+ *
+ * @param permission The permission that is required for the command that this is directly assigned to
+ * @param componentInsertionIndex the index where any sub-components are inserted. -1 for "at the end".
+ * @param failMessage the message to send if the permission is not met
+ * @return the context filter
+ * @throws IllegalArgumentException if componentInsertionIndex is out of range
+ */
+ static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) {
+ Objects.requireNonNull(permission);
+ Objects.requireNonNull(failMessage);
+ if (componentInsertionIndex > permission.split("\\.").length || componentInsertionIndex < -1) {
+ throw new IllegalArgumentException("componentInsertionIndex out of range");
+ }
+
+
+ return new IContextFilter() {
+ private String getInheritedPermission(String[] components) {
+ int insertedAmount = components.length;
+ String[] currentComponents = permission.split("\\.");
+ int currentAmount = currentComponents.length;
+ String[] targetArray = new String[currentAmount + insertedAmount];
+
+ int insertionIndex;
+ //int newInsertionIndex;
+ if (componentInsertionIndex == -1) {
+ insertionIndex = currentAmount;
+ //newInsertionIndex = -1;
+ } else {
+ insertionIndex = componentInsertionIndex;
+ //newInsertionIndex = insertionIndex + insertedAmount;
+ }
+
+ // copy the current components up to insertionIndex
+ System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex);
+ // copy the new components into the array at insertionIndex
+ System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount);
+ // copy the current components from insertionIndex + inserted amount
+ System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex);
+
+ return String.join(".", targetArray);
+ }
+
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ Validate.isAuthorized(context.getSender(), permission, failMessage);
+ }
+
+ @Override
+ public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
+ Validate.isAuthorized(subContext.getSender(), getInheritedPermission(path), failMessage);
+ }
+
+ @Override
+ public Priority getPriority() {
+ return Priority.PERMISSION;
+ }
+
+ @Override
+ public boolean isInheritable() {
+ return true;
+ }
+
+ @Override
+ public IContextFilter inherit(String... components) {
+ int insertedAmount = components.length;
+ String[] currentComponents = permission.split("\\.");
+ int currentAmount = currentComponents.length;
+ String[] targetArray = new String[currentAmount + insertedAmount];
+
+ int insertionIndex;
+ int newInsertionIndex;
+ if (componentInsertionIndex == -1) {
+ insertionIndex = currentAmount;
+ newInsertionIndex = -1;
+ } else {
+ insertionIndex = componentInsertionIndex;
+ newInsertionIndex = insertionIndex + insertedAmount;
+ }
+
+ // copy the current components up to insertionIndex
+ System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex);
+ // copy the new components into the array at insertionIndex
+ System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount);
+ // copy the current components from insertionIndex + inserted amount
+ System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex);
+
+ return inheritablePermission(String.join(".", targetArray), newInsertionIndex, failMessage);
+ }
+ };
+ }
+
+}