Маппинг аргументов командной строки JAVA

116
07 октября 2019, 01:30

Есть некий framework, в котором парсится командная срока. Строка имеет вид: --file=simple.txt add --name=Jane --surname=Doe -age=21 --email=jane@gmail.com, где add это исполняющая команда, все, что стоит до команды - глобальные опции, которые нужны каждой(любой) команде, все что после команды - локальные опции, с которыми работает только конкретная команда. Список локальных опций содержится в описании самой команды. Глобальные опции - в отдельном списке. Во время парсинга глобальные и локальные опции сохраняются в двух картах Map<String, String> global, Map<String, String> local.Сейчас я смог реализовать парсинг так, что только ОДНА опция может быть глобальной (хард кодом написано i == 0).Вопрос, - как сделать так, чтобы глобальных опций могло быть несколько (в принципе, их может быть сколько угодно).Ниже привожу код. Класс парсера:

public class Parser {
private static final String LONG_OPTION_PREFIX = "--";
private static final String SHORT_OPTION_PREFIX = "-";
public void parseArguments(String[] args, List<CommandDescription> commands, List<OptionDescription> globalOptions)
        throws RuntimeException {
    ParsingResult result = cmdParser(args, commands, globalOptions);
    ParsingValidator validator = new ParsingValidator();
    if (validator.validate(result)) {
        Command command = result.getCommandDescription().getCommand();
        command.execute(result.getGlobal(), result.getLocal());
    }
}
private ParsingResult cmdParser(String[] args, List<CommandDescription> commands, List<OptionDescription> globalOptions) {
    ParsingValidator validator = new ParsingValidator();
    Map<String, String> global = new LinkedHashMap<>();
    Map<String, String> local = new LinkedHashMap<>();
    String key;
    String value;
    int separatorIndex;
    String commandName = null;
    CommandDescription command = null;
    List<OptionDescription> localOptions = null;
    for (int i = 0; i < args.length; i++) {
        if (args[i].startsWith(SHORT_OPTION_PREFIX)) {
            if (args[i].startsWith(LONG_OPTION_PREFIX)) {
                assertOK(args[i].length() > 2, "Empty long argument in the string:" + " " +
                        "'" + args[i] + "'");
                key = args[i].substring(2);
            } else {
                assertOK(args[i].length() > 2, "Empty short argument in the string:" + " " +
                        "'" + args[i] + "'");
                assertOK(args[i].charAt(2) == '=', "Short argument " + args[i] + " - must have one " +
                        "character, e.g. '-x'");
                key = args[i].substring(1);
            }
            separatorIndex = key.indexOf('=');
            value = key.substring(separatorIndex + 1);
            assertOK(value.length() > 0, "The option has no parameter !!!" + " " + key);
            assertOK(separatorIndex > 0, "The option has no parameter !!!" + " " + key);
            key = key.substring(0, separatorIndex);
            if (i == 0){
                global.put(validator.findOption(globalOptions, key), value);
            } else if (command == null) {
                throw new IllegalArgumentException("You have to enter command name after global arguments");
            } else {
                local.put(validator.findOption(localOptions, key), value);
            }
        } else if (commandName == null) {
            commandName = args[i];
            command = validator.findCommand(commands, commandName);
            localOptions = command.getLocalOptions();
            System.out.println(command);
        } else {
            throw new IllegalArgumentException("Command already exists, you can enter only single command");
        }
    }
    return new ParsingResult(global, local, command, globalOptions);
}
private void assertOK(boolean ok, String reason) {
    if (!ok) {
        throw new RuntimeException(reason);
    }
}}

Контейнер, для хранения результатов парсинга:

class ParsingResult {
private Map<String, String> global;
private Map<String, String> local;
private CommandDescription commandDescription;
private List<OptionDescription> globalOptions;
ParsingResult(Map<String, String> global, Map<String, String> local, CommandDescription commandDescription,
              List<OptionDescription> globalOptions) {
    this.global = global;
    this.local = local;
    this.commandDescription = commandDescription;
    this.globalOptions = globalOptions;
}
Map<String, String> getGlobal() {
    return global;
}
Map<String, String> getLocal() {
    return local;
}
CommandDescription getCommandDescription() {
    return commandDescription;
}
public List<OptionDescription> getGlobalOptions() {
    return globalOptions;
}}

