Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -71,6 +71,24 @@ ProcessedCommand<Command<CI>, CI> parseAndPopulate(InvocationProviders invocatio
AeshContext aeshContext)
throws CommandLineParserException, OptionValidatorException;

/**
* Parse and populate the command with CommandContext for parent command injection.
*
* @param invocationProviders providers
* @param aeshContext the aesh context
* @param commandContext the command context for sub-command mode (may be null)
* @return the processed command
* @throws CommandLineParserException on parse error
* @throws OptionValidatorException on validation error
*/
default ProcessedCommand<Command<CI>, CI> parseAndPopulate(InvocationProviders invocationProviders,
AeshContext aeshContext,
org.aesh.command.impl.context.CommandContext commandContext)
throws CommandLineParserException, OptionValidatorException {
// Default implementation ignores context for backward compatibility
return parseAndPopulate(invocationProviders, aeshContext);
}

CommandContainerResult executeCommand(ParsedLine line, InvocationProviders invocationProviders,
AeshContext aeshContext,
CI commandInvocation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ public void emptyLine() {
public ProcessedCommand<Command<CI>, CI> parseAndPopulate(InvocationProviders invocationProviders,
AeshContext aeshContext)
throws CommandLineParserException, OptionValidatorException {
return parseAndPopulate(invocationProviders, aeshContext, null);
}

@Override
public ProcessedCommand<Command<CI>, CI> parseAndPopulate(InvocationProviders invocationProviders,
AeshContext aeshContext,
org.aesh.command.impl.context.CommandContext commandContext)
throws CommandLineParserException, OptionValidatorException {
if(lines.isEmpty())
return null;
ParsedLine aeshLine = lines.poll();
Expand All @@ -75,8 +83,14 @@ public ProcessedCommand<Command<CI>, CI> parseAndPopulate(InvocationProviders in
if (getParser().parsedCommand() == null) {
throw new CommandLineParserException("Command and/or sub-command is not valid!");
}
getParser().parsedCommand().getCommandPopulator().populateObject(getParser().parsedCommand().getProcessedCommand(),
invocationProviders, aeshContext, CommandLineParser.Mode.VALIDATE);
// Use context-aware populateObject if CommandContext is available
if (commandContext != null && commandContext.isInSubCommandMode()) {
getParser().parsedCommand().getCommandPopulator().populateObject(getParser().parsedCommand().getProcessedCommand(),
invocationProviders, aeshContext, CommandLineParser.Mode.VALIDATE, commandContext);
} else {
getParser().parsedCommand().getCommandPopulator().populateObject(getParser().parsedCommand().getProcessedCommand(),
invocationProviders, aeshContext, CommandLineParser.Mode.VALIDATE);
}
return getParser().parsedCommand().getProcessedCommand();
}

Expand Down
58 changes: 57 additions & 1 deletion aesh/src/main/java/org/aesh/command/impl/Executions.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
import org.aesh.command.container.CommandContainer;
import org.aesh.command.impl.completer.CompleterData;
import org.aesh.command.impl.completer.NullOptionCompleter;
import org.aesh.command.impl.context.CommandContext;
import org.aesh.command.impl.internal.OptionType;
import org.aesh.command.impl.internal.ParsedCommand;
import org.aesh.command.impl.internal.ProcessedCommand;
import org.aesh.command.impl.internal.ProcessedOption;
import org.aesh.command.option.ParentCommand;
import org.aesh.command.impl.operator.AndOperator;
import org.aesh.command.impl.operator.AppendOutputRedirectionOperator;
import org.aesh.command.impl.operator.ConfigurationOperator;
Expand Down Expand Up @@ -60,6 +62,8 @@
import org.aesh.selector.Selector;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -115,7 +119,9 @@ public Command<T> getCommand() {
@Override
public void populateCommand() throws CommandLineParserException, OptionValidatorException {
if (!populated) {
cmd = commandContainer.parseAndPopulate(runtime.invocationProviders(), runtime.getAeshContext());
// Get command context for inherited option injection
CommandContext cmdContext = getCommandInvocation().getCommandContext();
cmd = commandContainer.parseAndPopulate(runtime.invocationProviders(), runtime.getAeshContext(), cmdContext);
populated = true;
}
}
Expand All @@ -130,6 +136,13 @@ public CommandResult execute() throws CommandException, InterruptedException, Co
CommandLineParserException, OptionValidatorException {
//first we need to parse and populate the command line
populateCommand();

// Inject @ParentCommand fields if in sub-command mode
CommandContext cmdContext = getCommandInvocation().getCommandContext();
if (cmdContext != null && cmdContext.isInSubCommandMode()) {
injectParentCommands(cmd.getCommand(), cmdContext);
}

//finally we set the command that should be executed
executable.setCommand(cmd.getCommand());

Expand Down Expand Up @@ -434,4 +447,47 @@ private static Operator buildOperator(OperatorType op, AeshContext context) {
}
throw new IllegalArgumentException("Unsupported operator " + op);
}

/**
* Inject parent command instances into fields annotated with @ParentCommand.
*/
private static void injectParentCommands(Object command, CommandContext commandContext) {
List<Field> fields = getAllFields(command.getClass());
for (Field field : fields) {
if (field.isAnnotationPresent(ParentCommand.class)) {
Class<?> fieldType = field.getType();

// Find matching parent command in context stack
@SuppressWarnings("unchecked")
Command<?> parent = commandContext.getParentCommand(
(Class<? extends Command<?>>) fieldType.asSubclass(Command.class));

if (parent != null) {
try {
if (!Modifier.isPublic(field.getModifiers())) {
field.setAccessible(true);
}
field.set(command, parent);
} catch (IllegalAccessException e) {
// Log warning but continue
e.printStackTrace();
}
}
}
}
}

/**
* Get all fields from a class and its superclasses.
*/
private static List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
fields.add(field);
}
clazz = clazz.getSuperclass();
}
return fields;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ private static void processField(ProcessedCommand processedCommand, Field field)
.overrideRequired(o.overrideRequired())
.negatable(o.negatable())
.negationPrefix(o.negationPrefix())
.inherited(o.inherited())
.build()
);
}
Expand Down Expand Up @@ -311,6 +312,7 @@ else if((arg = field.getAnnotation(Argument.class)) != null) {
.renderer(arg.renderer())
.parser(arg.parser())
.overrideRequired(arg.overrideRequired())
.inherited(arg.inherited())
.build()
);
}
Expand Down
Loading