ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

springshell自定义命令顺序

2022-06-06 02:00:08  阅读:149  来源: 互联网

标签:顺序 description 自定义 command result springshell AttributedStyle append BOLD


在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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有