JCommander(命令行参数解析工具)

Commander是一个用于解析命令行参数的Java框架,支持解析所有基本的数据类型,也支持将命令行解析成用户自定义的类型,只需要写一个转变函数。

1. JCommander用法

将用来表示参数的fields用@Parameter标识:

 

[java]  view plain  copy
 
  1. import com.beust.jcommander.Parameter;  
  2.    
  3. public class JCommanderExample {  
  4.   @Parameter  
  5.   private List<String> parameters = new ArrayList<String>();  
  6.    
  7.   @Parameter(names = { "-log""-verbose" }, description = "Level of verbosity")  
  8.   private Integer verbose = 1;  
  9.    
  10.   @Parameter(names = "-groups", description = "Comma-separated list of group names to be run")  
  11.   private String groups;  
  12.    
  13.   @Parameter(names = "-debug", description = "Debug mode")  
  14.   private boolean debug = false;  
  15. }  


JCommander就可以解析命令行中的参数了:

 

 

[java]  view plain  copy
 
  1. JCommanderExample jct = new JCommanderExample();  
  2. String[] argv = { "-log""2""-groups""unit" };  
  3. new JCommander(jct, argv);  
  4.    
  5. Assert.assertEquals(jct.verbose.intValue(), 2);  



 

2. 选项类型

Boolean:

 

[java]  view plain  copy
 
  1. @Parameter(names = "-debug", description = "Debug mode", arity = 1)  
  2. private boolean debug = true;  

boolean类型默认arity=0,这样的话不要用户为参数设置value,只要参数中有-debug就被parse成true,没有就parse成false。但是如果加上 arity=1,则需要用户明确指定value:

 

 

[java]  view plain  copy
 
  1. program -debug true  
  2. program -debug false  



String,Interger,Long:

 

对这几种类型,JCommander会尝试parse随后的参数,并上溯成参数的类型,如果参数不能成功上溯则会产生exception

 

[java]  view plain  copy
 
  1. @Parameter(names = "-log", description = "Level of verbosity")  
  2. private Integer verbose = 1;  
[java]  view plain  copy
 
  1. java Main -log 3  
  2. //got exception for the following one  
  3. java Main -log text  



Lists:

 

如果类型是List,则这个参数可出现多次

 

[java]  view plain  copy
 
  1. @Parameter(names = "-host", description = "The host")  
  2. private List<String> hosts = new ArrayList<String>();  

下例中fields hosts包含了两个字符串

 

 

[java]  view plain  copy
 
  1. java Main -host host1 -verbose -host host2  



Password:

 

指明password为true表明这个field是password,我们不想他出现在命令行上,遇到password的参数,用户会被要求在prompt中输入密码

 

[java]  view plain  copy
 
  1. public class ArgsPassword {  
  2.   @Parameter(names = "-password", description = "Connection password", password = true)  
  3.   private String password;  
  4. }  



Echo Input:

 

当你想让password显示在prompt中时,需要设置echoInput=true,这个setting只有在password=true时才有效

 

[java]  view plain  copy
 
  1. public class ArgsPassword {  
  2.   @Parameter(names = "-password", description = "Connection password", password = true, echoInput = true)  
  3.   private String password;  
  4. }  



 

 

3. 自定义类型

当需要更负责的参数时,比如files,host names,lists等,我们可以写一个转换器(converter)来将参数转换成我们想要的格式或类型。转换器需要实现IStringConverter接口

 

[java]  view plain  copy
 
  1. public interface IStringConverter<T> {  
  2.   T convert(String value);  
  3. }  

 

a. 通过annotation实现:

下面的converter将string转换成file

 

[java]  view plain  copy
 
  1. public class FileConverter implements IStringConverter<File> {  
  2.   @Override  
  3.   public File convert(String value) {  
  4.     return new File(value);  
  5.   }  
  6. }  


将参数对应的field定义成下面这样,注意要在annotation里指定converter

 

 

[java]  view plain  copy
 
  1. @Parameter(names = "-file", converter = FileConverter.class)  
  2. File file;  


b. 通过工厂 factory 实现:

 

在每个parameter annotation中都指定converter有点烦,可以通过factory解决这个问题。

比如你要将下面的参数解析成host和port java App -target example.com:8080
定义一个类来放参数

 

[java]  view plain  copy
 
  1. public class HostPort {  
  2.   private String host;  
  3.   private Integer port;  
  4. }  


写个converter把参数String转换成HostPort

 

 

