package io.dico.dicore.command; import io.dico.dicore.Formatting; import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ContextParser; import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.ParameterList; import org.bukkit.Location; import org.bukkit.command.CommandSender; import java.util.*; /** * The context of execution. *

* This class is responsible for the control flow of parameter parsing, as well as caching and providing the parsed parameter values. * It is also responsible for keeping track of the parameter to complete in the case of a tab completion. */ public class ExecutionContext { // Sender of the command private final CommandSender sender; // Address while parsing parameters with ContextParser private ICommandAddress address; // Command to execute private Command command; // if this flag is set, this execution is only for completion purposes. private boolean tabComplete; private final ArgumentBuffer buffer; // private ArgumentBuffer processedBuffer; // caches the buffer's cursor before parsing. This is needed to provide the original input of the player. private int cursorStart; // when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException. //private boolean attemptedToParse; // The parsed parameter values, mapped by parameter name. // This also includes default values. All parameters from the parameter list are present if parsing was successful. private Map parameterValueMap = new HashMap<>(); // this set contains the names of the parameters that were present in the command, and not given a default value. private Set parsedParameters = new HashSet<>(); // these fields store information required to provide completions. // the parameter to complete is the parameter that threw an exception when it was parsing. // the exception's message was discarded because it is a completion. private Parameter parameterToComplete; // this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested. private int parameterToCompleteCursor = -1; // if this flag is set, any messages sent through the sendMessage methods are discarded. private boolean muted; public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) { this.sender = Objects.requireNonNull(sender); this.buffer = Objects.requireNonNull(buffer); this.muted = tabComplete; this.tabComplete = tabComplete; // If its tab completing, keep the empty element that might be at the end of the buffer // due to a space at the end of the command. // This allows the parser to correctly identify the parameter to be completed in this case. if (!tabComplete) { buffer.dropTrailingEmptyElements(); } } /** * Construct an execution context that is ready to parse the parameter values. * * @param sender the sender * @param address the address * @param command the command * @param buffer the arguments * @param tabComplete true if this execution is a tab-completion */ public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) { this(sender, buffer, tabComplete); setAddress(address); setCommand(command); } /** * Sender of the command * * @return the sender of the command */ public CommandSender getSender() { return sender; } /** * @return the buffer of arguments */ public ArgumentBuffer getBuffer() { return buffer; } /** * Command's address * * @return the command's address */ public ICommandAddress getAddress() { return address; } /** * Set the address * * @param address the new address */ public void setAddress(ICommandAddress address) { this.address = address; } /** * The command * * @return the command */ public Command getCommand() { return command; } /** * Set the command * * @param command the new command */ public void setCommand(Command command) { this.command = command; } /** * @return true if this context is for a tab completion. */ public boolean isTabComplete() { return tabComplete; } /** * @return true if this context is muted. */ public boolean isMuted() { return muted; } /** * Parse parameters from the given parameter list, * adding their values to the cache of this context. * * @param parameterList the parameterList * @throws CommandException if the arguments are not valid */ public void parse(ParameterList parameterList) throws CommandException { cursorStart = buffer.getCursor(); ContextParser parser = new ContextParser(this, parameterList, parameterValueMap, parsedParameters); try { parser.parse(); } finally { if (tabComplete) { parameterToComplete = parser.getCompletionTarget(); parameterToCompleteCursor = parser.getCompletionCursor(); } } } /** * The command's parameter definition. * * @return the parameter list */ @Deprecated public ParameterList getParameterList() { return null;//command.getParameterList(); } /** * Get the buffer as it was before preprocessing the arguments. * * @return the original buffer */ @Deprecated public ArgumentBuffer getOriginalBuffer() { return buffer; } /** * The arguments * * @return the argument buffer */ @Deprecated public ArgumentBuffer getProcessedBuffer() { return buffer; } /** * The cursor start, in other words, the buffer's cursor before parameters were parsed. * * @return the cursor start */ public int getCursorStart() { return cursorStart; } /** * The original arguments. * * @return original arguments. */ public String[] getOriginal() { return buffer.getArrayFromIndex(cursorStart); } /** * The path used to access this address. * * @return the path used to access this address. */ public String[] getRoute() { return Arrays.copyOf(buffer.toArray(), address.getDepth()); } public Formatting getFormat(EMessageType type) { return address.getChatHandler().getChatFormatForType(type); } /** * The full command as cached by the buffer. Might be incomplete depending on how it was dispatched. * * @return the full command */ public String getRawInput() { return buffer.getRawInput(); } /** * Get the value of the parameter with the given name * * @param name the parameter's name * @param expected type * @return the parsed value or the default value */ @SuppressWarnings("unchecked") public T get(String name) { if (!parameterValueMap.containsKey(name)) { throw new IllegalArgumentException(); } try { return (T) parameterValueMap.get(name); } catch (ClassCastException ex) { throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex); } } /** * Get the value of the flag with the given name * * @param flag the flag's name, without preceding "-" * @param expected type * @return the parsed value or the default value */ public T getFlag(String flag) { return get("-" + flag); } @SuppressWarnings("unchecked") @Deprecated public T get(int index) { return null;//get(getParameterList().getIndexedParameterName(index)); } /** * Checks if the parameter by the name was provided in the command's arguments. * * @param name the parameter name * @return true if it was provided */ public boolean isProvided(String name) { return parsedParameters.contains(name); } /** * Checks if the parameter by the index was provided in the command's arguments. * * @param index the parameter index * @return true if it was provided */ @Deprecated public boolean isProvided(int index) { return false;//isProvided(getParameterList().getIndexedParameterName(index)); } /** * The parameter to complete. * This parameter is requested suggestions * * @return the parameter to complete. */ public Parameter getParameterToComplete() { return parameterToComplete; } /** * Get suggested completions. * * @param location The location as passed to {link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}, or null if requested in another way. * @return completions. */ public List getSuggestedCompletions(Location location) { if (parameterToComplete != null) { return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor)); } List result = new ArrayList<>(); for (String name : parameterValueMap.keySet()) { if (name.startsWith("-") && !parsedParameters.contains(name)) { result.add(name); } } return result; } /* Chat handling */ public void sendMessage(String message) { sendMessage(true, message); } public void sendMessage(EMessageType messageType, String message) { sendMessage(messageType, true, message); } public void sendMessage(boolean translateColours, String message) { sendMessage(EMessageType.NEUTRAL, translateColours, message); } public void sendMessage(EMessageType messageType, boolean translateColours, String message) { if (!muted) { if (translateColours) { message = Formatting.translateChars('&', message); } address.getChatHandler().sendMessage(this, messageType, message); } } public void sendMessage(String messageFormat, Object... args) { sendMessage(true, messageFormat, args); } public void sendMessage(EMessageType messageType, String messageFormat, Object... args) { sendMessage(messageType, true, messageFormat, args); } public void sendMessage(boolean translateColours, String messageFormat, Object... args) { sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args); } public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) { sendMessage(messageType, translateColours, String.format(messageFormat, args)); } public void sendHelpMessage(int page) { if (!muted) { address.getChatHandler().sendHelpMessage(sender, this, address, page); } } public void sendSyntaxMessage() { if (!muted) { address.getChatHandler().sendSyntaxMessage(sender, this, address); } } }