posts - 262,  comments - 221,  trackbacks - 0
【一】需求

前面我们使用了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 阅读(1054) 评论(0)  编辑  收藏 所属分类: J2SE

只有注册用户登录后才能发表评论。


网站导航:
 
<2010年4月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

常用链接

留言簿(21)

随笔分类

随笔档案

BlogJava热点博客

好友博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