使用IDE有很多好处,比如重构会很方便,比如说GUI组件拖曳等。这些功能都很好很强大,也很难在字符界面找到很好的解决方案,但是也有一些简单的而重要的功能比如代码模板等,可以非常方便的用脚本来实现。
首先来看一下模板的结构。在脚本编程中我们经常用到类似于$HOME之类的形式将变量嵌在字符串内,那么用同样的方式也可以用来描述模板。以下的范例是从NetBeans中复制的代码模板
1: <#assign licenseFirst = "/*">
2: <#assign licensePrefix = " * ">
3: <#assign licenseLast = " */">
4: <#include "../Licenses/license-${project.license}.txt">
5:
6: <#if package?? && package != "">
7: package ${package};
8:
9: if>
10: /**
11: *
12: * @author ${user}
13: */
14: public class ${name} {
15:
16: }
每当在NetBeans中新建一个Java Class时,ide就会调用这个模板,在其中加入许可、package声明和类的主体框架。<# >的结构使得模板中可以包含复杂的逻辑。以下,我们来在Groovy下实现它(这就是我在回归ksh中提到的脚本,不过我太烂以至于不想用其它语言来重写一遍,也太笨以至于不知道如何用其它语言写一遍……)。注意,因为是用Groovy实现的,我忍不住在模板中直接使用了一些Java代码,如果要考虑模板的通用性,可以把这部分代码用$的结构简单替代。
首先假设我们要创建一个groovy script的模板,其中包含起始的shebang、许可、代码说明以及一句hello world的输出。那么它看起来会是这个样子: $XMETA_HOME/templates/java/gsh
#!/bin/env groovy
/*
* SCRIPT: ${file_name}
* AUTHOR: ${xmeta_user}
* DATE: ${today}
*
* PURPOSE:
*/
println 'hello $xmeta_user'
看起来和Netbeans的模板不太一样,但原理还是相同的,这里用到了类似于JSP的语法。在第三行我们看到了变量license_text也就是许可证,license的内容当然不会写死在程序里,也使用模板生成。创建第二个模板$XMETA_HOME/templates/license/gpl3:
Copyright (C) $current_year $xmeta_user
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation,either version 3 of the License,or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not,see //www.gnu.org/licenses/>.
可以看到license的结构很简单,只包含两个变量。同理也可以简单建立如apache2等流行的许可。
为了实现模板替换,以下将建立一个cast脚本,这个脚本做的事非常简单,就是读取模板,然后以环境变量和脚本上下文的变量以及命令行的输入内容来替换模板中的参数,最后打印到标准输出(这里没有直接生成文件,是为了便于和其它脚本的配合),比如要生成一个名字为hello的groovy script,需要运行的命令为:
cast $XMETA_HOME/templates/java/gsh file_name=hello.gsh
# 不插入license
cast $XMETA_HOME/templates/java/gsh file_name=hello.gsh --nolicense
# 使用apache 2.0 license
cast $XMETA_HOME/templates/java/gsh file_name=hello.gsh license=apache2
看起来还有点小罗嗦,但是记住,这个cast脚本是给其它脚本用的,所以罗嗦一点点不是大问题,但是,这个问题我们会留在以后解决。cast脚本可以使用--nolicense的选项来禁止在生成的文件中插入许可。
$XMETA_HOME/scripts/cast
/*
* Copyright (C) 2010 Young,Fey
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation,or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not,see .
************************************************************************
* SCRIPT: cast
* AUTHOR: Young,Fey
* DATE: 2010-8-31
* REV: 1.0.P
* PURPOSE: read a template file,substitute the '$VAR' likes parts and print
* it out. the script will replace these varibles with environment
* variables (include those which was exported in the context) or
* command line input (see usage)
* notice: command line inputs may override context variables if they
* share the same name,and context variables may override env-var.
import groovy.text.SimpleTemplateEngine
def err(msg,code) {
System.err.println msg
System.exit(code)
}
// check args
if(args.size() < 1) {
err("Usage: cast template_file /noption: --nolicense without license insert",1)
}
// template file must exists
def templateFile = new File(args[0])
if(!templateFile.exists() || templateFile.isDirectory()) {
err('template not found',2)
template = null
engine = new SimpleTemplateEngine()
try {
template = engine.createTemplate(templateFile)
} catch(ex) {
err('Illegal template',3)
noLicense = false
if(args.size() > 1) args[1..-1].each { var ->
if(var == '--nolicense') {
noLicense = true
} else {
list = var.split('=')
if(list.size() >= 2) vars.put(list[0],list[1..-1].join('='))
}
vars = [file_name: 'untitled']
vars += System.getenv()
setDefault = { key,value ->
if(!vars.containsKey(key)) vars.putAt(key,value)
setDefault('xmeta_user',vars.USER)
setDefault('today','datestr -s'.execute().text[0..-2])
setDefault('license_text',null)
// handle the license
if(!noLicense) {
setDefault('license',"${vars.XMETA_HOME}/templates/license/gpl3")
licenseTemplate = engine.createTemplate(
new File(vars.license))
vars.license_text = licenseTemplate.make(vars).toString()
println template.make(vars)
这里用到了groovy的模板引擎。注意templates文件夹的位置和license文件夹的位置,它们是不能随便放的,还有就是默认的许可是gpl3,可以换成自己喜欢的(省得每次都要输入license=XXX)。