Skip to content

Commit 57c89b6

Browse files
author
burdo
committed
introduce CommandErrorHandler
this goes to allow more precise handling of just exceptions by third-party libraries
1 parent 553fd2e commit 57c89b6

File tree

6 files changed

+49
-17
lines changed

6 files changed

+49
-17
lines changed

src/main/java/org/comroid/commands/impl/CommandManager.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import lombok.extern.log4j.Log4j2;
77
import org.apache.logging.log4j.Level;
88
import org.comroid.annotations.Doc;
9+
import org.comroid.annotations.Order;
910
import org.comroid.annotations.internal.Annotations;
1011
import org.comroid.api.Polyfill;
1112
import org.comroid.api.attr.Aliased;
1213
import org.comroid.api.data.seri.adp.JSON;
1314
import org.comroid.api.func.util.Invocable;
1415
import org.comroid.api.java.Activator;
1516
import org.comroid.api.java.ReflectionHelper;
17+
import org.comroid.api.java.StackTraceUtils;
1618
import org.comroid.api.tree.Container;
1719
import org.comroid.commands.Command;
1820
import org.comroid.commands.autofill.AutoFillOption;
@@ -22,6 +24,7 @@
2224
import org.comroid.commands.model.CommandCapability;
2325
import org.comroid.commands.model.CommandContextProvider;
2426
import org.comroid.commands.model.CommandError;
27+
import org.comroid.commands.model.CommandErrorHandler;
2528
import org.comroid.commands.model.CommandInfoProvider;
2629
import org.comroid.commands.model.CommandResponseHandler;
2730
import org.comroid.commands.model.permission.PermissionChecker;
@@ -36,8 +39,10 @@
3639
import java.util.ArrayList;
3740
import java.util.Arrays;
3841
import java.util.Collection;
42+
import java.util.Comparator;
3943
import java.util.HashSet;
4044
import java.util.Map;
45+
import java.util.Objects;
4146
import java.util.Optional;
4247
import java.util.Set;
4348
import java.util.UUID;
@@ -79,6 +84,7 @@ public final Set<Node> register(final Object target) {
7984
var attr = klass.getAnnotation(Command.class);
8085
if (attr != null) nodes = Set.of(Group.builder()
8186
.attribute(attr)
87+
.registeredTarget(target)
8288
.name(Command.EmptyAttribute.equals(attr.value()) ? klass.getSimpleName() : attr.value())
8389
.source(klass)
8490
.groups(groups)
@@ -97,8 +103,9 @@ public final void initialize() {
97103
}
98104

99105
public final Stream<AutoFillOption> autoComplete(
100-
CommandResponseHandler source, @Doc("Do not include currentValue") String[] fullCommand, String argName,
101-
@Nullable String currentValue, Object... extraArgs
106+
CommandResponseHandler source,
107+
@Doc("Do not include currentValue") String[] fullCommand,
108+
String argName, @Nullable String currentValue, Object... extraArgs
102109
) {
103110
var usage = createUsageBase(source, fullCommand, extraArgs);
104111
return autoComplete(usage, argName, currentValue);
@@ -111,6 +118,7 @@ public final CommandUsage createUsageBase(CommandResponseHandler source, String[
111118
.findAny()
112119
.orElseThrow(() -> new CommandError("No such command: " + Arrays.toString(fullCommand)));
113120
var builder = CommandUsage.builder()
121+
.registeredTarget(baseNode.getRegisteredTarget())
114122
.source(source)
115123
.manager(this)
116124
.fullCommand(trimFullCommand(fullCommand))
@@ -163,8 +171,7 @@ public final Stream<AutoFillOption> autoComplete(
163171
.map(n -> new AutoFillOption(n.getName(), n.getDescription()));
164172
} catch (Throwable e) {
165173
log.log(isDebug() ? Level.WARN : Level.DEBUG, "An error ocurred during command autocompletion", e);
166-
return of(usage.getSource().handleThrowable(e)).map(String::valueOf)
167-
.map(str -> new AutoFillOption(str, str));
174+
return of(tryHandleThrowable(usage, e)).map(String::valueOf).map(str -> new AutoFillOption(str, str));
168175
}
169176
}
170177

@@ -198,8 +205,7 @@ public final Stream<AutoFillOption> autoComplete(
198205
// parse user argument
199206
if (getCapabilities().contains(CommandCapability.NAMED_ARGS) && namedArgs != null) {
200207
useArgs[i] = namedArgs.get(commandParameter.getName());
201-
if (type.getTargetClass().isEnum())
202-
useArgs[i] = type.parse(String.valueOf(useArgs[i]));
208+
if (type.getTargetClass().isEnum()) useArgs[i] = type.parse(String.valueOf(useArgs[i]));
203209
} else {
204210
var str = usage.getArgumentStrings().get(commandParameter);
205211
useArgs[i] = type.parse(str);
@@ -217,10 +223,10 @@ public final Stream<AutoFillOption> autoComplete(
217223
// execute method
218224
result = response = call.getCallable().invoke(call.getTarget(), useArgs);
219225
} catch (CommandError err) {
220-
response = err.getResponse() == null ? usage.getSource().handleThrowable(err) : err.getResponse();
226+
response = err.getResponse() == null ? tryHandleThrowable(usage, err) : err.getResponse();
221227
} catch (Throwable e) {
222228
log.log(isDebug() ? Level.ERROR : Level.DEBUG, "An error ocurred during command execution", e);
223-
response = usage.getSource().handleThrowable(e);
229+
response = tryHandleThrowable(usage, e);
224230
}
225231
if (response != null) usage.getSource().handleResponse(usage, response, usage.getContext().toArray());
226232
return result;
@@ -240,10 +246,18 @@ protected Optional<AbstractCommandAdapter> adapter() {
240246
return streamChildren(AbstractCommandAdapter.class).findAny();
241247
}
242248

249+
private String tryHandleThrowable(CommandUsage usage, Throwable t) {
250+
return Stream.concat(children(CommandErrorHandler.class).sorted(Comparator.comparing(Object::getClass,
251+
Order.COMPARATOR)), of((CommandErrorHandler) usage.getSource()))
252+
.sorted(Comparator.comparingInt(handler -> handler instanceof CommandResponseHandler ? 1 : 0))
253+
.flatMap(handler -> handler.handleThrowable(usage, t).stream())
254+
.findFirst()
255+
.orElseGet(() -> StackTraceUtils.toString(t));
256+
}
257+
243258
private String[] trimFullCommand(String[] fullCommand) {
244259
var ls = new ArrayList<String>();
245-
for (var i = 0; i < fullCommand.length; i++) {
246-
var part = fullCommand[i];
260+
for (var part : fullCommand) {
247261
if (part.isBlank() && ls.getLast().isBlank()) break;
248262
ls.add(part);
249263
}
@@ -272,6 +286,7 @@ private void validatePermitted(CommandUsage usage, Callable callable) {
272286
private Group createGroupNode(@Nullable Object target, Class<?> source) {
273287
var attribute = Annotations.findAnnotations(Command.class, source).findFirst().orElseThrow().getAnnotation();
274288
var group = Group.builder()
289+
.registeredTarget(Objects.requireNonNullElse(target, source))
275290
.name(Command.EmptyAttribute.equals(attribute.value()) ? source.getSimpleName() : attribute.value())
276291
.attribute(attribute)
277292
.source(source)
@@ -293,6 +308,7 @@ private Group createGroupNode(@Nullable Object target, Class<?> source) {
293308
private Call createCallNode(@Nullable Object target, Method source) {
294309
var attribute = Annotations.findAnnotations(Command.class, source).findFirst().orElseThrow().getAnnotation();
295310
var call = Call.builder()
311+
.registeredTarget(Objects.requireNonNullElse(target, source))
296312
.name(Command.EmptyAttribute.equals(attribute.value()) ? source.getName() : attribute.value())
297313
.attribute(attribute)
298314
.target(target)
@@ -333,7 +349,7 @@ private org.comroid.commands.node.Parameter createParameterNode(int index, Param
333349
.index(index);
334350

335351
// init special types
336-
if (source.getType().isEnum()) builder.autoFillProvider(new EnumBasedAutoFillProvider(Polyfill.uncheckedCast(
352+
if (source.getType().isEnum()) builder.autoFillProvider(new EnumBasedAutoFillProvider<>(Polyfill.uncheckedCast(
337353
source.getType())));
338354
else if (attribute.autoFill().length > 0) builder.autoFillProvider(new ArrayBasedAutoFillProvider(attribute.autoFill()));
339355

src/main/java/org/comroid/commands/impl/CommandUsage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
public class CommandUsage {
2626
CommandManager manager;
2727
String[] fullCommand;
28+
@NotNull Object registeredTarget;
2829
@NotNull CommandResponseHandler source;
2930
@NotNull Callable baseNode;
3031
@Default Stack<Callable> stackTrace = new Stack<>();
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package org.comroid.commands.impl.minecraft;
22

3+
import org.comroid.commands.impl.CommandUsage;
34
import org.comroid.commands.model.CommandResponseHandler;
45

6+
import java.util.Optional;
7+
58
public interface MinecraftResponseHandler extends CommandResponseHandler {
69
@Override
7-
default String handleThrowable(Throwable throwable) {
8-
return "§c" + throwable.getClass().getSimpleName() + ": " + throwable.getMessage();
10+
default Optional<String> handleThrowable(CommandUsage usage, Throwable throwable) {
11+
return Optional.of("§c" + throwable.getClass().getSimpleName() + ": " + throwable.getMessage());
912
}
1013
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.comroid.commands.model;
2+
3+
import org.comroid.commands.impl.CommandUsage;
4+
5+
import java.util.Optional;
6+
7+
public interface CommandErrorHandler {
8+
Optional<String> handleThrowable(CommandUsage usage, Throwable throwable);
9+
}

src/main/java/org/comroid/commands/model/CommandResponseHandler.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,19 @@
99
import java.io.PrintStream;
1010
import java.io.StringWriter;
1111
import java.lang.reflect.InvocationTargetException;
12+
import java.util.Optional;
1213

1314
@FunctionalInterface
14-
public interface CommandResponseHandler {
15+
public interface CommandResponseHandler extends CommandErrorHandler {
1516
@Deprecated
1617
default Capitalization getDesiredKeyCapitalization() {
1718
return Capitalization.IDENTITY;
1819
}
1920

2021
void handleResponse(CommandUsage command, @NotNull Object response, Object... args);
2122

22-
default String handleThrowable(Throwable throwable) {
23+
@Override
24+
default Optional<String> handleThrowable(CommandUsage usage, Throwable throwable) {
2325
while (throwable instanceof InvocationTargetException itex) throwable = throwable.getCause();
2426
var msg = "%s: %s".formatted(throwable instanceof CommandError
2527
? throwable.getClass().getSimpleName()
@@ -34,7 +36,7 @@ default String handleThrowable(Throwable throwable) {
3436
.getClass()
3537
.getSimpleName() + ": " + throwable.getCause().getMessage()
3638
: throwable.getMessage());
37-
if (throwable instanceof CommandError) return msg;
39+
if (throwable instanceof CommandError) return Optional.of(msg);
3840
var buf = new StringWriter();
3941
var out = new PrintStream(new DelegateStream.Output(buf));
4042
out.println(msg);
@@ -47,6 +49,6 @@ default String handleThrowable(Throwable throwable) {
4749
StackTraceUtils.wrap(cause, out, true);
4850
var str = buf.toString();
4951
if (str.length() > 1950) str = str.substring(0, 1950);
50-
return str;
52+
return Optional.of(str);
5153
}
5254
}

src/main/java/org/comroid/commands/node/Callable.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
@SuperBuilder
1616
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
1717
public abstract class Callable extends Node {
18+
@NotNull Object registeredTarget;
1819
@NotNull Command attribute;
1920

2021
public @NotNull String getName() {

0 commit comments

Comments
 (0)