对于 Groovy 用户而言,每次查找 JavaDoc 都很麻烦,因为 Groovy 本身带了三种不同的 JavaDoc,分别在三个目录下,再加上原本的 JDK API,每次都要在四个文档中都搜索一遍。为了解决这个问题,这里提供一个脚本来对这四个文档进行合并。
大体的使用场景如下:比如要查 java.util.Date 这个类, 那么在搜索栏里输入 Date,则列表中出现所有类名为 Date 的类(还包括 java.sql.Date,如果勾选了 Fuzzy 选项,那么所有以 Date 开头的类都会列出)。在列表中选择 java.util.Date,点击按钮,则系统将自动打开合并后的页面。在这里,我们可以从页面顶部的目录看到 Date 类在 JDK 和 Groovy-JDK 中均有出现。
1: #!/bin/env groovy
2: import groovy.swing.*
3: import javax.swing.*
4:
5: class JavaDoc {
6: // 获取环境变量
7: // 使用环境变量来定位文档目录是根据我的文档目录位置而决定的
8: // 对于其它情况,可以简单的手动修改 folders 的内容
9: static env(name) { System.getenv().get(name) }
10: static final SEP = File.separator
11: static folders = ['J2SE API Specification': "${env('JAVA_HOME')}${SEP}docs${SEP}api"]
12: static String css = new File("${folders.'J2SE API Specification'}${SEP}stylesheet.css").toURL()
13: static String pic = new File("${folders.'J2SE API Specification'}${SEP}/resources/inherit.gif").toURL()
14: static {
15: ['JavaDoc for Java classes': 'api',
16: 'GroovyDoc for Groovy and Java classes': 'gapi',
17: 'Groovy JDK API Specification': 'groovy-jdk'].each { folder, path ->
18: folders.put(folder, "${env('GROOVY_HOME')}${SEP}html${SEP}$path")
19: }
20: folders.each { type, folder ->
21: new File(folder).eachFileRecurse { file ->
22: if(file.name.matches(~/[A-Z].*/.html/) && !file.parent.endsWith('class-use')) {
23: docs << new JavaDoc(type, file)
24: }
25: }
26: }
27: }
28:
29: static docs = [] //脚本将一次性扫描所有文件,并将结果存入该列表
30:
31: // 根据SimpleName查找并返回其(完整)类名列表
32: // fuzzy: 是否模糊查找(返回所有keyword开头的类)
33: // 注意这里闭包的用法很帅
34: static String[] search(String keyword, fuzzy = false) {
35: def cls = fuzzy ? { it.startsWith(keyword) } : { it == keyword }
36: docs.findAll { cls(it.simpleName) }.collect { it.name }.unique()
37: }
38:
39: static final HEAD = """text/css" HREF="$css" TITLE="Style">""
40: " TYPE="
41: ">">${it.type}"
42: "
43: static final END = ''
44:
45: static String getHtml(String name) {
46: def docs = docs.findAll { it.name == name }
47: if(!docs) return ''
48: final tableOfContents = docs.collect { """"" }.join('
')
49: "$HEAD$tableOfContents${docs.collect{ it.classData }.join('/n')}$END"
50: }
51:
52: final type, file, name, simpleName
53: def JavaDoc(type, file) {
54: this.type = type
55: this.file = file
56: simpleName = file.name - '.html'
57: name = (file.absolutePath - "${folders.get(type)}$SEP" - '.html').replace(SEP, '.')
58: }
59:
60: String getTypeId() { type.replaceAll(' ', '') }
61:
62: String getClassData() {
63: final text = file.text.replaceAll(~/".*//inherit/.gif"/,
64: ")
65: final start = text.indexOf('START OF CLASS DATA')
66: final end = text.indexOf('END OF CLASS DATA')
67: final html = text.substring(start, end)
68: """">$type
">(Back to top) """
69: }
70: }
71:
72: m = new DefaultListModel()
73: desktop = java.awt.Desktop.getDesktop()
74: browser = {
75: if(docList.selectedIndex != -1) {
76: def f = File.createTempFile('ApiDocBuster', '.html')
77: f.write(JavaDoc.getHtml(docList.selectedValue))
78: desktop.open(f)
79: }
80: }
81:
82: search = {
83: def keyword = keywordField.text
84: if(keyword.size() > 1) { // 检查了下,类名最少两个字符
85: m.clear()
86: JavaDoc.search(keywordField.text, cb.selected).each { m << it }
87: }
88: }
89:
90: w = 300
91: new SwingBuilder().edt {
92: frame(title: 'API Doc Buster', size:[w, 240], show: true, locationRelativeTo: null) {
93: lookAndFeel('system')
94: vbox {
95: scrollPane { docList = list(model: m) }
96: panel(maximumSize: [w, 24]) {
97: boxLayout()
98: cb = checkBox(text: 'Fuzzy')
99: keywordField = textField(caretUpdate: search)
100: button(text: 'browser', actionPerformed: browser)
101: }
102: }
103: }
104: }
一百多行的代码就搞定了。换成纯粹的 Java 代码可能连 Swing 界面都写不完。SwingBuilder 的强大完美的诠释了 Groovy 的强大。
PS. 如果你在运行这个脚本的时候发生初始化错误,很可能是你的文档路径和我不同,这样的话可以去修改 folders 的内容,直接写死也没什么问题的。