接上文javac编译流程(1)准备阶段
回到compile方法
public Result compile(String[] argv, Context context) {
if (stdOut != null) {
context.put(Log.outKey, stdOut);
}
if (stdErr != null) {
context.put(Log.errKey, stdErr);
}
log = Log.instance(context);
//argv中没有输入信息,即,空值
if (argv.length == 0) {
//创建一个log包装类
OptionHelper h = new OptionHelper.GrumpyHelper(log) {
@Override
public String getOwnName() { return ownName; }
@Override
public void put(String name, String value) { }
};
try {
//调用process打印至控制台,我们上一篇已经分析过了
//如果我们没有设置默认输出流,默认为system.out,即,控制台
Option.HELP.process(h, "-help");
} catch (Option.InvalidValueException ignore) {
}
//返回结束标志符2
return Result.CMDERR;
}
process方法,注意是HELP里的process方法,如果直接点击process会跳至另外一个方法
HELP("--help -help", "opt.help", STANDARD, INFO) {
@Override
public void process(OptionHelper helper, String option) throws InvalidValueException {
//获取log对象
Log log = helper.getLog();
//获取指令名,这里为Javac
String ownName = helper.getOwnName();
//WriterKind.STDOUT表示标准输入
//PrefixKind.JAVAC,即,Javac
log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.header", ownName);
//打印可用命令和帮助文档
showHelp(log, OptionKind.STANDARD);
//进行换行,意义不大
log.printNewline(WriterKind.STDOUT);
//进入父类的process方法
super.process(helper, option);
}
}
进入printLines
public void printLines(WriterKind wk, PrefixKind pk, String key, Object... args) {
//getWriter用于获取对应的流,即,system.out,没什么好讲的
//我们重点来讲localize
printRawLines(getWriter(wk), localize(pk, key, args));
}
跳过包装类,进入getLocalizedString方法
private static String getLocalizedString(List<ResourceBundle> bundles,
String key,
Object... args) {
String msg = null;
//bundles是一个本地化的提示信息集合
for (List<ResourceBundle> l = bundles; l.nonEmpty() && msg == null; l = l.tail) {
ResourceBundle rb = l.head;
try {
//如果getString找不到对应的string,会直接报错,即,不会赋值给msg
//所以msg如果找到了对应的打印数据,不会被null覆盖
msg = rb.getString(key);
}
catch (MissingResourceException e) {
// ignore, try other bundles in list
}
}
if (msg == null) {
msg = "compiler message file broken: key=" + key +
" arguments={0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}";
}
//使用Java文字格式类返回格式化后的结果
return MessageFormat.format(msg, args);
}
这里要讲一下格式化的效果
//格式化前的字符串
"用法: {0} <options> <source files>\n其中, 可能的选项包括:"
//格式化后的字符串
"用法: javac <options> <source files>\n其中, 可能的选项包括:"
效果类似于动态sql语句,回到printRawLines方法,将返回信息逐行打印
public static void printRawLines(PrintWriter writer, String msg) {
int nl;
while ((nl = msg.indexOf('\n')) != -1) {
writer.println(msg.substring(0, nl));
msg = msg.substring(nl+1);
}
if (msg.length() != 0) writer.println(msg);
}
最终打印出提示信息的前两行,进入showHelp
private static void showHelp(Log log, OptionKind kind) {
//创建Comparator用于给流对象排序
Comparator<Option> comp = new Comparator<Option>() {
//默认以英文规则排序
final Collator collator = Collator.getInstance(Locale.US);
//只比较首字母
{ collator.setStrength(Collator.PRIMARY); }
@Override
//使用上面创建的比较器
public int compare(Option o1, Option o2) {
return collator.compare(o1.primaryName, o2.primaryName);
}
};
//获取本类中所有的枚举对象
getJavaCompilerOptions()
//将其转换为流对象
.stream()
//对流对象进行筛选,只保留kind等于OptionKind.STANDARD的枚举对象
.filter(o -> o.kind == kind)
//使用上面创建的Comparator进行排序
.sorted(comp)
//遍历流对象,并调用help方法
.forEach(o -> {
o.help(log);
});
}
进入help方法
protected void help(Log log) {
//localize方法本质上就是getLocalizedString方法,请参考上面的分析
help(log, log.localize(PrefixKind.JAVAC, descrKey));
}
进入真正的help方法,主要是做一些格式判断,主要方法为printRawLines,已在上面分析过了,请自行查看
protected void help(Log log, String descr) {
String synopses = Arrays.stream(names)
.map(s -> helpSynopsis(s, log))
.collect(Collectors.joining(", "));
// If option synopses and description fit on a single line of reasonable length,
// display using COMPACT_FORMAT
if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH
&& !descr.contains("\n")
&& (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + descr.length() <= DEFAULT_MAX_LINE_LENGTH)) {
log.printRawLines(WriterKind.STDOUT, String.format(COMPACT_FORMAT, synopses, descr));
return;
}
// If option synopses fit on a single line of reasonable length, show that;
// otherwise, show 1 per line
if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {
log.printRawLines(WriterKind.STDOUT, SMALL_INDENT + synopses);
} else {
for (String name: names) {
log.printRawLines(WriterKind.STDOUT, SMALL_INDENT + helpSynopsis(name, log));
}
}
// Finally, show the description
log.printRawLines(WriterKind.STDOUT, LARGE_INDENT + descr.replace("\n", "\n" + LARGE_INDENT));
}
进入父类的process方法
public void process(OptionHelper helper, String option) throws InvalidValueException {
//若为无参
if (argKind == ArgKind.NONE) {
process(helper, primaryName, option);
} else {
//findSeparator会对参数个数进行判断
int sep = findSeparator(option);
process(helper, primaryName, option.substring(sep + 1));
}
}
最终的process方法,由于创建OptionHelper时对put方法设为空,且未重写remove方法和handleFileManagerOption方法,该方法没有任何效果(当然,如果执行remove或handleFileManagerOption直接报错也算的话(lll¬ω¬),也许有那么一点点效果ε=ε=ε=┏(゜ロ゜;)┛)
public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
if (choices != null) {
if (choiceKind == ChoiceKind.ONEOF) {
// some clients like to see just one of option+choice set
for (String s : choices)
helper.remove(primaryName + s);
String opt = primaryName + arg;
helper.put(opt, opt);
// some clients like to see option (without trailing ":")
// set to arg
String nm = primaryName.substring(0, primaryName.length() - 1);
helper.put(nm, arg);
} else {
// set option+word for each word in arg
for (String a: arg.split(",+")) {
String opt = primaryName + a;
helper.put(opt, opt);
}
}
}
helper.put(primaryName, arg);
if (group == OptionGroup.FILEMANAGER)
helper.handleFileManagerOption(this, arg);
}
至此,处理空值结束