66import lombok .extern .log4j .Log4j2 ;
77import org .apache .logging .log4j .Level ;
88import org .comroid .annotations .Doc ;
9+ import org .comroid .annotations .Order ;
910import org .comroid .annotations .internal .Annotations ;
1011import org .comroid .api .Polyfill ;
1112import org .comroid .api .attr .Aliased ;
1213import org .comroid .api .data .seri .adp .JSON ;
1314import org .comroid .api .func .util .Invocable ;
1415import org .comroid .api .java .Activator ;
1516import org .comroid .api .java .ReflectionHelper ;
17+ import org .comroid .api .java .StackTraceUtils ;
1618import org .comroid .api .tree .Container ;
1719import org .comroid .commands .Command ;
1820import org .comroid .commands .autofill .AutoFillOption ;
2224import org .comroid .commands .model .CommandCapability ;
2325import org .comroid .commands .model .CommandContextProvider ;
2426import org .comroid .commands .model .CommandError ;
27+ import org .comroid .commands .model .CommandErrorHandler ;
2528import org .comroid .commands .model .CommandInfoProvider ;
2629import org .comroid .commands .model .CommandResponseHandler ;
2730import org .comroid .commands .model .permission .PermissionChecker ;
3639import java .util .ArrayList ;
3740import java .util .Arrays ;
3841import java .util .Collection ;
42+ import java .util .Comparator ;
3943import java .util .HashSet ;
4044import java .util .Map ;
45+ import java .util .Objects ;
4146import java .util .Optional ;
4247import java .util .Set ;
4348import 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
0 commit comments