系统相关
首页 > 系统相关> > springshell自定义命令顺序

springshell自定义命令顺序

作者:互联网

在springshell工程中,有时需要对方法进行顺序提示流程,方便用户理解.

我们需要对help内置命令进行自定义修改,并自定义修改其中的顺序规则

启动类中禁用内置help命令

public static void main(String[] args) {String[] disabledCommands = {"--spring.shell.command.help.enabled=false",
            "--spring.shell.command.exit.enabled=false",
            "--spring.shell.command.quit.enabled=false"};
        String[] fullArgs = StringUtils.concatenateStringArrays(args, disabledCommands);
        SpringApplication.run(SpringShellDemoApplication.class, fullArgs);
}

自定义help命令类

package com.example.springshelldemo.command;

import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.shell.*;
import org.springframework.shell.standard.CommandValueProvider;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.shell.standard.commands.Help;

import javax.validation.MessageInterpolator;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.metadata.ConstraintDescriptor;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.*;
import static java.util.stream.Collectors.toCollection;

@ShellComponent
public class HelpCommand implements Help.Command {
    public interface Command {
    }

    private final List<ParameterResolver> parameterResolvers;

    private CommandRegistry commandRegistry;

    private MessageInterpolator messageInterpolator = Validation.buildDefaultValidatorFactory()
            .getMessageInterpolator();

    @Autowired
    public HelpCommand(List<ParameterResolver> parameterResolvers) {
        this.parameterResolvers = parameterResolvers;
    }

    @Autowired // ctor injection impossible b/c of circular dependency
    public void setCommandRegistry(CommandRegistry commandRegistry) {
        this.commandRegistry = commandRegistry;
    }


    @Autowired(required = false)
    public void setValidatorFactory(ValidatorFactory validatorFactory) {
        this.messageInterpolator = validatorFactory.getMessageInterpolator();
    }


    @ShellMethod(value = "Display help about available commands.", prefix = "-")
    public CharSequence help(
            @ShellOption(defaultValue = ShellOption.NULL, valueProvider = CommandValueProvider.class, value = { "-C",
                    "--command" }, help = "The command to obtain help for.") String command)
            throws IOException {
        if (command == null) {
            return listCommands();
        }
        else {
            return documentCommand(command);
        }

    }

    /**
     * Return a description of a specific command. Uses a layout inspired by *nix man pages.
     */
    private CharSequence documentCommand(String command) {
        MethodTarget methodTarget = commandRegistry.listCommands().get(command);
        if (methodTarget == null) {
            throw new IllegalArgumentException("Unknown command '" + command + "'");
        }

        AttributedStringBuilder result = new AttributedStringBuilder().append("\n\n");
        List<ParameterDescription> parameterDescriptions = getParameterDescriptions(methodTarget);

        // NAME
        documentCommandName(result, command, methodTarget.getHelp());

        // SYNOPSYS
        documentSynopsys(result, command, parameterDescriptions);

        // OPTIONS
        documentOptions(result, parameterDescriptions);

        // ALSO KNOWN AS
        documentAliases(result, command, methodTarget);

        // AVAILABILITY
        documentAvailability(result, methodTarget);

        result.append("\n");
        return result;
    }

    private void documentCommandName(AttributedStringBuilder result, String command, String help) {
        result.append("NAME", AttributedStyle.BOLD).append("\n\t");
        result.append(command).append(" - ").append(help).append("\n\n");
    }

    private void documentSynopsys(AttributedStringBuilder result, String command,
                                  List<ParameterDescription> parameterDescriptions) {
        result.append("SYNOPSYS", AttributedStyle.BOLD).append("\n\t");
        result.append(command, AttributedStyle.BOLD);
        result.append(" ");

        for (ParameterDescription description : parameterDescriptions) {

            if (description.defaultValue().isPresent() && description.formal().length() > 0) {
                result.append("["); // Whole parameter is optional, as there is a default value (1)
            }
            List<String> keys = description.keys();
            if (!keys.isEmpty()) {
                if (!description.mandatoryKey()) {
                    result.append("["); // Specifying a key is optional (ie positional params). (2)
                }
                result.append(first(keys), AttributedStyle.BOLD);
                if (!description.mandatoryKey()) {
                    result.append("]"); // (close 2)
                }
                if (!description.formal().isEmpty()) {
                    result.append(" ");
                }
            }
            if (description.defaultValueWhenFlag().isPresent()) {
                result.append("["); // Parameter can be used as a toggle flag (3)
            }
            appendUnderlinedFormal(result, description);
            if (description.defaultValueWhenFlag().isPresent()) {
                result.append("]"); // (close 3)
            }
            if (description.defaultValue().isPresent() && description.formal().length() > 0) {
                result.append("]"); // (close 1)
            }
            result.append("  "); // two spaces between each param for better legibility
        }
        result.append("\n\n");
    }

