package dev.alangomes.springspigot.picocli; import dev.alangomes.springspigot.picocli.conversion.PicocliConversorInjector; import lombok.Getter; import lombok.SneakyThrows; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.BeanFactory; import picocli.CommandLine; import picocli.CommandLine.IHelpSectionRenderer; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @Getter public class CommandLineDefinition { private final String beanName; private final Object instance; private final CommandLine.IFactory commandFactory; private final HashMap<String, CommandLineDefinition> subcommands = new HashMap<>(); CommandLineDefinition(Object instance, CommandLine.IFactory factory) { this.beanName = instance instanceof String ? (String) instance : null; this.instance = !(instance instanceof String) ? instance : null; this.commandFactory = factory; } void addSubcommand(String name, Object commandLine) { if (commandLine instanceof CommandLineDefinition) { subcommands.put(name, (CommandLineDefinition) commandLine); } else { subcommands.put(name, new CommandLineDefinition(commandLine, commandFactory)); } } public CommandLine build(BeanFactory factory) { CommandLine commandLine = doBuild(factory); overrideHelpRenderers(commandLine); overrideConverters(factory, commandLine); return commandLine; } private CommandLine doBuild(BeanFactory factory) { CommandLine commandLine = new CommandLine(beanName != null ? getBean(factory, beanName) : instance, commandFactory); subcommands.forEach((key, value) -> commandLine.addSubcommand(key, value.doBuild(factory))); return commandLine; } @SneakyThrows private Object getBean(BeanFactory factory, String name) { Object bean = factory.getBean(name); if (AopUtils.isAopProxy(bean)) { return ((Advised) bean).getTargetSource().getTarget(); } return bean; } public Set<String> getCommandNames() { return Collections.unmodifiableSet(subcommands.keySet()); } private void overrideConverters(BeanFactory beanFactory, CommandLine commandLine) { PicocliConversorInjector picocliConversorInjector = beanFactory.getBean(PicocliConversorInjector.class); picocliConversorInjector.injectConversor(commandLine); } private void overrideHelpRenderers(CommandLine commandLine) { Map<String, IHelpSectionRenderer> renderers = commandLine.getHelpSectionMap().keySet() .stream() .collect(Collectors.toMap(Function.identity(), (k) -> overrideRenderer(commandLine.getHelpSectionMap().get(k)))); commandLine.setHelpSectionMap(renderers); } private IHelpSectionRenderer overrideRenderer(IHelpSectionRenderer renderer) { // strip carriage returns when running inside a windows server return (h) -> renderer.render(h).replaceAll("\\r", ""); } }