Skip to content

Commit 9680c29

Browse files
committed
feat(commands): add invoker to remove duplication code
1 parent 62e6b67 commit 9680c29

File tree

25 files changed

+610
-481
lines changed

25 files changed

+610
-481
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package fr.traqueur.commands.api;
2+
3+
import fr.traqueur.commands.api.arguments.TabCompleter;
4+
import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException;
5+
import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException;
6+
import fr.traqueur.commands.api.requirements.Requirement;
7+
8+
import java.util.Arrays;
9+
import java.util.Collections;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.stream.Collectors;
13+
14+
/**
15+
* CommandInvoker is responsible for invoking commands based on the provided label and arguments.
16+
* It checks permissions, requirements, and executes the command if all conditions are met.
17+
*
18+
* @param <T> The type of the command sender (e.g., Player, Console).
19+
* @param <S> The type of the source (e.g., Player, CommandSender).
20+
*/
21+
public class CommandInvoker<T, S> {
22+
23+
/**
24+
* The command manager that holds the commands and their configurations.
25+
*/
26+
private final CommandManager<T, S> commandManager;
27+
28+
/**
29+
* Constructor for CommandInvoker.
30+
* @param manager The command manager that holds the commands and their configurations.
31+
*/
32+
public CommandInvoker(CommandManager<T,S> manager) {
33+
this.commandManager = manager;
34+
}
35+
36+
/**
37+
* Get the command label from the label and the arguments.
38+
* @param label The label of the command.
39+
* @param args The arguments of the command.
40+
* @param commandLabelSize The size of the command label.
41+
* @return The command label.
42+
*/
43+
private String getCommandLabel(String label, String[] args, int commandLabelSize) {
44+
StringBuilder buffer = new StringBuilder();
45+
String labelLower = label.toLowerCase();
46+
buffer.append(labelLower);
47+
for (int x = 0; x < commandLabelSize; x++) {
48+
buffer.append(".").append(args[x].toLowerCase());
49+
}
50+
return buffer.toString();
51+
}
52+
53+
/**
54+
* Invokes a command based on the provided source, base label, and raw arguments.
55+
* It checks for command existence, permissions, requirements, and executes the command if valid.
56+
*
57+
* @param source The source of the command (e.g., Player, Console).
58+
* @param baseLabel The base label of the command.
59+
* @param rawArgs The raw arguments passed to the command.
60+
* @return true if the command was successfully invoked, false otherwise.
61+
*/
62+
public boolean invoke(S source,
63+
String baseLabel,
64+
String[] rawArgs) {
65+
66+
Map<String, Command<T, S>> commands = this.commandManager.getCommands();
67+
68+
String cmdLabel = "";
69+
Command<T, S> commandFramework = null;
70+
for (int i = rawArgs.length; i >= 0; i--) {
71+
cmdLabel = getCommandLabel(baseLabel, rawArgs, i);
72+
commandFramework = commands.getOrDefault(cmdLabel, null);
73+
if(commandFramework != null) {
74+
break;
75+
}
76+
}
77+
78+
if (commandFramework == null) {
79+
return false;
80+
}
81+
82+
if(commandFramework.inGameOnly() && ! this.commandManager.getPlatform().isPlayer(source)) {
83+
this.commandManager.getPlatform().sendMessage(source, this.commandManager.getMessageHandler().getOnlyInGameMessage());
84+
return true;
85+
}
86+
87+
if (!commandFramework.getPermission().isEmpty() && !this.commandManager.getPlatform().hasPermission(source, commandFramework.getPermission())) {
88+
this.commandManager.getPlatform().sendMessage(source, this.commandManager.getMessageHandler().getNoPermissionMessage());
89+
return true;
90+
}
91+
92+
List<Requirement<S>> requirements = commandFramework.getRequirements();
93+
for (Requirement<S> requirement : requirements) {
94+
if (!requirement.check(source)) {
95+
String error = requirement.errorMessage().isEmpty()
96+
? this.commandManager.getMessageHandler().getRequirementMessage()
97+
: requirement.errorMessage();
98+
error = error.replace("%requirement%", requirement.getClass().getSimpleName());
99+
this.commandManager.getPlatform().sendMessage(source, error);
100+
return true;
101+
}
102+
}
103+
104+
105+
int subCommand = cmdLabel.split("\\.").length - 1;
106+
String[] modArgs = Arrays.copyOfRange(rawArgs, subCommand, rawArgs.length);
107+
108+
if ((modArgs.length < commandFramework.getArgs().size()) || (!commandFramework.isInfiniteArgs() && (modArgs.length > commandFramework.getArgs().size() + commandFramework.getOptinalArgs().size()))) {
109+
String usage = commandFramework.getUsage().equalsIgnoreCase("")
110+
? commandFramework.generateDefaultUsage(this.commandManager.getPlatform(), source, cmdLabel)
111+
: commandFramework.getUsage();
112+
this.commandManager.getPlatform().sendMessage(source, usage);
113+
return true;
114+
}
115+
116+
try {
117+
Arguments arguments = this.commandManager.parse(commandFramework, modArgs);
118+
commandFramework.execute(source, arguments);
119+
} catch (TypeArgumentNotExistException e) {
120+
throw new RuntimeException(e);
121+
} catch (ArgumentIncorrectException e) {
122+
String message = this.commandManager.getMessageHandler().getArgNotRecognized();
123+
message = message.replace("%arg%", e.getInput());
124+
this.commandManager.getPlatform().sendMessage(source, message);
125+
}
126+
return true;
127+
}
128+
129+
/**
130+
* Suggests completions for the command based on the provided source, label, and arguments.
131+
* It filters the suggestions based on the current argument and checks permissions for each suggestion.
132+
*
133+
* @param source The source of the command (e.g., Player, Console).
134+
* @param label The label of the command.
135+
* @param args The arguments passed to the command.
136+
* @return A list of suggested completions.
137+
*/
138+
public List<String> suggest(S source, String label, String[] args) {
139+
String arg = args[args.length-1];
140+
141+
142+
Map<String, Map<Integer, TabCompleter<S>>> completers = commandManager.getCompleters();
143+
Map<String, Command<T, S>> commands = this.commandManager.getCommands();
144+
145+
String cmdLabel = "";
146+
Map<Integer, TabCompleter<S>> map = null;
147+
for (int i = args.length; i >= 0; i--) {
148+
cmdLabel = getCommandLabel(label, args, i);
149+
map = completers.getOrDefault(cmdLabel, null);
150+
if(map != null) {
151+
break;
152+
}
153+
}
154+
155+
if (map == null || !map.containsKey(args.length)) {
156+
return Collections.emptyList();
157+
}
158+
159+
TabCompleter<S> converter = map.get(args.length);
160+
String argsBeforeString = (label +
161+
"." +
162+
String.join(".", Arrays.copyOf(args, args.length - 1)))
163+
.replaceFirst("^" + cmdLabel + "\\.", "");
164+
165+
List<String> completer = converter.onCompletion(source, Arrays.asList(argsBeforeString.split("\\.")))
166+
.stream()
167+
.filter(str -> str.toLowerCase().startsWith(arg.toLowerCase()) || str.equalsIgnoreCase(arg))
168+
.collect(Collectors.toList());
169+
170+
String finalCmdLabel = cmdLabel;
171+
return completer.stream().filter(str -> {
172+
String cmdLabelInner = finalCmdLabel + "." + str.toLowerCase();
173+
if(commands.containsKey(cmdLabelInner)) {
174+
Command<T, S> frameworkCommand = commands.get(cmdLabelInner);
175+
List<Requirement<S>> requirements = frameworkCommand.getRequirements();
176+
for (Requirement<S> requirement : requirements) {
177+
if (!requirement.check(source)) {
178+
return false;
179+
}
180+
}
181+
return frameworkCommand.getPermission().isEmpty() || this.commandManager.getPlatform().hasPermission(source, frameworkCommand.getPermission());
182+
}
183+
return true;
184+
}).collect(Collectors.toList());
185+
}
186+
}