    private void documentOptions(AttributedStringBuilder result, List<ParameterDescription> parameterDescriptions) {
        if (!parameterDescriptions.isEmpty()) {
            result.append("OPTIONS", AttributedStyle.BOLD).append("\n");
        }
        for (ParameterDescription description : parameterDescriptions) {
            result.append("\t").append(description.keys().stream().collect(Collectors.joining(" or ")),
                    AttributedStyle.BOLD);
            if (description.formal().length() > 0) {
                if (!description.keys().isEmpty()) {
                    result.append("  ");
                }
                description.defaultValueWhenFlag().ifPresent(f -> result.append('['));
                appendUnderlinedFormal(result, description);
                description.defaultValueWhenFlag().ifPresent(f -> result.append(']'));
                result.append("\n\t");
            }
            else if (description.keys().size() > 1) {
                result.append("\n\t");
            }
            result.append("\t");
            result.append(description.help()).append('\n');
            // Optional parameter
            if (description.defaultValue().isPresent()) {
                result
                        .append("\t\t[Optional, default = ", AttributedStyle.BOLD)
                        .append(description.defaultValue().get(), AttributedStyle.BOLD.italic());
                description.defaultValueWhenFlag().ifPresent(
                        s -> result.append(", or ", AttributedStyle.BOLD)
                                .append(s, AttributedStyle.BOLD.italic())
                                .append(" if used as a flag", AttributedStyle.BOLD));

                result.append("]", AttributedStyle.BOLD);
            } // Mandatory parameter, but with a default when used as a flag
            else if (description.defaultValueWhenFlag().isPresent()) {
                result
                        .append("\t\t[Mandatory, default = ", AttributedStyle.BOLD)
                        .append(description.defaultValueWhenFlag().get(), AttributedStyle.BOLD.italic())
                        .append(" when used as a flag]", AttributedStyle.BOLD);
            } // true mandatory parameter
            else {
                result.append("\t\t[Mandatory]", AttributedStyle.BOLD);
            }
            result.append('\n');
            if (description.elementDescriptor() != null) {
                for (ConstraintDescriptor<?> constraintDescriptor : description.elementDescriptor()
                        .getConstraintDescriptors()) {
                    String friendlyConstraint = messageInterpolator.interpolate(
                            constraintDescriptor.getMessageTemplate(), new HelpCommand.DummyContext(constraintDescriptor));
                    result.append("\t\t[" + friendlyConstraint + "]\n", AttributedStyle.BOLD);
                }
            }
            result.append('\n');
        }
    }

    private void documentAliases(AttributedStringBuilder result, String command, MethodTarget methodTarget) {
        Set<String> aliases = commandRegistry.listCommands().entrySet().stream()
                .filter(e -> e.getValue().equals(methodTarget))
                .map(Map.Entry::getKey)
                .filter(c -> !command.equals(c))
                .collect(toCollection(TreeSet::new));

        if (!aliases.isEmpty()) {
            result.append("ALSO KNOWN AS", AttributedStyle.BOLD).append("\n");
            for (String alias : aliases) {
                result.append('\t').append(alias).append('\n');
            }
        }
    }

    private void documentAvailability(AttributedStringBuilder result, MethodTarget methodTarget) {
        Availability availability = methodTarget.getAvailability();
        if (!availability.isAvailable()) {
            result.append("CURRENTLY UNAVAILABLE", AttributedStyle.BOLD).append("\n");
            result.append('\t').append("This command is currently not available because ")
                    .append(availability.getReason())
                    .append(".\n");
        }
    }

    private String first(List<String> keys) {
        return keys.iterator().next();
    }

