10 votes

Analyseur Java CLI

Je sais que cette question a déjà été posée, mais je recherche un analyseur Java cli avec une fonctionnalité spécifique. Je veux qu'il soit capable de définir l'arbre de la ligne de commande, en utilisant des sous-commandes (et plus d'un niveau de profondeur). Ainsi, je peux avoir 3-4 niveaux de commandes avant d'arriver aux options. Et ces sous-commandes sont mutuellement exclusives. Merci

14voto

rodion Points 6275

Peut être fait avec JCommander . Chaque JCommander est, par essence, une commande comportant un nombre arbitraire de paramètres et/ou un nombre arbitraire de sous-commandes imbriquées, où la commande supérieure JCommander est l'objet Racine commande. Les paramètres des commandes sont toujours spécifiques à la commande pour laquelle ils ont été déclarés et n'interfèrent pas avec les paramètres des autres commandes. L'interface d'ajout d'une sous-commande n'est pas très intuitive mais est possible (voir l'onglet addCommand méthode())

Voici une classe test de démonstration :

public class Test{

@Test
public void nestedSubCommandTest() {
    GeneralOptions generalOpts = new GeneralOptions();
    JCommander jc = new JCommander(generalOpts);

    Command command = new Command();
    JCommander jc_command = addCommand(jc, "command", command);

    SubCommand1 subcommand1 = new SubCommand1();
    JCommander jc_subcommand1 = addCommand(jc_command, "subcommand1",
            subcommand1);

    SubCommand2 subcommand2 = new SubCommand2();
    JCommander jc_subcommand2 = addCommand(jc_subcommand1, "subcommand2",
            subcommand2);

    SubCommand3 subcommand3 = new SubCommand3();
    addCommand(jc_subcommand2, "subcommand3", subcommand3);

    jc.parse("--general-opt", 
        "command", "--opt", 
        "subcommand1",
        "subcommand2", "--sub-opt2", 
        "subcommand3", "--sub-opt3");

    assertTrue(generalOpts.opt);// --general-opt was set
    assertTrue(command.opt);// command --opt was set
    assertFalse(subcommand1.opt);// subcommand1 --sub-opt1 was not set
    assertTrue(subcommand2.opt);// subcommand2 --sub-opt2 was set
    assertTrue(subcommand3.opt);// subcommand3 --sub-opt3 was set
}

private static JCommander addCommand(JCommander parentCommand,
        String commandName, Object commandObject) {
    parentCommand.addCommand(commandName, commandObject);
    return parentCommand.getCommands().get(commandName);
}

public static class GeneralOptions {
    @Parameter(names = "--general-opt")
    public boolean opt;
}

@Parameters
public static class Command {
    @Parameter(names = "--opt")
    public boolean opt;
}

@Parameters
public static class SubCommand1 {
    @Parameter(names = "--sub-opt1")
    public boolean opt;
}

@Parameters
public static class SubCommand2 {
    @Parameter(names = "--sub-opt2")
    public boolean opt;
}

@Parameters
public static class SubCommand3 {
    @Parameter(names = "--sub-opt3")
    public boolean opt;
}
}

Edit : Comment réutiliser les commandes.

Solution 1, utiliser l'héritage :

  public class CommonArgs{
    @Parameter(names="--common-opt")
    public boolean isCommonOpt;
  }

  @Parameters(description = "my command 1")
  public class MyCommand1 extends CommonArgs{}

  @Parameters(description = "my command 2")
  public class MyCommand2 extends CommonArgs{}

Je pense que l'utilisation et le comportement sont tout à fait évidents pour celui-ci. Le seul inconvénient est que vous ne pouvez étendre qu'à partir d'une seule classe, ce qui peut limiter la réutilisation dans le futur.

Solution 2, en utilisant le modèle de composition (voir le doc ici ):

  public class CommonArgs{
    @Parameter(names="--common-opt")
    public boolean isCommonOpt;
  }

  @Parameters(description = "my command 1")
  public class MyCommand1{
    @ParametersDelegate
    public CommonArgs commonArgs = new CommonArgs();
  }

  @Parameters(description = "my command 2")
  public class MyCommand2{
    @ParametersDelegate
    public CommonArgs commonArgs = new CommonArgs();
  }

Ici, l'emboîtement commonArgs seront traités comme s'ils étaient des paramètres directs de la classe de commande. Vous pouvez ajouter autant de délégués que vous le souhaitez, ou même imbriquer des délégués dans d'autres délégués et ainsi de suite. Pour obtenir la valeur de l'option déléguée après le parsing, il suffit de faire myCommand1.commonArgs.isCommonOpt etc.

5voto

Remko Popma Points 19015

picocli supporte des sous-commandes imbriquées jusqu'à une profondeur arbitraire.

CommandLine commandLine = new CommandLine(new MainCommand())
        .addSubcommand("cmd1", new ChildCommand1()) // 1st level
        .addSubcommand("cmd2", new ChildCommand2())
        .addSubcommand("cmd3", new CommandLine(new ChildCommand3()) // 2nd level
                .addSubcommand("cmd3sub1", new GrandChild3Command1())
                .addSubcommand("cmd3sub2", new GrandChild3Command2())
                .addSubcommand("cmd3sub3", new CommandLine(new GrandChild3Command3()) // 3rd
                        .addSubcommand("cmd3sub3sub1", new GreatGrandChild3Command3_1())
                        .addSubcommand("cmd3sub3sub2", new GreatGrandChild3Command3_2())
                                // etc
                )
        );

Vous aimerez peut-être aussi son aide à l'utilisation des styles et des couleurs ANSI.

Notez que l'aide à l'utilisation liste les sous-commandes enregistrées en plus des options et des paramètres positionnels.

enter image description here

L'aide à l'utilisation est facilement personnalisable avec des annotations.

enter image description here

  • basé sur les annotations
  • Sous-commandes de style git
  • Sous-sous-commandes imbriquées
  • paramètres d'option fortement typés
  • paramètres positionnels fortement typés
  • conversion de type personnalisable
  • options à valeurs multiples
  • modèle intuitif pour le nombre d'arguments qu'un champ consomme
  • API fluide
  • Options courtes groupées de style POSIX
  • Options longues à la GNU
  • permet tout préfixe d'option
  • Couleurs ANSI dans l'aide à l'utilisation
  • aide à l'utilisation personnalisable
  • fichier source unique : inclure comme source pour garder votre application dans un seul jar

0voto

dash1e Points 4321

Si votre expression de commande est complexe, vous pouvez penser à définir la syntaxe, à écrire sa BNF et à utiliser des bibliothèques telles que JavaCC o AntLR pour créer votre propre analyseur syntaxique.

0voto

Vadim Ponomarev Points 1015

Je pense qu'il serait préférable de diviser ces commandes à plusieurs niveaux en plusieurs outils CLI.

Au lieu de :
program cmd sub-cmd sub-sub-cmd -option1 argument -option2 argument

Ajouter un ou deux niveaux au nom du programme :
program-cmd-sub-cmd sub-sub-cmd -option1 argument -option2 argument

Un exemple concret :
svn-add -q -N foo.c

Vous pourriez conserver toutes les classes requises dans un seul JAR (et les réutiliser autant que vous le souhaitez), en ajoutant simplement plusieurs points d'entrée "principaux". Pour l'analyse de base du CLI, je recommande également JCommander .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X