0
点赞
收藏
分享

微信扫一扫

Opentsdb源码解析之ArgP类

自由的美人鱼 2022-01-28 阅读 34
sedi++java


Opentsdb源码解析之ArgP类

1.类注释

/**
A dead simple command-line argument parser.Because I couldn't find any one in Java that wasn't horribly bloated.
一个超级简单的命令行参数解析器。因为作者他实在找不到java中不臃肿的参数解析器了:(【一言不合就是造轮子】

This parser honors the convention that argument -- means "stop parsing options".
这个解析器尊重惯例:符号'--'意味着停止解析

This class is not thread-safe. 这个类并非线程安全
*/

如注释中所说,这个就是用于解析运行参数的类。

2.源代码

源代码如下:

package opentsdb;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

public final class ArgP {
/**
* Maps an option name (e.g, {@code "--foo"}) to a 2-element array
* {@code ["META", "Help string"]}
*
* 将一个选项名映射成一个String[](例如:将--foo 映射成size=2的String数组分别是META,HELP string)。
* 意思就是说这个options是一个map.
*/
private final HashMap<String, String[]> options
= new HashMap<String, String[]>();

/**
* Maps an option name to the value parsed for this option.
* The value can be {@code null}.
* 将option解析成一个option name->value的映射。值可以是null。
* 【Lawson:这个parsed是一个hashMap,是真正的所有参数对应的值,下面我举一个例子】
* 如果在TSDMain中的参数形式如下:
* --port 3306 --zkquorum 192.168.211.4
* 那么得到的parsed值就是[port,3306],[zkquorum,192.168.211.4]
*
*/
private HashMap<String, String> parsed;

/** Constructor. */
public ArgP() {
}

/**
* Registers an option in this argument parser. 在这个参数解析器中注册一个选项
* @param name The name of the option to recognize (e.g. {@code --foo}). 需要识别的选项名
* @param meta The meta-variable to associate with the value of the option. 与这个选项值想连的meta-variable
* @param help A short description of this option. 关于此选项简单的描述
*
* @throws IllegalArgumentException if the given name was already used.
* @throws IllegalArgumentException if the name doesn't start with a dash.
* @throws IllegalArgumentException if any of the given strings is empty.
*/
public void addOption(final String name,
final String meta,
final String help) {
if (name.isEmpty()) {
throw new IllegalArgumentException("empty name");
} else if (name.charAt(0) != '-') {
throw new IllegalArgumentException("name must start with a `-': " + name);
} else if (meta != null && meta.isEmpty()) {
throw new IllegalArgumentException("empty meta");
} else if (help.isEmpty()) {
throw new IllegalArgumentException("empty help");
}

//理解 => 为什么put()之后还有返回值
final String[] prev = options.put(name, new String[] { meta, help });
if (prev != null) {
options.put(name, prev); // Undo the 'put' above.
throw new IllegalArgumentException("Option " + name + " already defined"
+ " in " + this);
}
}

/**
* Registers an option that doesn't take a value in this argument parser.
* @param name The name of the option to recognize (e.g. {@code --foo}).
* @param help A short description of this option.
* @throws IllegalArgumentException if the given name was already used.
* @throws IllegalArgumentException if the name doesn't start with a dash.
* @throws IllegalArgumentException if any of the given strings is empty.
*/
public void addOption(final String name, final String help) {
addOption(name, null, help);
}

/**
* Returns whether or not the given option name exists.
* <p>Calling
* {@link #addOption(String, String, String) addOption}{@code (foo, ...)}
* entails that {@code optionExists(foo)} returns {@code true}.
* @param name The name of the option to recognize (e.g. {@code --foo}).
*/
public boolean optionExists(final String name) {
return options.containsKey(name);
}

/**
* Parses the command line given in argument. 解析参数中命令行
*
* @return The remaining words that weren't options (i.e. that didn't start with a dash).
* 剩下的单词则不是options(它们没有以一个dash开头)
*
* @throws IllegalArgumentException if the given command line wasn't valid.
*/
public String[] parse(final String[] args) {
System.out.println("options.size is :"+options.size());

//实例化parsed
parsed = new HashMap<String, String>(options.size());

//注意这个unparsed是一个空引用
ArrayList<String> unparsed = null;

//使用for循环进行遍历Stringp[] args => 这个args其实就是TSDMain这个类中的运行参数
for (int i = 0; i < args.length; i++) {
final String arg = args[i];

//因为options是一个map,可以根据arg(其实是options的key)去获取value
String[] opt = options.get(arg);//注意option 是一个String 到String[]的HashMap,所以下面会判断opt[0]是否为null
if (opt != null) { // Perfect match: got --foo 首先判断value(是一个String[])是不是空
if (opt[0] != null) { // This option requires an argument.
if (++i < args.length) {//care!! ++i 是为了取到第二个value 因为在args中都是 "--port 3306"这么写的,所以如果
//前面条件都满足的时候,就可以拿到这个参数对应的值了。
parsed.put(arg, args[i]);
} else {
throw new IllegalArgumentException("Missing argument for " + arg);
}
} else {
parsed.put(arg, null); //如果没有value,则赋为空
}
continue;
}


// Is it a --foo=blah? 也就是说如果参数的形式是--foo=blah这种,就需要使用如下的判断
final int equal = arg.indexOf('=', 1);//从下标1之后找出arg中第一次出现字符为'='的下标
if (equal > 0) { // Looks like so.
final String name = arg.substring(0, equal);//得到的name是"--foo"
opt = options.get(name);
if (opt != null) {
parsed.put(name, arg.substring(equal + 1, arg.length()));//取arg的后一部分值
continue;
}
}


// Not a flag.
//这地方真是精妙,如果unparsed这个ArrayList在经过i轮检测之后,则将其初始化的大小设置为args.length - i
//但是存在的问题是,如果第一次是null,那么第二次还是null。那么会初始化两次么?那么就是说,至少有一次的初始化操作是多余的。
//这个对性能以及内存有影响么?
if (unparsed == null) {
unparsed = new ArrayList<String>(args.length - i);
}
if (!arg.isEmpty() && arg.charAt(0) == '-') {
if (arg.length() == 2 && arg.charAt(1) == '-') { // judge whether or not '--'
for (i++; i < args.length; i++) {
unparsed.add(args[i]);
}
break;
}
throw new IllegalArgumentException("Unrecognized option " + arg);
}
unparsed.add(arg);
}

//最后是做一个返回值的确认操作
if (unparsed != null) {//如果存在不能解析的参数则返回
return unparsed.toArray(new String[unparsed.size()]);
} else {//否则返回一个空string[]
return new String[0];
}
}

/**
* Returns the value of the given option, if it was given.
* Returns {@code null} if the option wasn't given, or if the option doesn't
* take a value (in which case you should use {@link #has} instead).
* @param name The name of the option to recognize (e.g. {@code --foo}).
* @throws IllegalArgumentException if this option wasn't registered with
* {@link #addOption}.
* @throws IllegalStateException if {@link #parse} wasn't called.
*/
public String get(final String name) {
if (!options.containsKey(name)) {
throw new IllegalArgumentException("Unknown option " + name);
} else if (parsed == null) {
throw new IllegalStateException("parse() wasn't called");
}
return parsed.get(name);
}

/**
* Returns the value of the given option, or a default value.
* 获取已知option的值,否则返回一个默认值
*
* @param name The name of the option to recognize (e.g. {@code --foo}).
* @param defaultv The default value to return if the option wasn't given.
* @throws IllegalArgumentException if this option wasn't registered with
* {@link #addOption}.
* @throws IllegalStateException if {@link #parse} wasn't called.
*/
public String get(final String name, final String defaultv) {
final String value = get(name);
return value == null ? defaultv : value;
}

/**
* Returns whether or not the given option was given.
* @param name The name of the option to recognize (e.g. {@code --foo}).
* @throws IllegalArgumentException if this option wasn't registered with
* {@link #addOption}.
* @throws IllegalStateException if {@link #parse} wasn't called.
*/
public boolean has(final String name) {
if (!options.containsKey(name)) {
throw new IllegalArgumentException("Unknown option " + name);
} else if (parsed == null) {
throw new IllegalStateException("parse() wasn't called");
}
return parsed.containsKey(name);
}

/**
* Appends the usage to the given buffer.
* @param buf The buffer to write to.
*/
public void addUsageTo(final StringBuilder buf) {
final ArrayList<String> names = new ArrayList<String>(options.keySet());
Collections.sort(names);
int max_length = 0;
for (final String name : names) {
final String[] opt = options.get(name);
final int length = name.length()
+ (opt[0] == null ? 0 : opt[0].length() + 1);
if (length > max_length) {
max_length = length;
}
}
for (final String name : names) {
final String[] opt = options.get(name);
int length = name.length();
buf.append(" ").append(name);
if (opt[0] != null) {
length += opt[0].length() + 1;
buf.append('=').append(opt[0]);
}
for (int i = length; i <= max_length; i++) {
buf.append(' ');
}
buf.append(opt[1]).append('\n');
}
}

/** Returns a the parsed options and values */
public HashMap<String, String> getParsed() {
return this.parsed;
}

/**
* Returns a usage string.
*/
public String usage() {
final StringBuilder buf = new StringBuilder(16 * options.size());
addUsageTo(buf);
return buf.toString();
}

public String toString() {
final StringBuilder buf = new StringBuilder(16 * options.size());
buf.append("ArgP(");
for (final String name : options.keySet()) {
final String[] opt = options.get(name);
buf.append(name)
.append("=(").append(opt[0]).append(", ").append(opt[1]).append(')')
.append(", ");
}
buf.setLength(buf.length() - 2);
buf.append(')');
return buf.toString();
}
}

3.注释

鉴于在代码中的注释写的很清楚了,这里我就不再赘述了。

这里简单介绍其他的一些附加内容:

  • main方法中的args参数
public static void main(String[] args) {
//输出args的值
System.out.print("args are :");
for(int i = 0;i< args.length;i++)
{
String arg = args[i];
System.out.println(args[i]);
}
}

其中的args来自于​​Program argument​​这栏中的参数设置,如下:

Opentsdb源码解析之ArgP类_java

输出结果是:



举报

相关推荐

0 条评论