    private CharSequence listCommands() {
        Map<String, MethodTarget> commandsByName = commandRegistry.listCommands();

        SortedMap<String, Map<String, MethodTarget>> commandsByGroupAndName = commandsByName.entrySet().stream()
                .collect(groupingBy(e -> e.getValue().getGroup(), TreeMap::new, // group by and sort by command group
                        toMap(Map.Entry::getKey, Map.Entry::getValue)));

        AttributedStringBuilder result = new AttributedStringBuilder();
        result.append("AVAILABLE COMMANDS\n\n", AttributedStyle.BOLD);

        // display groups, sorted alphabetically, "Default" first
        commandsByGroupAndName.forEach((group, commandsInGroup) -> {
            result.append("".equals(group) ? "Default" : group, AttributedStyle.BOLD).append('\n');

            Map<MethodTarget, SortedSet<String>> commandNamesByMethod = commandsInGroup.entrySet().stream()
                    .collect(groupingBy(Map.Entry::getValue, // group by command method
                            mapping(Map.Entry::getKey, toCollection(TreeSet::new)))); // sort command names

            // display commands, sorted alphabetically by their first alias
            commandNamesByMethod.entrySet().stream().sorted(sortByFirstCommandName()).forEach(e -> {
                result
                        .append(isAvailable(e.getKey()) ? "        " : "      * ")
                        .append(String.join(", ", e.getValue()), AttributedStyle.BOLD)
                        .append(": ")
                        .append(e.getKey().getHelp())
                        .append('\n');
            });

            result.append('\n');
        });

        if (commandsByName.values().stream().distinct().anyMatch(m -> !isAvailable(m))) {
            result.append("Commands marked with (*) are currently unavailable.\nType `help <command>` to learn more.\n\n");
        }

        return result;
    }

    private Comparator<Map.Entry<MethodTarget, SortedSet<String>>> sortByFirstCommandName() {
        return Comparator.comparing(e -> e.getValue().first());
    }

    private boolean isAvailable(MethodTarget methodTarget) {
        return methodTarget.getAvailability().isAvailable();
    }

    private void appendUnderlinedFormal(AttributedStringBuilder result, ParameterDescription description) {
        for (char c : description.formal().toCharArray()) {
            if (c != ' ') {
                result.append("" + c, AttributedStyle.DEFAULT.underline());
            }
            else {
                result.append(c);
            }
        }
    }

    private List<ParameterDescription> getParameterDescriptions(MethodTarget methodTarget) {
        return Utils.createMethodParameters(methodTarget.getMethod())
                .flatMap(mp -> parameterResolvers.stream().filter(pr -> pr.supports(mp)).limit(1L)
                        .flatMap(pr -> pr.describe(mp)))
                .collect(Collectors.toList());

    }

    private static class DummyContext implements MessageInterpolator.Context {

        private final ConstraintDescriptor<?> descriptor;

        private DummyContext(ConstraintDescriptor<?> descriptor) {
            this.descriptor = descriptor;
        }

        @Override
        public ConstraintDescriptor<?> getConstraintDescriptor() {
            return descriptor;
        }

        @Override
        public Object getValidatedValue() {
            return null;
        }

        @Override
        public <T> T unwrap(Class<T> type) {
            return null;
        }
    }
}

注意其中

 // display commands, sorted alphabetically by their first alias
            commandNamesByMethod.entrySet().stream().sorted(sortByFirstCommandName()).forEach(e -> {
                result
                        .append(isAvailable(e.getKey()) ? "        " : "      * ")
                        .append(String.join(", ", e.getValue()), AttributedStyle.BOLD)
                        .append(": ")
                        .append(e.getKey().getHelp())
                        .append('\n');
            });

此处即为对方法排序处理的逻辑,此处可自定义修改如

    // Arrays.asList没有remove方法. 固定顺序方法列表
    private final List<String> commandSortList = Arrays.asList("sum","add-array","checklength","connect","echo","echoc","shutdown","shutdown-system","help","exit","clear","stacktrace");
            commandSortList.stream().forEach(commandSort -> {
                commandNamesByMethod.entrySet().stream().sorted(sortByFirstCommandName()).forEach(e -> {
                    // System.out.println("key: " + e.getKey() + ",value: " + e.getValue());
                    if (e.getValue().contains(commandSort)) {
                        result
                                .append(isAvailable(e.getKey()) ? "        " : "      * ")
                                .append(String.join(", ", e.getValue()), AttributedStyle.BOLD)
                                .append(": ")
                                .append(e.getKey().getHelp())
                                .append('\n');
                    }
                });
            });

 

标签:顺序,description,自定义,command,result,springshell,AttributedStyle,append,BOLD
来源: https://www.cnblogs.com/chencoolandclear/p/16332955.html