【一】需求
前面我們使用了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 閱讀(1073)
評論(0) 編輯 收藏 所屬分類:
J2SE