【一】需求
前面我们使用了commons io包中的DirectoryWalker和IOFileFilter来进行复杂条件的搜索,但是这个程序有几个问题:
①选项都是hard code在代码里面,难以修改
②有些选项是必需的,有些选项是可选的
③有些选项是不带参数的,有些选项是要带参数的
如果我们希望这个程序能够更加灵活,根据人性化,那么我们需要提供一个界面,无论是普通的命令行、控制台交互、GUI界面。而且还必须让用户自行决定是否要使用该选项。程序必须自动根据已有的条件动态组合
【二】简单而功能强大的commons CLI
Apache commons CLI是一个开源的,用于处理命令行的工具包。这个包目前的稳定版本是1.2,他非常简单只有20个左右的class,但提供了几乎所以可以用到的命令行功能。它的主页在这里:Apache commons CLI
根据CLI的逻辑,每一个命令行的处理都可以分为3个步骤:定义、解析、交互
①定义:定义命令行的各种选项属性(包括缩写、全写、是否必须、是否带参数、参数个数限制)
②解析:使用解析器对命令行选项列表进行解析
③交互:从解析好的命令行查询用户输入的参数值并进行处理
这里需要区分两个名词:选项(option)和参数(arguments)。选项是用来表明功能或者参数的意思的,例如“-d”这个字符串就是一个选项,它可以表示一个日期。那么如果我们需要指定一个日期用于处理,就需要在“-d”后面再加上一个具体值,这个具体值就是参数(argument)。
对应于这3个过程,我们来认识几个重要的类:
①定义阶段
A.Option:这个类用于定义命令行的选项,你可以通过构造方法来定义一个选项
B.Options:Option的容器,用于存储多个Option
C.OptionBuilder:使用描述性API来构建Option,而非直接使用Option的构造方法
②解析阶段
A.CommandLineParser:接口,定义了parse方法由实现类实现
B.PosixParser:Posix风格的命令行解析器
C.GnuParser:GNU风格的命令行解析器
③交互阶段
A.CommandLine:解析后的命令行对象,可以用于查询选项的值
【三】CLI快速入门
通常情况下如果命令的选项比较简单我们使用构造方法就够了,但是当选项的属性比较复杂或者描述性文本比较长时,使用构建器会令到程序的可读性更进一步。下面我们来看看这个需求:
有这样一个命令行,它具备如下的选项和参数组合:
①一个目录选项:-d,带参数值,必须选项
②一个日期选项:-D,带参数值,全写--date,可选项
③一个日期范围选项:-r,带参数值,当-D出现时为必选项,否则该选项无效
④一个文件名前缀选项:-p,带参数值,可以有多个前缀名,以逗号分隔,可选项
⑤一个文件扩展名选项:-s,带参数值,可以有多个扩展名,以逗号分隔,可选项
⑥一个文件大小选项:-S,带参数值,全写--file-size,可选项
⑦一个文件大小阀值选项:-l,带参数值,当-S出现时为必选项,否则该选项无效
⑧一个帮助信息选项:-h,无参数值
【四】代码示例
package example.io;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
/** *//**
* <pre>
* 用于指定各种搜索条件
* </pre>
*/
public class SearchCommandLineProcesser implements CommandLineProcesser {
/** *//**
* <pre>
* ①一个目录选项:-d,带参数值,必须选项
* ②一个日期选项:-D,带参数值,全写--date,可选项
* ③一个日期范围选项:-r,带参数值,当-D出现时为必选项,否则该选项无效
* ④一个文件名前缀选项:-p,带参数值,可以有多个前缀名,以逗号分隔,可选项
* ⑤一个文件扩展名选项:-s,带参数值,可以有多个扩展名,以逗号分隔,可选项
* ⑥一个文件大小选项:-S,带参数值,全写--file-size,可选项
* ⑦一个文件大小阀值选项:-l,带参数值,当-S出现时为必选项,否则该选项无效
* ⑧一个帮助信息选项:-h,无参数值
* </pre>.
*/
private Options searchOpts = new Options();
private CommandLine cl = null;
/** *//**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args) {
SearchCommandLineProcesser processer = new SearchCommandLineProcesser();
processer.run(args);
processer.validte();
}
/** *//**
* Instantiates a new search command line processer.
*/
public SearchCommandLineProcesser() {
String desc = "Specify the directory where search start";
Option optStartDir = OptionBuilder.withDescription(desc).isRequired(false)
.hasArgs().withArgName("START_DIRECTORY").create('d');
searchOpts.addOption(optStartDir);
}
/** *//**
* Set rule for command line parser, run parsing process
*
* @param args the args
*/
private void run(String[] args) {
setDate();
setDateRange();
setPrefix();
setSuffix();
setSize();
setSizeRange();
setHelp();
runProcess(searchOpts, args, new PosixParser());
}
/** *//**
* Sets the date.
*/
public void setDate() {
String desc = "Specify the file create date time";
Option optDate = OptionBuilder.withDescription(desc).isRequired(false)
.hasArgs().withArgName("FILE_CREATE_DATE").withLongOpt("date")
.create('D');
searchOpts.addOption(optDate);
}
/** *//**
* Sets the date range.
*/
public void setDateRange() {
StringBuffer desc = new StringBuffer(
"Specify acceptance date range for cutoff date specify by option -d");
desc.append("if true, older files (at or before the cutoff)");
desc.append("are accepted, else newer ones (after the cutoff)");
Option optDateRange = null;
optDateRange = OptionBuilder.withDescription(desc.toString())
.isRequired(false).hasArg().withArgName("DATE_RANGE")
.create('r');
searchOpts.addOption(optDateRange);
}
/** *//**
* Sets the prefix.
*/
public void setPrefix() {
String desc = "Specify the prefix of file, multiple prefixes can be split by comma";
Option optPrefix = OptionBuilder.withDescription(desc)
.isRequired(false).hasArgs().withArgName("FILE_PREFIXES")
.create('p');
searchOpts.addOption(optPrefix);
}
/** *//**
* Sets the suffix.
*/
public void setSuffix() {
String desc = "Specify the suffix of file, multiple suffixes can be split by comma";
Option optSuffix = OptionBuilder.withDescription(desc)
.isRequired(false).hasArgs().withArgName("FILE_SUFFIXES")
.create('s');
searchOpts.addOption(optSuffix);
}
/** *//**
* Sets the size.
*/
public void setSize() {
String desc = "Spcify the file size";
Option optSize = OptionBuilder.withDescription(desc).isRequired(false)
.hasArg().withArgName("FILE_SIZE_WITH_LONG_VALUE").withLongOpt(
"file-size").create('S');
searchOpts.addOption(optSize);
}
/** *//**
* Sets the size range.
*/
public void setSizeRange() {
StringBuffer desc = new StringBuffer(
"Specify acceptance size threshold for file specify by option -S");
desc.append("if true, files equal to or larger are accepted,");
desc.append("otherwise smaller ones (but not equal to)");
Option optDateRange = null;
optDateRange = OptionBuilder.withDescription(desc.toString())
.isRequired(false).hasArg().withArgName("SIZE_THRESHOLD")
.create('l');
searchOpts.addOption(optDateRange);
}
/** *//**
* Sets the help.
*/
public void setHelp() {
String desc = "Print help message and all options information";
Option optHelp = OptionBuilder.withDescription(desc).isRequired(false)
.create('h');
searchOpts.addOption(optHelp);
}
/** *//**
* Run process.
*
* @param opts the opts
* @param args the args
* @param parser the parser
*/
public void runProcess(Options opts, String[] args, CommandLineParser parser) {
try {
cl = process(searchOpts, args, parser);
} catch (ParseException e) {
System.out.println("Error on compile/parse command: "
+ e.getMessage());
printHelp(opts);
System.exit(-1);
}
Option[] allOpts = cl.getOptions();
Option opt = null;
for (int i = 0; i < allOpts.length; i++) {
opt = allOpts[i];
if("h".equals(opt.getOpt())) {
printHelp(opts);
System.exit(0);
}
System.out.println("Option name: -" + opt.getOpt()
+ ", and value = " + getOptValues(opt.getOpt(), ","));
}
}
/**//*
* (non-Javadoc)
*
* @see example.io.CommandLineProcesser#process(org.apache.commons.cli.Options,
* java.lang.String[], org.apache.commons.cli.CommandLineParser)
*/
public CommandLine process(Options options, String[] args,
CommandLineParser parser) throws ParseException {
return parser.parse(options, args);
}
/** *//**
* Validte required option and optional options
*/
private void validte() {
// Validate directory option
String directory = getOptValue("d");
if (directory == null) {
System.out.println("Missing start directory, ignore and exit");
System.exit(-1);
}
// Validate date option
String date = (getOptValue("D") == null) ? getOptValue("date")
: getOptValue("D");
String dateRange = getOptValue("r");
if(date != null && (dateRange == null)) {
System.out.println("Missing option -D/--date, exit immediately");
System.exit(-1);
}else if (date == null && (dateRange != null)) {
System.out.println("Date not specified, ignore option -r");
}
// Validate size option
String size = (getOptValue("S") == null) ? getOptValue("file-size")
: getOptValue("S");
String sizeRange = getOptValue("l");
if(size != null && (sizeRange == null)) {
System.out.println("Missing option -S/--file-size, exit immediately");
System.exit(-1);
}else if (size == null && (sizeRange != null)) {
System.out.println("File size not specified, ignore option -l");
}
}
/** *//**
* Prints the help.
*
* @param options the options
*/
public void printHelp(Options options) {
String formatstr = "java example.io.SearchCommandLineProcesser [-h][-d][-D/--date<-r>][-p][-s][-S/--size<-l>]";
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp(formatstr, options);
}
/**//*
* (non-Javadoc)
*
* @see example.io.CommandLineProcesser#getOptValue(java.lang.String)
*/
public String getOptValue(String opt) {
return (cl != null) ? cl.getOptionValue(opt) : "";
}
/**//*
* (non-Javadoc)
*
* @see example.io.CommandLineProcesser#getOptValues(java.lang.String)
*/
public String[] getOptValues(String opt) {
return (cl != null) ? cl.getOptionValues(opt) : new String[] { "" };
}
/**//*
* (non-Javadoc)
*
* @see example.io.CommandLineProcesser#getOptValues(java.lang.String,
* java.lang.String)
*/
public String getOptValues(String opt, String valueSeparater) {
String[] values = getOptValues(opt);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < values.length; i++) {
sb.append(values[i]).append(valueSeparater);
}
return sb.subSequence(0, sb.length() - 1).toString();
}
}
【五】结果演示
①演示使用方法:
控制台参数为:-h
②正确命令格式
控制台命令格式为:-d E:/Other/Picture/私人/ -D "2010-01-01-01 00:00:00" -r true -p IMG_,DSMG, -s .jpg,.gif --file-size 1024*1024*2 -l true
③错误命令格式
控制台命令格式为:-d E:/Other/Picture/私人/ -D "2010-01-01-01 00:00:00" -r true -p IMG_,DSMG, -s .jpg,.gif --file-size 1024*1024*2 -l true -Q
-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要尽力打好一手烂牌。
posted on 2010-04-02 14:20
Paul Lin 阅读(1062)
评论(0) 编辑 收藏 所属分类:
J2SE