【一】需求
前面我們使用了commons io包中的DirectoryWalker和IOFileFilter來進(jìn)行復(fù)雜條件的搜索,但是這個(gè)程序有幾個(gè)問題:
①選項(xiàng)都是hard code在代碼里面,難以修改
②有些選項(xiàng)是必需的,有些選項(xiàng)是可選的
③有些選項(xiàng)是不帶參數(shù)的,有些選項(xiàng)是要帶參數(shù)的
如果我們希望這個(gè)程序能夠更加靈活,根據(jù)人性化,那么我們需要提供一個(gè)界面,無論是普通的命令行、控制臺(tái)交互、GUI界面。而且還必須讓用戶自行決定是否要使用該選項(xiàng)。程序必須自動(dòng)根據(jù)已有的條件動(dòng)態(tài)組合
【二】簡(jiǎn)單而功能強(qiáng)大的commons CLI
Apache commons CLI是一個(gè)開源的,用于處理命令行的工具包。這個(gè)包目前的穩(wěn)定版本是1.2,他非常簡(jiǎn)單只有20個(gè)左右的class,但提供了幾乎所以可以用到的命令行功能。它的主頁在這里:Apache commons CLI
根據(jù)CLI的邏輯,每一個(gè)命令行的處理都可以分為3個(gè)步驟:定義、解析、交互
①定義:定義命令行的各種選項(xiàng)屬性(包括縮寫、全寫、是否必須、是否帶參數(shù)、參數(shù)個(gè)數(shù)限制)
②解析:使用解析器對(duì)命令行選項(xiàng)列表進(jìn)行解析
③交互:從解析好的命令行查詢用戶輸入的參數(shù)值并進(jìn)行處理
這里需要區(qū)分兩個(gè)名詞:選項(xiàng)(option)和參數(shù)(arguments)。選項(xiàng)是用來表明功能或者參數(shù)的意思的,例如“-d”這個(gè)字符串就是一個(gè)選項(xiàng),它可以表示一個(gè)日期。那么如果我們需要指定一個(gè)日期用于處理,就需要在“-d”后面再加上一個(gè)具體值,這個(gè)具體值就是參數(shù)(argument)。
對(duì)應(yīng)于這3個(gè)過程,我們來認(rèn)識(shí)幾個(gè)重要的類:
①定義階段
A.Option:這個(gè)類用于定義命令行的選項(xiàng),你可以通過構(gòu)造方法來定義一個(gè)選項(xiàng)
B.Options:Option的容器,用于存儲(chǔ)多個(gè)Option
C.OptionBuilder:使用描述性API來構(gòu)建Option,而非直接使用Option的構(gòu)造方法
②解析階段
A.CommandLineParser:接口,定義了parse方法由實(shí)現(xiàn)類實(shí)現(xiàn)
B.PosixParser:Posix風(fēng)格的命令行解析器
C.GnuParser:GNU風(fēng)格的命令行解析器
③交互階段
A.CommandLine:解析后的命令行對(duì)象,可以用于查詢選項(xiàng)的值
【三】CLI快速入門
通常情況下如果命令的選項(xiàng)比較簡(jiǎn)單我們使用構(gòu)造方法就夠了,但是當(dāng)選項(xiàng)的屬性比較復(fù)雜或者描述性文本比較長(zhǎng)時(shí),使用構(gòu)建器會(huì)令到程序的可讀性更進(jìn)一步。下面我們來看看這個(gè)需求:
有這樣一個(gè)命令行,它具備如下的選項(xiàng)和參數(shù)組合:
①一個(gè)目錄選項(xiàng):-d,帶參數(shù)值,必須選項(xiàng)
②一個(gè)日期選項(xiàng):-D,帶參數(shù)值,全寫--date,可選項(xiàng)
③一個(gè)日期范圍選項(xiàng):-r,帶參數(shù)值,當(dāng)-D出現(xiàn)時(shí)為必選項(xiàng),否則該選項(xiàng)無效
④一個(gè)文件名前綴選項(xiàng):-p,帶參數(shù)值,可以有多個(gè)前綴名,以逗號(hào)分隔,可選項(xiàng)
⑤一個(gè)文件擴(kuò)展名選項(xiàng):-s,帶參數(shù)值,可以有多個(gè)擴(kuò)展名,以逗號(hào)分隔,可選項(xiàng)
⑥一個(gè)文件大小選項(xiàng):-S,帶參數(shù)值,全寫--file-size,可選項(xiàng)
⑦一個(gè)文件大小閥值選項(xiàng):-l,帶參數(shù)值,當(dāng)-S出現(xiàn)時(shí)為必選項(xiàng),否則該選項(xiàng)無效
⑧一個(gè)幫助信息選項(xiàng):-h,無參數(shù)值
【四】代碼示例
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>
* ①一個(gè)目錄選項(xiàng):-d,帶參數(shù)值,必須選項(xiàng)
* ②一個(gè)日期選項(xiàng):-D,帶參數(shù)值,全寫--date,可選項(xiàng)
* ③一個(gè)日期范圍選項(xiàng):-r,帶參數(shù)值,當(dāng)-D出現(xiàn)時(shí)為必選項(xiàng),否則該選項(xiàng)無效
* ④一個(gè)文件名前綴選項(xiàng):-p,帶參數(shù)值,可以有多個(gè)前綴名,以逗號(hào)分隔,可選項(xiàng)
* ⑤一個(gè)文件擴(kuò)展名選項(xiàng):-s,帶參數(shù)值,可以有多個(gè)擴(kuò)展名,以逗號(hào)分隔,可選項(xiàng)
* ⑥一個(gè)文件大小選項(xiàng):-S,帶參數(shù)值,全寫--file-size,可選項(xiàng)
* ⑦一個(gè)文件大小閥值選項(xiàng):-l,帶參數(shù)值,當(dāng)-S出現(xiàn)時(shí)為必選項(xiàng),否則該選項(xiàng)無效
* ⑧一個(gè)幫助信息選項(xiàng):-h,無參數(shù)值
* </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();
}

}
【五】結(jié)果演示
①演示使用方法:
控制臺(tái)參數(shù)為:-h

②正確命令格式
控制臺(tái)命令格式為:-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

③錯(cuò)誤命令格式
控制臺(tái)命令格式為:-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 閱讀(1075)
評(píng)論(0) 編輯 收藏 所屬分類:
J2SE