Поиск команды и опции и их валидация:

class ParsingValidator {
CommandDescription findCommand(List<CommandDescription> commands, String commandName){
    return commands.stream().filter(commandDescription ->
            commandDescription.getName().equals(commandName)
                    || commandDescription.getAlias().equals(commandName))
            .findFirst()
            .orElseThrow(() -> new RuntimeException("Command not found for: " + commandName));
}
String findOption(List<OptionDescription> options, String option){
    return options.stream().filter(optionDescription ->
            optionDescription.getLongOptionName().equals(option)
                    || optionDescription.getShortOptionName().equals(option))
            .findFirst()
            .orElseThrow(() -> new RuntimeException("Option not found for: " + option))
            .getLongOptionName();
}
boolean validate(ParsingResult result){
    CommandDescription commandDescription = result.getCommandDescription();
    return validateMandatoryOptions(result.getGlobalOptions(), result.getGlobal(), commandDescription) &&
            validateMandatoryOptions(commandDescription.getLocalOptions(), result.getLocal(), commandDescription);
}
private boolean validateMandatoryOptions(List<OptionDescription> commandOptions, Map<String, String> parsedOptions,
                                         CommandDescription commandDescription) {
    for (OptionDescription option : commandOptions){
        if (!parsedOptions.containsKey(option.getLongOptionName()) && option.isMandatory()) {
            throw new IllegalArgumentException
                    ("For command:\n" + commandDescription + "\n\nshould be provided mandatory option:\n" + option);
        }
    }
    return true;
}}

Интерфейс, подписывающий метод, котрорый использует каждая команда для выполнения:

public interface Command {
void execute(Map<String, String> globalOptions, Map<String, String> localOptions);}

Класс описания опций:

public class OptionDescription {
private String longOptionName;
private String shortOptionName;
private String description;
private boolean mandatory;
public OptionDescription(String option, String shortOptionName, String description, boolean mandatory) {
    this.longOptionName = option;
    this.shortOptionName = shortOptionName;
    this.description = description;
    this.mandatory = mandatory;
}
public String getLongOptionName() {
    return longOptionName;
}
public String getShortOptionName() {
    return shortOptionName;
}
private String getDescription() {
    return description;
}
public boolean isMandatory() {
    return mandatory;
}
@Override
public String toString() {
    return "\nOption: " + "'" + getLongOptionName() + "'" + " or " + "'" + getShortOptionName() +
            "'" + "\nDescription: " + getDescription();
}}

Класс описания команд:

public class CommandDescription {
private String name;
private String alias;
private String description;
private Command command;
private List<OptionDescription> localOptions;
public CommandDescription(String name, String alias, String description, Command command, OptionDescription... localOptions) {
    this.name = name;
    this.alias = alias;
    this.description = description;
    this.command = command;
    this.localOptions = Arrays.asList(localOptions);
}
public String getName() {
    return name;
}
public String getAlias() {
    return alias;
}
private String getDescription() {
    return description;
}
public Command getCommand() {
    return command;
}
public List<OptionDescription> getLocalOptions() {
    return localOptions;
}
@Override
public String toString() {
    return "\nCommand: " + "'" + getName() + "'" + " or " + "'" + getAlias() + "'" + " - " + getDescription()
            + "\nmandatory or optional options: " + getLocalOptions();
}}
Answer 1

Вы Apache Commons CLI уже видели?

READ ALSO
Spring Test No qualifying bean of type с использование JavaConfig

Spring Test No qualifying bean of type с использование JavaConfig

недавно начал пользоваться JavaConfig в SpringИ сейчас решил попировать провести тесты, без использования xml

112
Сериализация java

Сериализация java

Есть задача: Необходимо написать класс который сериализует/десериализует Java BeansВ случае наличия циклических ссылок выкинуть exception

128
Регулярные выражения содержащие ()

Регулярные выражения содержащие ()

Возник вопросНеобходимо разобрать строку

135
Vkontakte API ошибка: error: invalid_client, error_description; client_id is incorrect

Vkontakte API ошибка: error: invalid_client, error_description; client_id is incorrect

Делаю авторизацию в вк по этому видео

146