Skip to content

Commit fe7007d

Browse files
committed
Added Flag and Option annotations for parsing options via arguments
1 parent c84823c commit fe7007d

File tree

10 files changed

+229
-24
lines changed

10 files changed

+229
-24
lines changed

src/main/java/me/despical/commandframework/CommandArguments.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929

3030
import java.text.MessageFormat;
3131
import java.util.Arrays;
32+
import java.util.List;
33+
import java.util.Map;
3234
import java.util.Optional;
35+
import java.util.Set;
3336

3437
/**
3538
* A utility class to use command arguments without external
@@ -41,10 +44,14 @@
4144
*/
4245
public final class CommandArguments {
4346

44-
final me.despical.commandframework.annotations.Command command;
47+
private Set<String> parsedFlags;
48+
private Map<String, List<String>> parsedArguments;
49+
50+
private final me.despical.commandframework.annotations.Command command;
4551
private final CommandSender commandSender;
4652
private final Command bukkitCommand;
47-
private final String label, arguments[];
53+
private final String label;
54+
private final String [] arguments;
4855

4956
CommandArguments(CommandSender commandSender,
5057
Command bukkitCommand,
@@ -447,4 +454,25 @@ public boolean isFloatingDecimal(String string) {
447454
public boolean checkCooldown() {
448455
return CommandFramework.instance.getCooldownManager().hasCooldown(this);
449456
}
457+
458+
void setParsedArguments(Map<String, List<String>> parsedArguments) {
459+
this.parsedArguments = parsedArguments;
460+
}
461+
462+
@Nullable
463+
public List<String> getOption(final @NotNull String option) {
464+
return this.parsedArguments.get(option);
465+
}
466+
467+
public Optional<List<String>> findOption(final @NotNull String option) {
468+
return Optional.ofNullable(this.getOption(option));
469+
}
470+
471+
void setParsedFlags(Set<String> parsedFlags) {
472+
this.parsedFlags = parsedFlags;
473+
}
474+
475+
public boolean isFlagPresent(final @NotNull String flag) {
476+
return this.parsedFlags != null && this.parsedFlags.contains(flag);
477+
}
450478
}

src/main/java/me/despical/commandframework/CommandFramework.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,20 @@ public class CommandFramework extends CommandHandler {
5555

5656
protected final Plugin plugin;
5757
private final OptionManager optionManager;
58-
private final ParameterHandler parameterHandler;
5958
private final CommandRegistry registry;
6059

6160
public CommandFramework(@NotNull Plugin plugin) {
6261
this.checkRelocation();
6362
this.checkIsAlreadyInitialized();
6463

6564
this.plugin = plugin;
66-
this.optionManager = new OptionManager();
6765
this.registry = new CommandRegistry();
68-
this.parameterHandler = new ParameterHandler();
66+
this.optionManager = new OptionManager();
6967
this.initializeLogger();
7068
super.setRegistry(this);
7169
}
7270

7371
private void checkRelocation() {
74-
if (this.isOptionEnabled(Option.DEBUG)) return;
75-
7672
String suppressRelocation = System.getProperty("commandframework.suppressrelocation");
7773

7874
if ("true".equals(suppressRelocation)) return;
@@ -205,11 +201,6 @@ final CommandRegistry getRegistry() {
205201
return registry;
206202
}
207203

208-
@ApiStatus.Internal
209-
final ParameterHandler getParameterHandler() {
210-
return parameterHandler;
211-
}
212-
213204
@ApiStatus.Internal
214205
final boolean checkConfirmation(CommandSender sender, final Command command, final Method method) {
215206
if (!isOptionEnabled(Option.CONFIRMATIONS)) return false;

src/main/java/me/despical/commandframework/CommandHandler.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package me.despical.commandframework;
22

3+
import me.despical.commandframework.annotations.Option;
34
import me.despical.commandframework.annotations.Command;
45
import me.despical.commandframework.annotations.Completer;
56
import me.despical.commandframework.exceptions.CooldownException;
6-
import me.despical.commandframework.options.Option;
7+
import me.despical.commandframework.parser.OptionParser;
78
import me.despical.commandframework.utils.Utils;
89
import org.bukkit.command.CommandExecutor;
910
import org.bukkit.command.CommandSender;
@@ -31,14 +32,14 @@
3132
*/
3233
@ApiStatus.Internal
3334
@ApiStatus.NonExtendable
34-
public abstract class CommandHandler implements CommandExecutor, TabCompleter {
35+
abstract class CommandHandler implements CommandExecutor, TabCompleter {
3536

3637
private CommandRegistry registry;
37-
private CommandFramework commandFramework;
38+
protected ParameterHandler parameterHandler;
3839

3940
void setRegistry(CommandFramework commandFramework) {
40-
this.commandFramework = commandFramework;
4141
this.registry = commandFramework.getRegistry();
42+
this.parameterHandler = new ParameterHandler();
4243
}
4344

4445
@Override
@@ -82,20 +83,28 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull org.bukkit.comm
8283
}
8384

8485
final Method method = entry.getValue().getKey();
86+
final CommandFramework commandFramework = CommandFramework.getInstance();
8587

8688
if (commandFramework.checkConfirmation(sender, command, method)) {
8789
return true;
8890
}
8991

90-
if (!this.commandFramework.isOptionEnabled(Option.CUSTOM_COOLDOWN_CHECKER) && commandFramework.getCooldownManager().hasCooldown(arguments, command, method)) {
92+
if (commandFramework.getCooldownManager().hasCooldown(arguments, command, method)) {
9193
return true;
9294
}
9395

96+
if (method.getAnnotationsByType(Option.class).length > 0) {
97+
OptionParser optionParser = new OptionParser(newArgs, method);
98+
99+
arguments.setParsedArguments(optionParser.parseOptions());
100+
arguments.setParsedFlags(optionParser.parseFlags());
101+
}
102+
94103
final Runnable invocation = () -> {
95104
try {
96105
final Object instance = entry.getValue().getValue();
97106

98-
method.invoke(instance, commandFramework.getParameterHandler().getParameterArray(method, arguments));
107+
method.invoke(instance, parameterHandler.getParameterArray(method, arguments));
99108
} catch (Exception exception) {
100109
if (exception.getCause() instanceof CooldownException) {
101110
return;
@@ -133,7 +142,7 @@ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull org.bu
133142
final Object instance = entry.getValue().getValue();
134143
final String[] splitName = entry.getKey().name().split("\\.");
135144
final String[] newArgs = Arrays.copyOfRange(args, splitName.length - 1, args.length);
136-
final Object completer = method.invoke(instance, commandFramework.getParameterHandler().getParameterArray(method, new CommandArguments(sender, cmd, null, label, newArgs)));
145+
final Object completer = method.invoke(instance, parameterHandler.getParameterArray(method, new CommandArguments(sender, cmd, null, label, newArgs)));
137146

138147
return (List<String>) completer;
139148
} catch (Exception exception) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package me.despical.commandframework.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Repeatable;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
/**
10+
* @author Despical
11+
* <p>
12+
* Created at 20.09.2024
13+
*/
14+
@Target(ElementType.METHOD)
15+
@Retention(RetentionPolicy.RUNTIME)
16+
@Repeatable(Flag.FlagContainer.class)
17+
public @interface Flag {
18+
19+
String[] value();
20+
21+
String prefix() default "--";
22+
23+
@Target(ElementType.METHOD)
24+
@Retention(RetentionPolicy.RUNTIME)
25+
@interface FlagContainer {
26+
27+
Flag[] value();
28+
}
29+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package me.despical.commandframework.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Repeatable;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
/**
10+
* @author Despical
11+
* <p>
12+
* Created at 20.09.2024
13+
*/
14+
@Target(ElementType.METHOD)
15+
@Retention(RetentionPolicy.RUNTIME)
16+
@Repeatable(Option.OptionContainer.class)
17+
public @interface Option {
18+
19+
String name();
20+
21+
String prefix() default "--";
22+
23+
String valueSeparator() default ",";
24+
25+
String keySeparator() default "=";
26+
27+
boolean allowSeparating() default true;
28+
29+
@Target(ElementType.METHOD)
30+
@Retention(RetentionPolicy.RUNTIME)
31+
@interface OptionContainer {
32+
33+
Option[] value();
34+
}
35+
}

src/main/java/me/despical/commandframework/cooldown/CooldownManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public boolean hasCooldown(CommandArguments arguments) {
6161
}
6262

6363
public boolean hasCooldown(final CommandArguments arguments, final Command command, final Method method) {
64+
if (commandFramework.isOptionEnabled(Option.CUSTOM_COOLDOWN_CHECKER)) return false;
6465
if (method == null) return false;
6566
if (!method.isAnnotationPresent(Cooldown.class)) return false;
6667

src/main/java/me/despical/commandframework/exceptions/CommandException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@
2828
* Created at 23.01.2024
2929
*/
3030
@ApiStatus.Internal
31-
public final class CommandException extends RuntimeException {
31+
public class CommandException extends RuntimeException {
32+
33+
public CommandException() {
34+
super();
35+
}
3236

3337
public CommandException(final String message) {
3438
super(message);
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package me.despical.commandframework.exceptions;
22

3-
import org.jetbrains.annotations.ApiStatus;
4-
53
/**
64
* @author Despical
75
* <p>
86
* Created at 18.07.2024
97
*/
10-
@ApiStatus.Internal
11-
public final class CooldownException extends RuntimeException {
8+
public class CooldownException extends CommandException {
129
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @author Despical
3+
* <p>
4+
* Created at 20.09.2024
5+
*/
6+
@ApiStatus.Internal
7+
package me.despical.commandframework.exceptions;
8+
9+
import org.jetbrains.annotations.ApiStatus;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package me.despical.commandframework.parser;
2+
3+
import me.despical.commandframework.annotations.Flag;
4+
import me.despical.commandframework.annotations.Option;
5+
6+
import java.lang.reflect.Method;
7+
import java.util.*;
8+
import java.util.regex.Pattern;
9+
10+
/**
11+
* @author Despical
12+
* <p>
13+
* Created at 20.09.2024
14+
*/
15+
public class OptionParser {
16+
17+
private final Flag[] flags;
18+
private final Option[] options;
19+
private final Set<String> arguments;
20+
private final Set<String> parsedFlags;
21+
private final Map<String, List<String>> parsedOptions;
22+
23+
public OptionParser(String[] arguments, Method method) {
24+
this.flags = method.getAnnotationsByType(Flag.class);
25+
this.options = method.getAnnotationsByType(Option.class);
26+
this.arguments = new HashSet<>(Arrays.asList(arguments));
27+
this.parsedFlags = new HashSet<>();
28+
this.parsedOptions = new HashMap<>();
29+
}
30+
31+
public Map<String, List<String>> parseOptions() {
32+
for (Option option : options) {
33+
this.parseOption(option);
34+
}
35+
36+
return this.parsedOptions;
37+
}
38+
39+
public Set<String> parseFlags() {
40+
for (Flag flag : flags) {
41+
this.parseFlag(flag);
42+
}
43+
44+
return this.parsedFlags;
45+
}
46+
47+
private void parseOption(Option option) {
48+
String prefix = option.prefix();
49+
String keySeparator = Pattern.quote(option.keySeparator());
50+
String valueSeparator = Pattern.quote(option.valueSeparator());
51+
Iterator<String> iterator = arguments.iterator();
52+
53+
while (iterator.hasNext()) {
54+
String argument = iterator.next();
55+
56+
if (!argument.startsWith(prefix)) {
57+
continue;
58+
}
59+
60+
if (option.allowSeparating() && !argument.contains(keySeparator)) {
61+
continue;
62+
}
63+
64+
String[] options = argument.substring(prefix.length()).split(keySeparator);
65+
66+
if (!option.allowSeparating()) {
67+
String value = options.length <= 1 ? "" : options[1];
68+
this.parsedOptions.put(option.name(), Collections.singletonList(value));
69+
70+
iterator.remove();
71+
continue;
72+
}
73+
74+
String[] values = options[1].split(valueSeparator);
75+
this.parsedOptions.put(option.name(), Arrays.asList(values));
76+
77+
iterator.remove();
78+
}
79+
}
80+
81+
private void parseFlag(Flag flag) {
82+
String prefix = flag.prefix();
83+
84+
outer:
85+
for (String argument : this.arguments) {
86+
if (!argument.startsWith(prefix)) {
87+
continue;
88+
}
89+
90+
String foundFlag = argument.substring(prefix.length());
91+
92+
for (String flagName : flag.value()) {
93+
if (!flagName.equals(foundFlag)) {
94+
continue;
95+
}
96+
97+
this.parsedFlags.add(flagName);
98+
continue outer;
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)