core/src/main/java/fr/traqueur/commands/api/CommandManager.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public abstract class CommandManager<T, S> {
5757
private final Map<String, Map<Integer, TabCompleter<S>>> completers;
5858

5959

60+
private final CommandInvoker<T,S> invoker;
61+
6062
/**
6163
* The message handler of the command manager.
6264
*/
@@ -87,6 +89,7 @@ public CommandManager(CommandPlatform<T,S> platform) {
8789
this.commands = new HashMap<>();
8890
this.typeConverters = new HashMap<>();
8991
this.completers = new HashMap<>();
92+
this.invoker = new CommandInvoker<>(this);
9093
this.registerInternalConverters();
9194
}
9295

@@ -500,6 +503,14 @@ private boolean applyParsing(String[] args, Arguments arguments, List<Argument<S
500503
return false;
501504
}
502505

506+
/**
507+
* Get the command invoker of the command manager.
508+
* @return The command invoker of the command manager.
509+
*/
510+
public CommandInvoker<T, S> getInvoker() {
511+
return invoker;
512+
}
513+
503514
/**
504515
* Register the internal converters of the command manager.
505516
*/

core/src/main/java/fr/traqueur/commands/api/CommandPlatform.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ public interface CommandPlatform<T, S> {
4040
*/
4141
boolean hasPermission(S sender, String permission);
4242

43+
/**
44+
* Checks if the sender is a player.
45+
*
46+
* @param sender The sender to check.
47+
* @return true if the sender is a player, false otherwise.
48+
*/
49+
boolean isPlayer(S sender);
50+
51+
/**
52+
* Sends a message to the sender.
53+
*
54+
* @param sender The sender to send the message to.
55+
* @param message The message to send.
56+
*/
57+
void sendMessage(S sender, String message);
58+
4359
/**
4460
* Adds a command to the platform.
4561
*

core/src/main/java/fr/traqueur/commands/api/updater/Updater.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,18 @@ public class Updater {
2626
}
2727
}
2828

29+
/**
30+
* Set the URL to use to check for the latest release
31+
* @param URL_LATEST_RELEASE The URL to use
32+
*/
2933
public static void setUrlLatestRelease(URL URL_LATEST_RELEASE) {
3034
Updater.URL_LATEST_RELEASE = URL_LATEST_RELEASE;
3135
}
3236

37+
/**
38+
* Set the logger to use for logging messages
39+
* @param LOGGER The logger to use
40+
*/
3341
public static void setLogger(Logger LOGGER) {
3442
Updater.LOGGER = LOGGER;
3543
}

core/src/main/java/fr/traqueur/commands/impl/arguments/EnumArgument.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,45 @@
77
import java.util.List;
88
import java.util.stream.Collectors;
99

10+
/**
11+
* An argument converter for enum types, allowing conversion from string to enum and providing tab completion.
12+
*
13+
* @param <T> The type of the enum.
14+
* @param <S> The type of the sender (e.g., player, console).
15+
*/
1016
public class EnumArgument<T extends Enum<T>, S> implements ArgumentConverter<Enum<T>>, TabCompleter<S> {
1117

18+
/**
19+
* Creates a new EnumArgument instance for the specified enum class.
20+
*
21+
* @param enumClass The class of the enum
22+
* @param <E> The type of the enum
23+
* @param <S> The type of the sender (e.g., player, console)
24+
* @return A new instance of EnumArgument
25+
*/
1226
public static <E extends Enum<E>, S> EnumArgument<E, S> of(Class<E> enumClass) {
1327
return new EnumArgument<>(enumClass);
1428
}
1529

30+
/**
31+
* The class of the enum type this argument converter handles.
32+
*/
1633
private final Class<T> clazz;
1734

35+
/**
36+
* Constructs a new EnumArgument for the specified enum class.
37+
*
38+
* @param clazz The class of the enum type
39+
*/
1840
public EnumArgument(Class<T> clazz) {
1941
this.clazz = clazz;
2042
}
2143

44+
/**
45+
* Gets the class of the enum type this argument converter handles.
46+
*
47+
* @return The enum class
48+
*/
2249
@Override
2350
public Enum<T> apply(String s) {
2451
if (s == null || s.isEmpty()) {
@@ -31,6 +58,11 @@ public Enum<T> apply(String s) {
3158
}
3259
}
3360

61+
/**
62+
* Gets the class of the enum type this argument converter handles.
63+
*
64+
* @return The enum class
65+
*/
3466
@Override
3567
public List<String> onCompletion(S sender, List<String> args) {
3668
return Arrays.stream(clazz.getEnumConstants())

0 commit comments

Comments
 (0)