8000 Implementing optional arguments by DerEchtePilz · Pull Request #393 · CommandAPI/CommandAPI · GitHub
[go: up one dir, main page]

Skip to content

Implementing optional arguments #393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cdb4c08
implementing optional arguments
DerEchtePilz Jan 3, 2023
96a03ec
Add optional arguments to the Kotlin DSL. Rename commandapi-kotlin-bu…
DerEchtePilz Jan 4, 2023
5b46749
add missing JavaDocs on AbstractArgument#setOptional(boolean)
DerEchtePilz Jan 4, 2023
2ad5828
fixing build error
DerEchtePilz Jan 4, 2023
3cf8297
adding documentation for optional arguments
DerEchtePilz Jan 4, 2023
6eb09e5
fixing dependency in the Bukkit Kotlin DSL example
DerEchtePilz Jan 4, 2023
7c4f635
address code review
DerEchtePilz Jan 4, 2023
a25547d
create optional_arguments.md
DerEchtePilz Jan 4, 2023
d1c0088
change /kill command to /sayhi
DerEchtePilz Jan 4, 2023
5ad1a21
make Mardown Linter happy (hopefully)
DerEchtePilz Jan 4, 2023
c82feff 8000
fix wrong enchor end
DerEchtePilz Jan 4, 2023
f777925
address code review
DerEchtePilz Jan 4, 2023
33228d4
convert tabs to spaces in Examples.java
DerEchtePilz Jan 4, 2023
c226296
Merge branch 'dev/dev' into dev/dev
JorelAli Jan 4, 2023
210bed6
refactor AbstractCommandAPICommand#register()
DerEchtePilz Jan 4, 2023
afbd40a
Merge branch 'DerEchtePilz:dev/dev' into 'dev/dev'
DerEchtePilz Jan 4, 2023
7e36de1
simplifying AbstractCommandAPICommand#getArgumentsToRegister()
DerEchtePilz Jan 4, 2023
458b8d8
refactor AbstractCommandAPICommand#register() again
DerEchtePilz Jan 4, 2023
3e7c6ca
Add a section 'Setting existing arguments as optional arguments' to o…
DerEchtePilz Jan 4, 2023
d913acc
remove the List parameter from AbstractCommandAPICommand#getCommandsT…
DerEchtePilz Jan 4, 2023
6266990
Merge branch 'dev/dev' into dev/dev
DerEchtePilz Jan 4, 2023
ce835c6
fix a bug where a required argument would be registered as an optiona…
DerEchtePilz Jan 4, 2023
473bc75
implement multiple getOrDefault methods for the CommandArguments class
DerEchtePilz Jan 4, 2023
a31b755
adding documentation for the getOrDefault method
DerEchtePilz Jan 4, 2023
572beb9
address code review
DerEchtePilz Jan 4, 2023
c8c4960
make CommandArguments#get(int) not throw an ArrayIndexOutOfBoundsExce…
DerEchtePilz Jan 4, 2023
3008d60
Merge branch 'dev/dev' into dev/dev
DerEchtePilz Jan 4, 2023
a76530d
fix variable names after merge
DerEchtePilz Jan 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import dev.jorel.commandapi.arguments.AbstractArgument;
import dev.jorel.commandapi.arguments.GreedyArgument;
import dev.jorel.commandapi.exceptions.GreedyArgumentException;
import dev.jorel.commandapi.exceptions.OptionalArgumentException;

import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -39,7 +40,7 @@
public abstract class AbstractCommandAPICommand<Impl extends AbstractCommandAPICommand<Impl, Argument, CommandSender>,
Argument extends AbstractArgument<?, ?, Argument, CommandSender>, CommandSender> extends ExecutableCommand<Impl, CommandSender> {

protected List<Argument> args = new ArrayList<>();
protected List<Argument> arguments = new ArrayList<>();
protected List<Impl> subcommands = new ArrayList<>();
protected boolean isConverted;

Expand Down Expand Up @@ -71,7 +72,7 @@ protected AbstractCommandAPICommand(CommandMetaData<CommandSender> metaData) {
* @return this command builder
*/
public Impl withArguments(List<Argument> args) {
this.args.addAll(args);
this.arguments.addAll(args);
return instance();
}

Expand All @@ -83,7 +84,41 @@ public Impl withArguments(List<Argument> args) {
*/
@SafeVarargs
public final Impl withArguments(Argument... args) {
this.args.addAll(Arrays.asList(args));
this.arguments.addAll(Arrays.asList(args));
return instance();
}

/**
* Appends the optional arguments to the current command builder.
*
* This also calls {@link AbstractArgument#setOptional(boolean)} on each argument to make sure they are optional
*
* @param args A <code>List</code> that represents the arguments that this
* command can accept
* @return this command builder
*/
public Impl withOptionalArguments(List<Argument> args) {
for (Argument argument : args) {
argument.setOptional(true);
this.arguments.add(argument);
}
return instance();
}

/**
* Appends the optional arguments to the current command builder.
*
* This also calls {@link AbstractArgument#setOptional(boolean)} on each argument to make sure they are optional
*
* @param args Arguments that this command can accept
* @return this command builder
*/
@SafeVarargs
public final Impl withOptionalArguments(Argument... args) {
for (Argument argument : args) {
argument.setOptional(true);
this.arguments.add(argument);
}
return instance();
}

Expand Down Expand Up @@ -115,7 +150,7 @@ public Impl withSubcommands(Impl... subcommands) {
* @return the list of arguments that this command has
*/
public List<Argument> getArguments() {
return args;
return arguments;
}

/**
Expand All @@ -124,7 +159,7 @@ public List<Argument> getArguments() {
* @param args the arguments that this command has
*/
public void setArguments(List<Argument> args) {
this.args = args;
this.arguments = args;
}

/**
Expand Down Expand Up @@ -194,8 +229,8 @@ void flatten(Impl rootCommand, List<Argument> prevArguments, Impl subcommand) {
// - has no subcommands(?)
// Honestly, if you're asking how or why any of this works, I don't
// know because I just trialled random code until it started working
rootCommand.args = prevArguments;
rootCommand.withArguments(subcommand.args);
rootCommand.arguments = prevArguments;
rootCommand.withArguments(subcommand.arguments);
rootCommand.executor = subcommand.executor;
rootCommand.subcommands = new ArrayList<>();
rootCommand.register();
Expand All @@ -212,29 +247,38 @@ public void register() {
CommandAPI.logWarning("Command /" + meta.commandName + " is being registered after the server had loaded. Undefined behavior ahead!");
}
try {
Argument[] argumentsArr = (Argument[]) (args == null ? new AbstractArgument[0] : args.toArray(AbstractArgument[]::new));
Argument[] argumentsArray = (Argument[]) (arguments == null ? new AbstractArgument[0] : arguments.toArray(AbstractArgument[]::new));

// Check GreedyArgument constraints
for (int i = 0, numGreedyArgs = 0; i < argumentsArr.length; i++) {
if (argumentsArr[i] instanceof GreedyArgument) {
if (++numGreedyArgs > 1 || i != argumentsArr.length - 1) {
throw new GreedyArgumentException(argumentsArr);
for (int i = 0, numGreedyArgs = 0; i < argumentsArray.length; i++) {
if (argumentsArray[i] instanceof GreedyArgument) {
if (++numGreedyArgs > 1 || i != argumentsArray.length - 1) {
throw new GreedyArgumentException(argumentsArray);
}
}
}

// Assign the command's permissions to arguments if the arguments don't already
// have one
for (Argument argument : argumentsArr) {
for (Argument argument : argumentsArray) {
if (argument.getArgumentPermission() == null) {
argument.withPermission(meta.permission);
}
}

// Create a List<Argument[]> that is used to register optional arguments
List<Argument[]> argumentsToRegister = getArgumentsToRegister(argumentsArray);

if (executor.hasAnyExecutors()) {
// Need to cast handler to the right CommandSender type so that argumentsArr and executor are accepted
// Need to cast handler to the right CommandSender type so that argumentsArray and executor are accepted
CommandAPIHandler<Argument, CommandSender, ?> handler = (CommandAPIHandler<Argument, CommandSender, ?>) CommandAPIHandler.getInstance();
handler.register(meta, argumentsArr, executor, isConverted);
if (argumentsToRegister.isEmpty()) {
handler.register(meta, argumentsArray, executor, isConverted);
} else {
for (Argument[] arguments : argumentsToRegister) {
handler.register(meta, arguments, executor, isConverted);
}
}
}

// Convert subcommands into multiliteral arguments
Expand All @@ -248,11 +292,43 @@ public void register() {

public Impl copy() {
Impl command = newConcreteCommandAPICommand(new CommandMetaData<>(this.meta));
command.args = new ArrayList<>(this.args);
command.arguments = new ArrayList<>(this.arguments);
command.subcommands = new ArrayList<>(this.subcommands);
command.isConverted = this.isConverted;
return command;
}

protected abstract Impl newConcreteCommandAPICommand(CommandMetaData<CommandSender> metaData);

private List<Argument[]> getArgumentsToRegister(Argument[] argumentsArray) {
List<Argument[]> argumentsToRegister = new ArrayList<>();

// Check optional argument constraints
// They can only be at the end, no required argument can follow an optional argument
int firstOptionalArgumentIndex = -1;
for (int i = 0, optionalArgumentIndex = -1; i < argumentsArray.length; i++) {
if (argumentsArray[i].isOptional()) {
if (firstOptionalArgumentIndex == -1) {
firstOptionalArgumentIndex = i;
}
optionalArgumentIndex = i;
} else if (optionalArgumentIndex != -1) {
// Argument is not optional
throw new OptionalArgumentException();
}
}

// Create a List of arrays that hold arguments to register optional arguments
// if optional arguments have been found
if (firstOptionalArgumentIndex != -1) {
for (int i = 0; i <= argumentsArray.length; i++) {
if (i >= firstOptionalArgumentIndex) {
Argument[] arguments = (Argument[]) new AbstractArgument[i];
System.arraycopy(argumentsArray, 0, arguments, 0, i);
argumentsToRegister.add(arguments);
}
}
}
return argumentsToRegister;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,32 @@ public Impl setListed(boolean listed) {
return instance();
}

/////////////////
// Optionality //
/////////////////

private boolean isOptional = false;

/**
* Returns true if this argument will be optional when executing the command this argument is included in
*
* @return true if this argument will be optional when executing the command this argument is included in
*/
public boolean isOptional() {
return isOptional;
}

/**
* Sets whether this argument will be optional when executing the command this argument is included in
*
* @param optional if true, this argument will be optional when executing the command this argument is included in
* @return this current argument
*/
public Impl setOptional(boolean optional) {
this.isOptional = optional;
return instance();
}

///////////
// Other //
///////////
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dev.jorel.commandapi.exceptions;

public class OptionalArgumentException extends RuntimeException {

public OptionalArgumentException() {
super("Optional argument can not be followed by a required argument!");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import javax.annotation.Nullable;
import java.util.Map;
import java.util.function.Supplier;

/**
* This class stores the arguments for this command
Expand All @@ -14,7 +15,7 @@ public class CommandArguments {
/**
* Constructs a new CommandArguments instance
*
* @param args The arguments for this command
* @param args The arguments for this command
* @param argsMap The arguments for this command mapped to the node names. This is an ordered map
*/
public CommandArguments(Object[] args, Map<String, Object> argsMap) {
Expand All @@ -35,8 +36,13 @@ public Object[] args() {
* @param index The position of this argument
* @return an argument which is placed at the given index
*/
@Nullable
public Object get(int index) {
return args[index];
if (args.length <= index) {
return null;
} else {
return args[index];
}
}

/**
Expand All @@ -48,5 +54,57 @@ public Object get(int index) {
@Nullable
public Object get(String nodeName) {
return argsMap.get(nodeName);
}
}

/**
* Returns an argument by its index
*
* @param index The position of this argument
* @param defaultValue The Object returned if the argument is not existent
* @return An argument which is placed at the given index, or the provided default value
*/
public Object getOrDefault(int index, Object defaultValue) {
if (args.length <= index) {
return defaultValue;
} else {
return args[index];
}
}

/**
* Returns an argument by its node name
*
* @param nodeName The node name of this argument. This was set when initializing an argument
* @param defaultValue The Object returned if the argument was not found.
* @return The argument with the specified node name or the provided default value
*/
public Object getOrDefault(String nodeName, Object defaultValue) {
return argsMap.getOrDefault(nodeName, defaultValue);
}

/**
* Returns an argument by its index
*
* @param index The position of this argument
* @param defaultValue The Object returned if the argument is not existent
* @return An argument which is placed at the given index, or the provided default value
*/
public Object getOrDefault(int index, Supplier<?> defaultValue) {
if (args.length <= index) {
return defaultValue.get();
} else {
return args[index];
}
}

/**
* Returns an argument by its node name
*
* @param nodeName The node name of this argument. This was set when initializing an argument
* @param defaultValue The Object returned if the argument was not found.
* @return The argument with the specified node name or the provided default value
*/
public Object getOrDefault(String nodeName, Supplier<?> defaultValue) {
return argsMap.getOrDefault(nodeName, defaultValue.get());
}
}
2 changes: 1 addition & 1 deletion commandapi-documentation-code/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
</dependency>
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-kotlin-bukkit</artifactId>
<artifactId>commandapi-bukkit-kotlin</artifactId>
<version>${project.version}</version>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1303,23 +1303,31 @@ void resultingcommandexecutor3(){
}

{
/* ANCHOR: argumentkillcmd */
new CommandAPICommand("kill")
/* ANCHOR: argumentsayhicmd */
new CommandAPICommand("sayhi")
.withOptionalArguments(new PlayerArgument("target"))
.executesPlayer((player, args) -> {
player.setHealth(0);
Player target = (Player) args.get("target");
if (target != null) {
target.sendMessage("Hi!");
} else {
player.sendMessage("Hi!");
}
})
.register();
/* ANCHOR_END: argumentkillcmd */
/* ANCHOR_END: argumentsayhicmd */
}

/* ANCHOR: argumentkillcmd2 */
// Register our second /kill <target> command
new CommandAPICommand("kill")
.withArguments(new PlayerArgument("target"))
{
/* ANCHOR: argumentsayhicmd2 */
new CommandAPICommand("sayhi")
.withOptionalArguments(new PlayerArgument("target"))
.executesPlayer((player, args) -> {
((Player) args.get(0)).setHealth(0);
Player target = (Player) args.getOrDefault("target", player);
target.sendMessage("Hi!");
})
.register();
/* ANCHOR_END: argumentkillcmd2 */
/* ANCHOR_END: argumentsayhicmd2 */
}

@SuppressWarnings("unused")
Expand Down
Loading
0