[java]  view plain  copy
 
  1. class HostPortConverter implements IStringConverter<HostPort> {  
  2.   @Override  
  3.   public HostPort convert(String value) {  
  4.     HostPort result = new HostPort();  
  5.     String[] s = value.split(":");  
  6.     result.host = s[0];  
  7.     result.port = Integer.parseInt(s[1]);  
  8.    
  9.     return result;  
  10.   }  
  11. }  

写一个Factory,作用是返回converter

 

 

[java]  view plain  copy
 
  1. public class Factory implements IStringConverterFactory {  
  2.   public Class<? extends IStringConverter<?>> getConverter(Class forType) {  
  3.     if (forType.equals(HostPort.class)) return HostPortConverter.class;  
  4.     else return null;  
  5.   }  

参数类也很简单,参数类型直接用HostPort就好了

 

 

[java]  view plain  copy
 
  1. public class ArgsConverterFactory {  
  2.   @Parameter(names = "-hostport")  
  3.   private HostPort hostPort;  
  4. }  

没有看到Factory用在什么地方?是在你的程序创建JCommander时传进去的

 

 

[java]  view plain  copy
 
  1. ArgsConverterFactory a = new ArgsConverterFactory();  
  2. JCommander jc = new JCommander(a);  
  3. jc.addConverterFactory(new Factory());  
  4. jc.parse("-hostport""example.com:8080");  
  5.    
  6. Assert.assertEquals(a.hostPort.host, "example.com");  
  7. Assert.assertEquals(a.hostPort.port.intValue(), 8080);  




4. 参数验证
通过提供一个实现了IParameterValidator接口的类,可以让JCommander验证你的参数

 

 

[java]  view plain  copy
 
  1. public interface IParameterValidator {  
  2.  /** 
  3.    * Validate the parameter. 
  4.    * 
  5.    * @param name The name of the parameter (e.g. "-host"). 
  6.    * @param value The value of the parameter that we need to validate 
  7.    * 
  8.    * @throws ParameterException Thrown if the value of the parameter is invalid. 
  9.    */  
  10.   void validate(String name, String value) throws ParameterException;  
  11. }  

下面这个validator可以验证提供的参数为正整数

 

 

[java]  view plain  copy
 
  1. public class PositiveInteger implements IParameterValidator {  
  2.  public void validate(String name, String value)  
  3.       throws ParameterException {  
  4.     int n = Integer.parseInt(value);  
  5.     if (n < 0) {  
  6.       throw new ParameterException("Parameter " + name + " should be positive (found " + value +")");  
  7.     }  
  8.   }  
  9. }  

在parameter annotation中用validateWith来指定验证器

 

 

[java]  view plain  copy
 
  1. @Parameter(names = "-age", validateWith = PositiveInteger.class)  
  2. private Integer age;  

 

 

 

5. 主参数

我们可以定义一个main parameter来放所有参数,这个参数不用指定names,必须为List<String>类型

 

[java]  view plain  copy
 
  1. @Parameter(description = "Files")  
  2. private List<String> files = new ArrayList<String>();  
  3.    
  4. @Parameter(names = "-debug", description = "Debugging level")  
  5. private Integer debug = 1;  

run下面命令行,files会保存‘file1’和‘file2’

 

 

[java]  view plain  copy
 
  1. java Main -debug file1 file2  




6. 私有参数

 

参数可以是private的

 

[java]  view plain  copy
 
  1. public class ArgsPrivate {  
  2.   @Parameter(names = "-verbose")  
  3.   private Integer verbose = 1;  
  4.    
  5.   public Integer getVerbose() {  
  6.     return verbose;  
  7.   }  
  8. }  

 

 

[java]  view plain  copy
 
  1. ArgsPrivate args = new ArgsPrivate();  
  2. new JCommander(args, "-verbose""3");  
  3. Assert.assertEquals(args.getVerbose().intValue(), 3);  




7. 参数分隔符

参数默认用空格分隔,但是我们也可以指定自己的分隔符,比如用“:”, “=”等,java Main -log:3
可以在参数类定义中使用@Parameter annotation指定分隔符

 

[java]  view plain  copy
 
  1. @Parameters(separators = "=")  
  2. public class SeparatorEqual {  
  3.   @Parameter(names = "-level")  
  4.   private Integer level = 2;  
  5. }  



 

8. 多重描述(多个参数类)

你可以将参数放在不同的参数类中,比如定义以下两个含有不同参数的参数类

 

[java]  view plain  copy
 
  1. public class ArgsMaster {  
  2.   @Parameter(names = "-master")  
  3.   private String master;  
  4. }  
[java]  view plain  copy
 
  1. public class ArgsSlave {  
  2.   @Parameter(names = "-slave")  
  3.   private String slave;  
  4. }  

使用时将两个类都传入JCommander

 

 

[java]  view plain  copy
 
  1. ArgsMaster m = new ArgsMaster();  
  2. ArgsSlave s = new ArgsSlave();  
  3. String[] argv = { "-master""master""-slave""slave" };  
  4. new JCommander(new Object[] { m , s }, argv);  
  5.    
  6. Assert.assertEquals(m.master, "master");  
  7. Assert.assertEquals(s.slave, "slave");  



9. @语法

 

JCommander支持从文件中获取并解析参数,比如文件/tmp/params中包含了以下内容

 

[java]  view plain  copy
 
  1. -verbose  
  2. file1  
  3. file2  
  4. file3  

在命令行中用@指定文件路径即可

 

 

[java]  view plain  copy
 
  1. java Main @/tmp/parameters  




10. 参数value数量(Arities)

 

有时候你想为一个参数设置多个值,这时就要用到arity,比如-pairs需要两个values,则设置arity=2

 

[java]  view plain  copy
 
  1. @Parameter(names = "-pairs", arity = 2, description = "Pairs")  
  2. private List<String> pairs;  

JCommander就会为pairs接收两个values

 

 

[java]  view plain  copy
 
  1. java Main -pairs slave master  

注意想接收多个value的参数需要设置成List<String>类型,boolean类型默认arity为0,String、Integer、int、Long、long默认arity都为1。

 

 

可变参数值数量,设置variableArity=true,你可以指定一个参数接收不定数量的values

 

[java]  view plain  copy
 
  1. @Parameter(names = "-foo", variableArity = true)  
  2. public List<String> foo = new ArrayList<String>();  

下面两个例子一个接收2个参数,一个接收3个参数

 

 

[java]  view plain  copy
 
  1. program -foo a1 a2 a3 -bar  
  2. program -foo a1 -bar  




11. 多重选项名

同一个选项(option)可以有不同别名,比如--recursive,可以有一个缩写-r,可以通过给names设置多个值实现

 

[java]  view plain  copy
 
  1. @Parameter(names = { "-d""--outputDirectory" }, description = "Directory")  
  2. private String outputDirectory;  

下面两种都可以给outputDirectory赋值

 

 

[java]  view plain  copy
 
  1. </pre><pre name="code" class="java">java Main -d /tmp  
  2. java Main --outputDirectory /tmp  



12. 其他选项配置

 

JCommander#setCaseSensitiveOptions(boolean):选项是否大小写敏感,如果设置成true,则‘-option' 和'-OPTION'是不同的选项

JCommander#setAllowAbbreviatedOptions(boolean):打开选项缩写功能,’-op‘可以表示’-option‘选项,如果选项缩写太模糊不能定位到某个选项,则会throw一个ParameterException

 

 

13. 必须和可选选项

@Parameter annotation的required设置为true则表示此选项为必须的,否则为可选(默认为可选)

 

[java]  view plain  copy
 
  1. @Parameter(names = "-host", required = true)  
  2. private String host;  




 

14. 默认值

一般情况下直接给选项赋值就可以了

 

[java]  view plain  copy
 
  1. private Integer logLevel = 3;  

如果有更复杂的情况,比如对某些选项可以再不同的类里重复使用,或者你想把所有的default value都存在同一个地方比如一个XML文件,这些可以通过实现IDefaultProvider接口达成

 

 

[java]  view plain  copy
 
  1. public interface IDefaultProvider {  
  2.   /** 
  3.    * @param optionName The name of the option as specified in the names() attribute 
  4.    * of the @Parameter option (e.g. "-file"). 
  5.    * 
  6.    * @return the default value for this option. 
  7.    */  
  8.   String getDefaultValueFor(String optionName);  
  9. }  

实例

 

 

[java]  view plain  copy
 
  1. //这是个内部类  
[java]  view plain  copy
 
  1. private static final IDefaultProvider DEFAULT_PROVIDER = new IDefaultProvider() {  
  2.   @Override  
  3.   public String getDefaultValueFor(String optionName) {  
  4.     return "-debug".equals(optionName) ? "false" : "42";  
  5.   }  
  6. };  
  7.    
  8. // ...  
  9.    
  10. JCommander jc = new JCommander(new Args());  
  11. jc.setDefaultProvider(DEFAULT_PROVIDER); //把default provider传入  




15. 帮助选项

 

非常简单

 

[java]  view plain  copy
 
  1. @Parameter(names = "--help", help = true)  
  2. private boolean help;  



 

 

16. 更多复杂的命令语法

想git之类的工具,除了git命令本身需要选项,它还包含一些子命令,如git commit,子命令本身也包含一些选项

 

[java]  view plain  copy
 
  1. git commit --amend -m "Bug fix"  

上面的命令中,commit在JCommander中叫做’commands‘, 我们要为这个子命令单独创建一个类

 

 

[java]  view plain  copy
 
  1. @Parameters(separators = "=", commandDescription = "Record changes to the repository")  
  2. private class CommandCommit {  
  3.    
  4.   @Parameter(description = "The list of files to commit")  
  5.   private List<String> files;  
  6.    
  7.   @Parameter(names = "--amend", description = "Amend")  
  8.   private Boolean amend = false;  
  9.    
  10.   @Parameter(names = "--author")  
  11.   private String author;  
  12. }  


再为另一个git命令add创建一个类

 

 

[java]  view plain  copy
 
  1. @Parameters(commandDescription = "Add file contents to the index")  
  2. public class CommandAdd {  
  3.    
  4.   @Parameter(description = "File patterns to add to the index")  
  5.   private List<String> patterns;  
  6.    
  7.   @Parameter(names = "-i")  
  8.   private Boolean interactive = false;  
  9. }  


创建好后要在JCommander对象里通过addCommand注册这些命令,当然我们也可以创建一个主要的参数类来接受git的参数

 

 

[java]  view plain  copy
 
  1. CommandMain cm = new CommandMain();  
  2. JCommander jc = new JCommander(cm);  
  3.    
  4. CommandAdd add = new CommandAdd();  
  5. jc.addCommand("add", add);  
  6. CommandCommit commit = new CommandCommit();  
  7. jc.addCommand("commit", commit);  
  8.    
  9. jc.parse("-v""commit""--amend""--author=cbeust""A.java""B.java");  
  10.    
  11. Assert.assertTrue(cm.verbose);  
  12. Assert.assertEquals(jc.getParsedCommand(), "commit");  
  13. Assert.assertTrue(commit.amend);  
  14. Assert.assertEquals(commit.author, "cbeust");  
  15. Assert.assertEquals(commit.files, Arrays.asList("A.java""B.java"));  




 

17. Exception

JCommander发现错误时会抛出ParameterExcpetion,这是一个runtime exception

 

 

18. usage

可以再JCommander实例中调用usage()方法获取命令行使用方法

 

19. 隐藏参数

如果不想某些选项出现在usage里,可以隐藏他们

 

[java]  view plain  copy
 
  1. @Parameter(names = "-debug", description = "Debug mode", hidden = true)  
  2. private boolean debug = false;  



 

20. 国际化

选项的描述(description)可以国际化,首先在类上使用@Parameter指定消息bundle的名字,然后在选项的@Parameter中使用descriptionKey指定bundle中的key

 

[java]  view plain  copy
 
  1. @Parameters(resourceBundle = "MessageBundle")  
  2. private class ArgsI18N2 {  
  3.   @Parameter(names = "-host", description = "Host", descriptionKey = "host")  
  4.   String hostName;  
  5. }  

在MessageBundle中,定义host如下

 

 

[java]  view plain  copy
 
  1. host:主机  



21. 参数代理

 

使用@ParameterDelegate可以在主参数类中引用其他的代理参数类

 

[java]  view plain  copy
 
  1. class Delegate {  
  2.   @Parameter(names = "-port")  
  3.   private int port;  
  4. }  
  5.    
  6. class MainParams {  
  7.   @Parameter(names = "-v")  
  8.   private boolean verbose;  
  9.    
  10.   @ParametersDelegate  
  11.   private Delegate delegate = new Delegate();  
  12. }  

使用时只需在JCommander中指定主参数类

 

 

[java]  view plain  copy
 
  1. MainParams p = new MainParams();  
  2. new JCommander(p).parse("-v""-port""1234");  
  3. Assert.assertTrue(p.isVerbose);  
  4. Assert.assertEquals(p.delegate.port, 1234);  



22. 动态参数

 

JCommander允许接收没有预先定义的选项,比如’-Da=b -Dc=d‘, Da和Dc没有在参数类中定义,这些参数要用@DynamicParameter annotation标记的类型为Map<String, String>的选项接收,动态参数可以重复多次

 

[java]  view plain  copy
 
  1. @DynamicParameter(names = "-D", description = "Dynamic parameters go here")  
  2. private Map<String, String> params = new HashMap<String, String>();  

通过使用assignment属性,可以用其他符号代替’=‘作为赋值符号

http://blog.csdn.net/navyhu/article/details/40430267

猜你喜欢

转载自aoyouzi.iteye.com/blog/2288127