
dev.jorel.commandapi.arguments.CustomArgument Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2018, 2020 Jorel Ali (Skepter) - MIT License
*
* 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 dev.jorel.commandapi.arguments;
import java.io.Serializable;
import org.bukkit.command.CommandSender;
import com.mojang.brigadier.LiteralMessage;
import com.mojang.brigadier.Message;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import dev.jorel.commandapi.BukkitTooltip;
import dev.jorel.commandapi.CommandAPIBukkit;
import dev.jorel.commandapi.CommandAPIHandler;
import dev.jorel.commandapi.executors.CommandArguments;
import net.kyori.adventure.text.Component;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* An argument that represents any custom object
*
* @param the return type of this custom argument when it is used
* @param the return type of the underlying base argument {@code base}. For
* example, this would be {@code Integer} for an
* {@link IntegerArgument}
*
* @since 2.0
* @apiNote Returns a {@link T} object
*/
public class CustomArgument extends Argument {
private final CustomArgumentInfoParser infoParser;
private final Argument base;
private static final String INPUT = "%input%";
private static final String FULL_INPUT = "%finput%";
/**
* Creates a CustomArgument with a valid parser, with an underlying base
* argument as its parsing implementation.
*
* @param base the base argument to use for this custom argument. This base
* argument will represent the parsing implementation for client
* side and server side parsing. This base argument cannot be a
* {@link LiteralArgument} or {@link MultiLiteralArgument}
* @param parser A {@link CustomArgumentInfo} parser object which includes
* information such as the command sender, previously declared
* arguments and current input. This parser should return an
* object of your choice.
*
* {@code } the return type of this custom argument when
* it is used
* {@code } the return type of the underlying base
* argument {@code base}. For example, this would be
* {@code Integer} for an {@link IntegerArgument}
*
*/
public CustomArgument(Argument base, CustomArgumentInfoParser parser) {
super(base.getNodeName(), base.getRawType());
if (base instanceof LiteralArgument || base instanceof MultiLiteralArgument) {
throw new IllegalArgumentException(base.getClass().getSimpleName() + " is not a suitable base argument type for a CustomArgument");
}
this.base = base;
this.infoParser = parser;
}
@Override
public Class getPrimitiveType() {
return null;
}
@Override
public CommandAPIArgumentType getArgumentType() {
return CommandAPIArgumentType.CUSTOM;
}
@Override
public T parseArgument(CommandContext cmdCtx, String key, CommandArguments previousArgs)
throws CommandSyntaxException {
// Get the raw input and parsed input
final String customresult = CommandAPIHandler.getRawArgumentInput(cmdCtx, key);
final B parsedInput = base.parseArgument(cmdCtx, key, previousArgs);
try {
return infoParser.apply(new CustomArgumentInfo<>(CommandAPIBukkit.get().getCommandSenderFromCommandSource(cmdCtx.getSource()).getSource(),
previousArgs, customresult, parsedInput));
} catch (CustomArgumentException e) {
throw e.toCommandSyntax(customresult, cmdCtx);
} catch (Exception e) {
String errorMsg = new MessageBuilder("Error in executing command ").appendFullInput().append(" - ")
.appendArgInput().appendHere().toString().replace(INPUT, customresult)
.replace(FULL_INPUT, cmdCtx.getInput());
throw new SimpleCommandExceptionType(() -> errorMsg).create();
}
}
/**
* MessageBuilder is used to create error messages for invalid argument inputs
*/
public static class MessageBuilder implements Serializable {
private static final long serialVersionUID = 838497662821791798L;
StringBuilder builder;
/**
* Create a blank message
*/
public MessageBuilder() {
builder = new StringBuilder();
}
/**
* Create a message with an input string
*
* @param str The string to start the message with
*/
public MessageBuilder(String str) {
builder = new StringBuilder(str);
}
/**
* Appends the argument input that the CommandSender used in this command.
* For example, if /foo bar
was executed and an error occurs with
* the CustomArgument bar
, then the arg input will append
* bar
to the end of the message.
*
* This input is determined at runtime, and is stored as %input%
* until executed
*
* @return A reference to this object
*/
public MessageBuilder appendArgInput() {
builder.append(INPUT);
return this;
}
/**
* Appends the whole input that the CommandSender used in this command.
* For example, if /foo bar
was executed, then foo bar
* will be appended to the end of the message.
*
* This input is determined at runtime, and is stored as %finput%
* until executed
*
* @return A reference to this object
*/
public MessageBuilder appendFullInput() {
builder.append(FULL_INPUT);
return this;
}
/**
* Appends <--[HERE]
to the end of the message
*
* @return A reference to this object
*/
public MessageBuilder appendHere() {
builder.append("<--[HERE]");
return this;
}
/**
* Appends a string to the end of this message
*
* @param str The string to append to the end of this message
* @return A reference to this object
*/
public MessageBuilder append(String str) {
builder.append(str);
return this;
}
/**
* Appends an object to the end of this message
*
* @param obj The object to append to the end of this message
* @return A reference to this object
*/
public MessageBuilder append(Object obj) {
builder.append(obj);
return this;
}
/**
* Returns the String content of this MessageBuilder
*
* @return the String content of this MessageBuilder
*/
@Override
public String toString() {
return builder.toString();
}
}
/**
* An exception used to create command-related errors for the CustomArgument
*/
@SuppressWarnings("serial")
public static class CustomArgumentException extends Exception {
private BaseComponent[] errorBaseComponent = null;
private Component errorComponent = null;
private String errorMessage = null;
private MessageBuilder errorMessageBuilder = null;
/* Prevent instantiation from any other sources */
private CustomArgumentException() {
}
/**
* Constructs a CustomArgumentException with a given error message
*
* @param errorMessage the error message to display to the user when this
* exception is thrown
* @deprecated Use {@link CustomArgumentException#fromBaseComponents(BaseComponent...)} instead
*/
@Deprecated(since = "9.0.1", forRemoval = true)
public CustomArgumentException(BaseComponent[] errorMessage) {
this.errorBaseComponent = errorMessage;
}
/**
* Constructs a CustomArgumentException with a given error message
*
* @param errorMessage the error message to display to the user when this
* exception is thrown
* @deprecated Use {@link CustomArgumentException#fromString(String)} instead
*/
@Deprecated(since = "9.0.1", forRemoval = true)
public CustomArgumentException(String errorMessage) {
this.errorMessage = errorMessage;
}
/**
* Constructs a CustomArgumentException with a given error message
*
* @param errorMessage the error message to display to the user when this
* exception is thrown
* @deprecated Use {@link CustomArgumentException#fromMessageBuilder(MessageBuilder)} instead
*/
@Deprecated(since = "9.0.1", forRemoval = true)
public CustomArgumentException(MessageBuilder errorMessage) {
this.errorMessageBuilder = errorMessage;
}
/**
* Constructs a CustomArgumentException with a given error message
*
* @param errorMessage the error message to display to the user when this
* exception is thrown
*/
public static CustomArgumentException fromBaseComponents(BaseComponent[] errorMessage) {
CustomArgumentException exception = new CustomArgumentException();
exception.errorBaseComponent = errorMessage;
return exception;
}
/**
* Constructs a CustomArgumentException with a given error message
*
* @param errorMessage the error message to display to the user when this
* exception is thrown
*/
public static CustomArgumentException fromString(String errorMessage) {
CustomArgumentException exception = new CustomArgumentException();
exception.errorMessage = errorMessage;
return exception;
}
/**
* Constructs a CustomArgumentException with a given error message
*
* @param errorMessage the error message to display to the user when this
* exception is thrown
*/
public static CustomArgumentException fromAdventureComponent(Component errorMessage) {
CustomArgumentException exception = new CustomArgumentException();
exception.errorComponent = errorMessage;
return exception;
}
/**
* Constructs a CustomArgumentException with a given error message
*
* @param errorMessage the error message to display to the user when this
* exception is thrown
*/
public static CustomArgumentException fromMessageBuilder(MessageBuilder errorMessage) {
CustomArgumentException exception = new CustomArgumentException();
exception.errorMessageBuilder = errorMessage;
return exception;
}
/**
* Converts this CustomArgumentException into a CommandSyntaxException
*
* @param result the argument that the user entered that caused this exception
* to arise
* @param cmdCtx the command context that executed this command
* @return a Brigadier CommandSyntaxException
*/
public CommandSyntaxException toCommandSyntax(String result, CommandContext> cmdCtx) {
if (errorBaseComponent != null) {
// Deal with BaseComponent[]
Message brigadierMessage = BukkitTooltip.messageFromBaseComponents(errorBaseComponent);
return new SimpleCommandExceptionType(brigadierMessage).create();
}
if (errorComponent != null) {
// Deal with Adventure Component
Message brigadierMessage = BukkitTooltip.messageFromAdventureComponent(errorComponent);
return new SimpleCommandExceptionType(brigadierMessage).create();
}
if (errorMessageBuilder != null) {
// Deal with MessageBuilder
String errorMsg = errorMessageBuilder.toString().replace(INPUT, result).replace(FULL_INPUT,
cmdCtx.getInput());
return new SimpleCommandExceptionType(new LiteralMessage(errorMsg)).create();
}
if (errorMessage != null) {
// Deal with String
return CommandAPIBukkit.get().getPaper().getExceptionFromString(errorMessage).getException();
}
throw new IllegalStateException("No error component, error message creator or error message specified");
}
}
/**
* A record which contains information which can be passed to the custom
* argument's parser.
*
* @param sender the sender that types this argument
* @param previousArgs previousArgs - a {@link CommandArguments} object holding previously declared (parsed)
* arguments. This can be used as if it were arguments in a command executor method.
* @param input the current input which the user has typed for this
* argument
* @param currentInput the current input, when parsed with the underlying base
* argument.
*/
public record CustomArgumentInfo(
/**
* sender - the sender that types this argument
*/
CommandSender sender,
/**
* previousArgs - a {@link CommandArguments} object holding previously declared (parsed) arguments. This can
* be used as if it were arguments in a command executor method.
*/
CommandArguments previousArgs,
/**
* input - the current input which the user has typed for this argument
*/
String input,
/**
* currentInput - the current input, when parsed with the underlying base
* argument.
*/
B currentInput) {
}
/**
* A FunctionalInterface that takes in a {@link CustomArgumentInfo}, returns T
* and can throw a {@link CustomArgumentException}
*
* @param the type that is returned when applying this parser
*/
@FunctionalInterface
public static interface CustomArgumentInfoParser {
/**
* Applies a CustomArgumentInfo input to this custom argument parser
*
* @param info the custom argument info to apply to this parser
* @return the applied output represented by this FunctionalInterface
* @throws CustomArgumentException if an error occurs during parsing
*/
public T apply(CustomArgumentInfo info) throws CustomArgumentException;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy