001/*
002 * PermissionsEx
003 * Copyright (C) zml and PermissionsEx contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ca.stellardrift.permissionsex.minecraft.command;
018
019import ca.stellardrift.permissionsex.PermissionsEngine;
020import ca.stellardrift.permissionsex.minecraft.MinecraftPermissionsEx;
021import cloud.commandframework.Command;
022import cloud.commandframework.CommandManager;
023import org.checkerframework.checker.nullness.qual.Nullable;
024
025import java.util.ArrayDeque;
026import java.util.Arrays;
027import java.util.Deque;
028import java.util.function.Consumer;
029import java.util.function.Function;
030
031/**
032 * Context for executing command registrations
033 */
034public final class CommandRegistrationContext {
035    private final String commandPrefix;
036    private final CommandManager<Commander> commandManager;
037    private final MinecraftPermissionsEx<?> manager;
038    private final Deque<Command.Builder<Commander>> builderStack = new ArrayDeque<>();
039
040    public CommandRegistrationContext(
041            final String commandPrefix,
042            final MinecraftPermissionsEx<?> manager,
043            final CommandManager<Commander> commandManager) {
044        this.commandPrefix = commandPrefix;
045        this.manager = manager;
046        this.commandManager = commandManager;
047    }
048
049    public PermissionsEngine engine() {
050        return this.manager.engine();
051    }
052
053    public MinecraftPermissionsEx<?> manager() {
054        return this.manager;
055    }
056
057    public String commandPrefix() {
058        return this.commandPrefix;
059    }
060
061    public CommandManager<Commander> commandManager() {
062        return this.commandManager;
063    }
064
065    /**
066     * Get the current command builder at the head of the command stack.
067     *
068     * @return the head of the stack
069     */
070    public Command.Builder<Commander> head() {
071        final Command.@Nullable Builder<Commander> head = this.builderStack.peek();
072        if (head == null) {
073            throw new IllegalStateException("Tried to peek command registration while stack was empty");
074        } else {
075            return head;
076        }
077    }
078
079    public void push(final Command.Builder<Commander> builder, final Consumer<CommandRegistrationContext> handler) {
080        final int startSize = this.builderStack.size();
081        this.builderStack.push(builder);
082        try {
083            handler.accept(this);
084        } finally {
085            this.builderStack.pop();
086            if (this.builderStack.size() != startSize) {
087                throw new IllegalStateException("Command registration stack corruption detected while popping " + builder.build());
088            }
089        }
090    }
091
092    public void push(final Consumer<CommandRegistrationContext> handler, final String primaryAlias, final String... aliases) {
093        push(this.head().literal(primaryAlias, aliases), handler);
094    }
095
096    /**
097     * Simple helper to register a subcommand of the PEX base command.
098     *
099     * @param maker a function that will add arguments to the PEX base command
100     * @param primaryAlias the primary alias for this subcommand
101     * @param aliases any other aliases
102     */
103    public Command<Commander> register(final Function<Command.Builder<Commander>, Command.Builder<Commander>> maker, final String primaryAlias, final String... aliases) {
104        final Command.@Nullable Builder<Commander> headBuilder = this.builderStack.peek();
105        if (headBuilder != null) {
106            return register(maker.apply(headBuilder.literal(primaryAlias, aliases)));
107        } else {
108            return register(maker.apply(absoluteBuilder(primaryAlias, aliases)));
109        }
110    }
111
112    /**
113     * Register a command with the manager.
114     *
115     * @param builder the builder to register
116     * @return the built command
117     */
118    public Command<Commander> register(final Command.Builder<Commander> builder) {
119        final Command<Commander> built = builder.manager(this.commandManager).build();
120        this.commandManager.command(built);
121        return built;
122    }
123
124    /**
125     * Create a new command builder at the root of the tree.
126     *
127     * @param primaryAlias the primary command alias
128     * @param aliases the aliases so tadd
129     * @return the builder
130     */
131    public Command.Builder<Commander> absoluteBuilder(String primaryAlias, String... aliases) {
132        if (!this.commandPrefix.isEmpty()) {
133            primaryAlias = this.commandPrefix + primaryAlias;
134            aliases = Arrays.copyOf(aliases, aliases.length);
135            for (int i = 0, length = aliases.length; i < length; ++i) {
136                aliases[i] = this.commandPrefix + aliases[i];
137            }
138        }
139        return this.commandManager.commandBuilder(primaryAlias, aliases);
140    }
141}