1
2= JCommander
3:author: Cédric Beust
4:email: cedric@beust.com
5:toc: left
6:source-highlighter: prettify
7:sectnums:
8
9++++
10<div style="float:right">
11<div style="display:inline-block">
12  <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
13  <input type="hidden" name="cmd" value="_donations">
14  <input type="hidden" name="business" value="cedric@beust.com">
15  <input type="hidden" name="lc" value="US">
16  <input type="hidden" name="item_name" value="Cedric Beust">
17  <input type="hidden" name="no_note" value="0">
18  <input type="hidden" name="currency_code" value="USD">
19  <input type="hidden" name="bn" value="PP-DonationsBF:btn_donate_LG.gif:NonHostedGuest">
20  <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
21  <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1" hidden="" style="display: none !important;">
22  </form>
23</div>
24</div>
25++++
26
27
28__"Because life is too short to parse command line parameters"__
29
30== Overview
31
32JCommander is a very small Java framework that makes it trivial to parse command line parameters.
33You annotate fields with descriptions of your options:
34
35[source,java]
36----
37import com.beust.jcommander.Parameter;
38
39public class Args {
40  @Parameter
41  private List<String> parameters = new ArrayList<>();
42
43  @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")
44  private Integer verbose = 1;
45
46  @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")
47  private String groups;
48
49  @Parameter(names = "-debug", description = "Debug mode")
50  private boolean debug = false;
51}
52----
53
54and then you simply ask JCommander to parse:
55
56[source,java]
57----
58Args args = new Args();
59String[] argv = { "-log", "2", "-groups", "unit" };
60JCommander.newBuilder()
61  .addObject(args)
62  .build()
63  .parse(argv);
64
65Assert.assertEquals(jct.verbose.intValue(), 2);
66----
67
68Here is another example:
69
70[source,java]
71----
72class Main {
73    @Parameter(names={"--length", "-l"})
74    int length;
75    @Parameter(names={"--pattern", "-p"})
76    int pattern;
77
78    public static void main(String ... argv) {
79        Main main = new Main();
80        JCommander.newBuilder()
81            .addObject(main)
82            .build()
83            .parse(argv);
84        main.run();
85    }
86
87    public void run() {
88        System.out.printf("%d %d", length, pattern);
89    }
90}
91----
92
93[source,bash]
94----
95$ java Main -l 512 --pattern 2
96512 2
97----
98
99
100== Types of options
101
102The fields representing your parameters can be of any type. Basic types (`Integer`, `Boolean`, etc...) are supported by default and you can write type converters to support any other type (`File`, etc...).
103
104=== Boolean
105
106When a Parameter annotation is found on a field of type `boolean` or `Boolean`, JCommander interprets it as an option with an arity of 0:
107
108[source,java]
109----
110@Parameter(names = "-debug", description = "Debug mode")
111private boolean debug = false;
112----
113
114Such a parameter does not require any additional parameter on the command line and if it's detected during parsing, the corresponding field will be set to true. If you want to define a boolean parameter that's true by default, you can declare it as having an arity of 1. Users will then have to specify the value they want explicitly:
115
116[source,java]
117----
118@Parameter(names = "-debug", description = "Debug mode", arity = 1)
119private boolean debug = true;
120----
121
122Invoke with either of:
123
124[source,bash]
125----
126program -debug true
127program -debug false
128----
129
130When a Parameter annotation is found on a field of type `String`, `Integer`, `int`, `Long` or `long`, JCommander will parse the following parameter and it will attempt to cast it to the right type:
131
132[source,java]
133----
134@Parameter(names = "-log", description = "Level of verbosity")
135private Integer verbose = 1;
136----
137
138[source,bash]
139----
140java Main -log 3
141----
142
143will cause the field verbose to receive the value 3. However:
144
145[source,bash]
146----
147$ java Main -log test
148----
149
150will cause an exception to be thrown.
151
152=== Lists
153
154When a Parameter annotation is found on a field of type `List`, JCommander will interpret it as an option that can occur multiple times:
155
156[source,java]
157----
158@Parameter(names = "-host", description = "The host")
159private List<String> hosts = new ArrayList<>();
160----
161
162will allow you to parse the following command line:
163
164[source,bash]
165----
166$ java Main -host host1 -verbose -host host2
167----
168
169When JCommander is done parsing the line above, the field hosts will contain the strings `"host1"` and `"host2"`.
170
171=== Password
172
173If one of your parameters is a password or some other value that you do not wish to appear in your history or in clear, you can declare it of type password and JCommander will then ask you to enter it in the console:
174
175[source,java]
176----
177public class ArgsPassword {
178  @Parameter(names = "-password", description = "Connection password", password = true)
179  private String password;
180}
181----
182
183When you run your program, you will get the following prompt:
184
185[source,bash]
186----
187Value for -password (Connection password):
188----
189
190You will need to type the value at this point before JCommander resumes.
191
192=== Echo Input
193
194In Java 6, by default, you will not be able to see what you type for passwords entered at the prompt (Java 5 and lower will always show the password). However, you can override this by setting echoInput to `true` (default is `false` and this setting only has an effect when password is `true`):
195
196[source,java]
197----
198public class ArgsPassword {
199  @Parameter(names = "-password", description = "Connection password", password = true, echoInput = true)
200  private String password;
201}
202----
203
204== Custom types (converters and splitters)
205
206To bind parameters to custom types or change the way how JCommander splits parameters (default is to split via comma) JCommander provides two
207interfaces `IStringConverter` and `IParameterSplitter`.
208
209[[single-value]]
210=== Custom types - Single value
211
212Use either the `converter=` attribute of the `@Parameter` or implement `IStringConverterFactory`.
213
214==== By annotation
215
216By default, JCommander parses the command line into basic types only (strings, booleans, integers and longs). Very often, your application actually needs more complex types (such as files, host names, lists, etc.). To achieve this, you can write a type converter by implementing the following interface:
217
218[source,java]
219----
220public interface IStringConverter<T> {
221  T convert(String value);
222}
223----
224
225For example, here is a converter that turns a string into a File:
226
227[source,java]
228----
229public class FileConverter implements IStringConverter<File> {
230  @Override
231  public File convert(String value) {
232    return new File(value);
233  }
234}
235----
236
237Then, all you need to do is declare your field with the correct type and specify the converter as an attribute:
238
239[source,java]
240----
241@Parameter(names = "-file", converter = FileConverter.class)
242File file;
243----
244
245JCommander ships with a few common converters (for more info please see the implementations of `IStringConverter`).
246
247===== Note
248
249If a converter is used for a `List` field:
250
251[source,java]
252----
253@Parameter(names = "-files", converter = FileConverter.class)
254List<File> files;
255----
256
257And the application is called as follows:
258
259[source,bash]
260----
261$ java App -files file1,file2,file3
262----
263
264JCommander will split the string `file1,file2,file3` into `file1`, `file2`, `file3` and feed it one by one to the converter.
265
266For an alternative solution to parse a list of values, see <<list-value>>.
267
268==== By factory
269
270If the custom types you use appear multiple times in your application, having to specify the converter in each annotation can become tedious. To address this, you can use an `IStringConverterFactory`:
271
272[source,java]
273----
274public interface IStringConverterFactory {
275  <T> Class<? extends IStringConverter<T>> getConverter(Class<T> forType);
276}
277----
278
279For example, suppose you need to parse a string representing a host and a port:
280
281[source,bash]
282----
283$ java App -target example.com:8080
284----
285
286You define the holder class :
287
288[source,java]
289----
290public class HostPort {
291  public HostPort(String host, String port) {
292     this.host = host;
293     this.port = port;
294  }
295
296  final String host;
297  final Integer port;
298}
299----
300
301and the string converter to create instances of this class:
302
303[source,java]
304----
305class HostPortConverter implements IStringConverter<HostPort> {
306  @Override
307  public HostPort convert(String value) {
308    String[] s = value.split(":");
309    return new HostPort(s[0], Integer.parseInt(s[1]));
310  }
311}
312----
313
314The factory is straightforward:
315
316[source,java]
317----
318public class Factory implements IStringConverterFactory {
319  public Class<? extends IStringConverter<?>> getConverter(Class forType) {
320    if (forType.equals(HostPort.class)) return HostPortConverter.class;
321    else return null;
322  }
323----
324
325You can now use the type `HostPort` as a parameter without any converterClass attribute:
326
327[source,java]
328----
329public class ArgsConverterFactory {
330  @Parameter(names = "-hostport")
331  private HostPort hostPort;
332}
333----
334
335All you need to do is add the factory to your JCommander object:
336
337[source,java]
338----
339ArgsConverterFactory a = new ArgsConverterFactory();
340JCommander jc = JCommander.newBuilder()
341    .addObject(a)
342    .addConverterFactory(new Factory())
343    .build()
344    .parse("-hostport", "example.com:8080");
345
346Assert.assertEquals(a.hostPort.host, "example.com");
347Assert.assertEquals(a.hostPort.port.intValue(), 8080);
348----
349
350Another advantage of using string converter factories is that your factories can come from a dependency injection framework.
351
352[[list-value]]
353=== Custom types - List value
354
355Use the `listConverter=` attribute of the `@Parameter` annotation and assign a custom `IStringConverter` implementation to convert a `String` into a `List` of values.
356
357==== By annotation
358
359If your application needs a list of complex types, write a list type converter by implementing the same interface as before:
360
361[source,java]
362----
363public interface IStringConverter<T> {
364  T convert(String value);
365}
366----
367where `T` is a `List`.
368
369
370For example, here is a list converter that turns a string into a `List<File>`:
371
372[source,java]
373----
374public class FileListConverter implements IStringConverter<List<File>> {
375  @Override
376  public List<File> convert(String files) {
377    String [] paths = files.split(",");
378    List<File> fileList = new ArrayList<>();
379    for(String path : paths){
380        fileList.add(new File(path));
381    }
382    return fileList;
383  }
384}
385----
386
387Then, all you need to do is declare your field with the correct type and specify the list converter as an attribute:
388
389[source,java]
390----
391@Parameter(names = "-files", listConverter = FileListConverter.class)
392List<File> file;
393----
394
395Now if you call for application as in the following example:
396
397[source,bash]
398----
399$ java App -files file1,file2,file3
400----
401
402The parameter `file1,file2,file3` is given to the `listConverter` and will the properly processed.
403
404JCommander ships with a default converter for `String` values.
405
406
407=== Splitting
408
409Use the `splitter=` attribute of the `@Parameter` annotation and assign a custom `IParameterSplitter` implementation to handle how parameters are split in sub-parts.
410
411==== By annotation
412
413By default, JCommander tries to split parameters for `List` field types on commas.
414
415To split parameters on other characters, you can write a custom splitter by implementing the following interface:
416
417[source,java]
418----
419public interface IParameterSplitter {
420  List<String> split(String value);
421}
422----
423
424For example, here is a splitter that splits a string on semicolon:
425
426[source,java]
427----
428public static class SemiColonSplitter implements IParameterSplitter {
429    public List<String> split(String value) {
430      return Arrays.asList(value.split(";"));
431    }
432}
433----
434
435Then, all you need to do is declare your field with the correct type and specify the splitter as an attribute:
436
437[source,java]
438----
439@Parameter(names = "-files", converter = FileConverter.class, splitter = SemiColonSplitter.class)
440List<File> files;
441----
442
443JCommander will split the string `file1;file2;file3` into `file1`, `file2`, `file3` and feed it one by one to the converter.
444
445
446== Parameter validation
447
448Parameter validation can be performed in two different ways: at the individual parameter level or globally.
449
450=== Individual parameter validation
451
452You can ask JCommander to perform early validation on your parameters by providing a class that implements the following interface:
453
454[source,java]
455----
456public interface IParameterValidator {
457 /**
458   * Validate the parameter.
459   *
460   * @param name The name of the parameter (e.g. "-host").
461   * @param value The value of the parameter that we need to validate
462   *
463   * @throws ParameterException Thrown if the value of the parameter is invalid.
464   */
465  void validate(String name, String value) throws ParameterException;
466}
467----
468
469Here is an example implementation that will make sure that the parameter is a positive integer:
470
471[source,java]
472----
473public class PositiveInteger implements IParameterValidator {
474 public void validate(String name, String value)
475      throws ParameterException {
476    int n = Integer.parseInt(value);
477    if (n < 0) {
478      throw new ParameterException("Parameter " + name + " should be positive (found " + value +")");
479    }
480  }
481}
482----
483
484Specify the name of a class implementing this interface in the `validateWith` attribute of your `@Parameter` annotations:
485
486[source,java]
487----
488@Parameter(names = "-age", validateWith = PositiveInteger.class)
489private Integer age;
490----
491
492Attempting to pass a negative integer to this option will cause a ParameterException to be thrown.
493
494Multiple validators may be specified:
495
496[source,java]
497----
498@Parameter(names = "-count", validateWith = { PositiveInteger.class, CustomOddNumberValidator.class })
499private Integer value;
500----
501
502
503=== Global parameter validation
504
505After parsing your parameters with JCommander, you might want to perform additional validation across these parameters, such as making sure that two mutually exclusive parameters are not both specified. Because of all the potential combinations involved in such validation, JCommander does not provide any annotation-based solution to perform this validation because such an approach would necessarily be very limited by the very nature of Java annotations. Instead, you should simply perform this validation in Java on all the arguments that JCommander just parsed.
506
507
508== Main parameter
509
510So far, all the `@Parameter` annotations we have seen had defined an attribute called `names`. You can define one (and at most one) parameter without any such attribute. This parameter needs to be a `List<String>` and it will contain all the parameters that are not options:
511
512[source,java]
513----
514@Parameter(description = "Files")
515private List<String> files = new ArrayList<>();
516
517@Parameter(names = "-debug", description = "Debugging level")
518private Integer debug = 1;
519----
520
521will allow you to parse:
522
523[source,bash]
524----
525$ java Main -debug file1 file2
526----
527
528and the field files will receive the strings `"file1"` and `"file2"`.
529
530== Private parameters
531
532Parameters can be private:
533
534[source,java]
535----
536public class ArgsPrivate {
537  @Parameter(names = "-verbose")
538  private Integer verbose = 1;
539
540  public Integer getVerbose() {
541    return verbose;
542  }
543}
544ArgsPrivate args = new ArgsPrivate();
545JCommander.newBuilder()
546    .addObject(args)
547    .build()
548    .parse("-verbose", "3");
549Assert.assertEquals(args.getVerbose().intValue(), 3);
550----
551
552== Parameter separators
553
554By default, parameters are separated by spaces, but you can change this setting to allow different separators:
555
556[source,bash]
557----
558$ java Main -log:3
559----
560
561or
562
563[source,bash]
564----
565$ java Main -level=42
566----
567
568You define the separator with the @Parameters annotation:
569
570[source,java]
571----
572@Parameters(separators = "=")
573public class SeparatorEqual {
574  @Parameter(names = "-level")
575  private Integer level = 2;
576}
577----
578
579== Multiple descriptions
580
581You can spread the description of your parameters on more than one class. For example, you can define the following two classes:
582
583[source,java]
584----
585public class ArgsMaster {
586  @Parameter(names = "-master")
587  private String master;
588}
589
590public class ArgsSlave {
591  @Parameter(names = "-slave")
592  private String slave;
593}
594----
595
596and pass these two objects to JCommander:
597
598[source,java]
599----
600ArgsMaster m = new ArgsMaster();
601ArgsSlave s = new ArgsSlave();
602String[] argv = { "-master", "master", "-slave", "slave" };
603JCommander.newBuilder()
604    .addObject(new Object[] { m , s })
605    .build()
606    .parse(argv);
607
608Assert.assertEquals(m.master, "master");
609Assert.assertEquals(s.slave, "slave");
610----
611
612== @ syntax
613
614JCommander supports the @ syntax, which allows you to put all your options into a file and pass this file as parameter:
615
616[[app-listing]]
617[source,bash]
618./tmp/parameters
619----
620-verbose
621file1
622file2
623file3
624----
625
626[source,bash]
627----
628$ java Main @/tmp/parameters
629----
630
631== Arities (multiple values for parameters)
632
633=== Fixed arities
634
635If some of your parameters require more than one value, such as the following example where two values are expected after -pairs:
636
637[source,bash]
638----
639$ java Main -pairs slave master foo.xml
640----
641
642then you need to define your parameter with the arity attribute and make that parameter a `List<String>`:
643
644[source,java]
645----
646@Parameter(names = "-pairs", arity = 2, description = "Pairs")
647private List<String> pairs;
648----
649
650You don't need to specify an arity for parameters of type `boolean` or `Boolean` (which have a default arity of 0) and of types `String`, `Integer`, `int`, `Long` and `long` (which have a default arity of 1).
651
652Also, note that only `List<String>` is allowed for parameters that define an arity. You will have to convert these values yourself if the parameters you need are of type `Integer` or other (this limitation is due to Java's erasure).
653
654=== Variable arities
655
656You can specify that a parameter can receive an indefinite number of parameters, up to the next option. For example:
657
658[source,bash]
659----
660program -foo a1 a2 a3 -bar
661program -foo a1 -bar
662----
663
664Such a parameter can be parsed in two different ways.
665
666==== With a list
667
668If the number of following parameters is unknown, your parameter must be of type `List<String>` and you
669need to set the boolean `variableArity` to `true`:
670
671[source,java]
672----
673@Parameter(names = "-foo", variableArity = true)
674public List<String> foo = new ArrayList<>();
675----
676
677==== With a class
678
679Alternatively, you can define a class in which the following parameters will be stored, based on their order
680of appearance:
681
682[source,java]
683----
684static class MvParameters {
685  @SubParameter(order = 0)
686  String from;
687
688  @SubParameter(order = 1)
689  String to;
690}
691
692@Test
693public void arity() {
694  class Parameters {
695    @Parameter(names = {"--mv"}, arity = 2)
696    private MvParameters mvParameters;
697  }
698
699  Parameters args = new Parameters();
700  JCommander.newBuilder()
701          .addObject(args)
702          .args(new String[]{"--mv", "from", "to"})
703          .build();
704
705  Assert.assertNotNull(args.mvParameters);
706  Assert.assertEquals(args.mvParameters.from, "from");
707  Assert.assertEquals(args.mvParameters.to, "to");
708}
709----
710
711== Multiple option names
712
713You can specify more than one option name:
714[source,java]
715----
716@Parameter(names = { "-d", "--outputDirectory" }, description = "Directory")
717private String outputDirectory;
718----
719
720will allow both following syntaxes:
721
722[source,bash]
723----
724$ java Main -d /tmp
725$ java Main --outputDirectory /tmp
726----
727
728== Other option configurations
729
730You can configure how options are looked up in a few different ways:
731
732- `JCommander#setCaseSensitiveOptions(boolean)`: specify whether options are case sensitive. If you call this method with `false`, then `"-param"` and `"-PARAM"` are considered equal.
733- `JCommander#setAllowAbbreviatedOptions(boolean)`: specify whether users can pass abbreviated options. If you call this method with `true` then users can pass `"-par"` to specify an option called `-param`. JCommander will throw a `ParameterException` if the abbreviated name is ambiguous.
734
735== Required and optional parameters
736
737If some of your parameters are mandatory, you can use the `required` attribute (which default to `false`):
738
739[source,java]
740----
741@Parameter(names = "-host", required = true)
742private String host;
743----
744
745If this parameter is not specified, JCommander will throw an exception telling you which options are missing.
746
747== Default values
748
749The most common way to specify a default value for your parameters is to initialize the field at declaration time:
750
751[source,java]
752----
753private Integer logLevel = 3;
754----
755
756For more complicated cases, you might want to be able to reuse identical default values across several main classes or be able to specify these default values in a centralized location such as a `.properties` or an XML file. In this case, you can use an `IDefaultProvider`:
757
758[source,java]
759----
760public interface IDefaultProvider {
761  /**
762   * @param optionName The name of the option as specified in the names() attribute
763   * of the @Parameter option (e.g. "-file").
764   *
765   * @return the default value for this option.
766   */
767  String getDefaultValueFor(String optionName);
768}
769----
770
771By passing an implementation of this interface to your JCommander object, you can now control which default value will be used for your options. Note that the value returned by this method will then be passed to a string converter, if any is applicable, thereby allowing you to specify default values for any types you need.
772
773For example, here is a default provider that will assign a default value of 42 for all your parameters except `"-debug"`:
774
775[source,java]
776----
777private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() {
778  @Override
779  public String getDefaultValueFor(String optionName) {
780    return "-debug".equals(optionName) ? "false" : "42";
781  }
782};
783
784// ...
785
786JCommander jc = JCommander.newBuilder()
787    .addObject(new Args())
788    .defaultProvider(DEFAULT_PROVIDER)
789    .build()
790
791----
792
793
794== Help parameter
795
796If one of your parameters is used to display some help or usage, you need use the help attribute:
797
798[source,java]
799----
800@Parameter(names = "--help", help = true)
801private boolean help;
802----
803
804If you omit this boolean, JCommander will instead issue an error message when it tries to validate your command and it finds that you didn't specify some of the required parameters.
805
806== More complex syntaxes (commands)
807
808Complex tools such as `git` or `svn` understand a whole set of commands, each of which with their own specific syntax:
809
810[source,bash]
811----
812$ git commit --amend -m "Bug fix"
813----
814
815Words such as `"commit"` above are called "commands" in JCommander, and you can specify them by creating one arg object per command:
816
817[source,java]
818----
819@Parameters(separators = "=", commandDescription = "Record changes to the repository")
820private class CommandCommit {
821
822  @Parameter(description = "The list of files to commit")
823  private List<String> files;
824
825  @Parameter(names = "--amend", description = "Amend")
826  private Boolean amend = false;
827
828  @Parameter(names = "--author")
829  private String author;
830}
831
832@Parameters(commandDescription = "Add file contents to the index")
833public class CommandAdd {
834
835  @Parameter(description = "File patterns to add to the index")
836  private List<String> patterns;
837
838  @Parameter(names = "-i")
839  private Boolean interactive = false;
840}
841----
842
843Then you register these commands with your JCommander object. After the parsing phase, you call `getParsedCommand()` on your JCommander object, and based on the command that is returned, you know which arg object to inspect (you can still use a main arg object if you want to support options before the first command appears on the command line):
844
845[source,java]
846----
847CommandMain cm = new CommandMain();
848CommandAdd add = new CommandAdd();
849CommandCommit commit = new CommandCommit();
850JCommander jc = JCommander.newBuilder()
851    .addObject(cm)
852    .addCommand("add", add);
853    .addCommand("commit", commit);
854    .build();
855
856jc.parse("-v", "commit", "--amend", "--author=cbeust", "A.java", "B.java");
857
858Assert.assertTrue(cm.verbose);
859Assert.assertEquals(jc.getParsedCommand(), "commit");
860Assert.assertTrue(commit.amend);
861Assert.assertEquals(commit.author, "cbeust");
862Assert.assertEquals(commit.files, Arrays.asList("A.java", "B.java"));
863----
864
865== Exception
866
867Whenever JCommander detects an error, it will throw a `ParameterException`. Note that this is a Runtime Exception, since your application is probably not initialized correctly at this point. Also, `ParameterException` contains the
868`JCommander` instance and you can also invoke `usage()` on it if you need to display some help.
869
870== Usage
871
872You can invoke `usage()` on the JCommander instance that you used to parse your command line in order to generate a summary of all the options that your program understands:
873
874[source,bash]
875----
876Usage: <main class> [options]
877  Options:
878    -debug          Debug mode (default: false)
879    -groups         Comma-separated list of group names to be run
880  * -log, -verbose  Level of verbosity (default: 1)
881    -long           A long number (default: 0)
882----
883
884You can customize the name of your program by calling `setProgramName()` on your JCommander object. Options preceded by an asterisk are required.
885
886You can also specify the order in which each option should be displayed when calling `usage()` by setting the `order` attribute of the `@Parameter` annotation:
887
888[source,java]
889----
890class Parameters {
891    @Parameter(names = "--importantOption", order = 0)
892    private boolean a;
893
894    @Parameter(names = "--lessImportantOption", order = 3)
895    private boolean b;
896----
897
898== Hiding parameters
899
900If you don't want certain parameters to appear in the usage, you can mark them as "hidden":
901
902[source,java]
903----
904@Parameter(names = "-debug", description = "Debug mode", hidden = true)
905private boolean debug = false;
906----
907
908
909== Internationalization
910
911You can internationalize the descriptions of your parameters. First you use the `@Parameters` annotation at the top of your class to define the name of your message bundle, and then you use the `descriptionKey` attribute instead of description on all the `@Parameters` that require translations. This `descriptionKey` is the key to the string into your message bundle:
912
913[source,java]
914----
915@Parameters(resourceBundle = "MessageBundle")
916private class ArgsI18N2 {
917  @Parameter(names = "-host", description = "Host", descriptionKey = "host")
918  String hostName;
919}
920----
921
922Your bundle needs to define this key:
923
924[source,bash]
925----
926host: Hôte
927----
928
929JCommander will then use the default locale to resolve your descriptions.
930
931== Parameter delegates
932
933If you are writing many different tools in the same project, you will probably find that most of these tools can share configurations. While you can use inheritance with your objects to avoid repeating this code, the restriction to single inheritance of implementation might limit your flexibility. To address this problem, JCommander supports parameter delegates.
934
935When JCommander encounters an object annotated with `@ParameterDelegate` in one of your objects, it acts as if this object had been added as a description object itself:
936
937[source,java]
938----
939class Delegate {
940  @Parameter(names = "-port")
941  private int port;
942}
943
944class MainParams {
945  @Parameter(names = "-v")
946  private boolean verbose;
947
948  @ParametersDelegate
949  private Delegate delegate = new Delegate();
950}
951----
952
953The example above specifies a delegate parameter Delegate which is then referenced in MainParams. You only need to add a `MainParams` object to your
954JCommander configuration in order to use the delegate:
955
956[source,java]
957----
958MainParams p = new MainParams();
959JCommander.newBuilder().addObject(p).build()
960    .parse("-v", "-port", "1234");
961Assert.assertTrue(p.isVerbose);
962Assert.assertEquals(p.delegate.port, 1234);
963----
964
965== Dynamic parameters
966
967JCommander allows you to specify parameters that are not known at compile time, such as "-Da=b -Dc=d". Such parameters are specified with the `@DynamicParameter` annotation and must be of type `Map<String, String>`. Dynamic parameters are allowed to appear multiple times on the command line:
968
969[source,java]
970----
971@DynamicParameter(names = "-D", description = "Dynamic parameters go here")
972private Map<String, String> params = new HashMap<>();
973----
974
975You can specify a different assignment string than = by using the attribute assignment.
976
977== JCommander in other languages
978
979=== Kotlin
980
981[source,kotlin]
982----
983class Args {
984    @Parameter
985    var targets: List<String> = arrayListOf()
986
987    @Parameter(names = arrayOf("-bf", "--buildFile"), description = "The build file")
988    var buildFile: String? = null
989
990    @Parameter(names = arrayOf("--checkVersions"),
991               description = "Check if there are any newer versions of the dependencies")
992    var checkVersions = false
993}
994----
995
996=== Groovy
997
998Courtesy of Paul King:
999
1000[source,groovy]
1001----
1002import com.beust.jcommander.*
1003
1004class Args {
1005  @Parameter(names = ["-f", "--file"], description = "File to load. Can be specified multiple times.")
1006  List<String> file
1007}
1008
1009new Args().with {
1010  JCommander.newBuilder().addObject(it).build().parse(argv)
1011  file.each { println "file: ${new File(it).name}" }
1012}
1013----
1014
1015== More examples
1016
1017Here are the description files for a few projects that use JCommander:
1018
1019- https://github.com/cbeust/testng/blob/master/src/main/java/org/testng/CommandLineArgs.java[TestNG]
1020- https://github.com/cbeust/kobalt/blob/master/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/Args.kt[Kobalt]
1021
1022== Mailing list
1023
1024Join the http://groups.google.com/group/jcommander[JCommander Google group] if you are interested in discussions about JCommander.
1025
1026== Javadocs
1027
1028The Javadocs for JCommander can be found http://jcommander.org/apidocs/[here].
1029
1030== License
1031
1032JCommander is released under the https://github.com/cbeust/jcommander/blob/master/license.txt[Apache 2.0 license].
1033
1034== Download
1035
1036You can download JCommander from the following locations:
1037
1038- http://github.com/cbeust/jcommander[Source on github]
1039- Kobalt
1040
1041[source,groovy]
1042----
1043compile("com.beust:jcommander:1.71")
1044----
1045
1046- Gradle
1047
1048[source,groovy]
1049----
1050compile "com.beust:jcommander:1.71"
1051----
1052
1053- Maven:
1054
1055[source,xml]
1056----
1057<dependency>
1058  <groupId>com.beust</groupId>
1059  <artifactId>jcommander</artifactId>
1060  <version>1.71</version>
1061</dependency>
1062----
1063
1064
1065
1066