之前讲了一大堆,关于 spoon 对程序进行分析的处理(Processor),别忘了 spoon 的另外一个功能,就是转换java源代码(代码级别的转换Transformation)
spoon针对程序转换,提供了不同级别上的非常好的仲裁机制(Intercession Mechanism)。在结构级(structural level),spoon可以让你添加或者删除包中,局部或者方法中的类型;在行为级(behavioral level),spoon可以让你修改任何部分的代码。举个例子,你可以为方法添加一些先决条件,或者自动为catch块中添加日志信息。
Table 2 提供了这些主要的仲裁方法整体的框架。这些方法都将一个活多个AST节点作为输入参数。
这些方法的使用也比较简单,我们以一个简单的例子来说明,比方说我想要在源程序中的每个方法的开始加上一个判断传入参数是否为空的验证,这个过程肯定需要我们之前将的分析器,也就是运用 CtParameter 这个元模型,将其作为参数,然后插入我们的代码部分(篇幅所限,我只贴出 process 函数)。
@Override
public void process( CtParameter arg0) {
// 声明一个CtCodeSnippetStatement(接口)的 snippet
CtCodeSnippetStatement snippet=
getFactory().Core().createCodeSnippetStatement();
//为snippet添加要加的语句
snippet. setValue("//this parameter is:"+arg0);
//如果该CtParameter元素的父级可执行元素不为空,我们就添加snippet
if(arg0. getParent( CtExecutable.class). getBody()!=null)
// 在父级元素的开头添加 snippet
arg0. getParent( CtExecutable.class). getBody(). insertBegin(snippet);
}
public void process( CtParameter arg0) {
// 声明一个CtCodeSnippetStatement(接口)的 snippet
CtCodeSnippetStatement snippet=
getFactory().Core().createCodeSnippetStatement();
//为snippet添加要加的语句
snippet. setValue("//this parameter is:"+arg0);
//如果该CtParameter元素的父级可执行元素不为空,我们就添加snippet
if(arg0. getParent( CtExecutable.class). getBody()!=null)
// 在父级元素的开头添加 snippet
arg0. getParent( CtExecutable.class). getBody(). insertBegin(snippet);
}
【注意】我的方法与 spoon 论文上的方法有所区别,需要指出的是论文上的方法存在错误(错误方法见 Spoon论文Section 3.1 中的Listing 3)
这样我们打开转换了的java函数(在生成的spoon文件夹中),就可以清楚的看清楚我们在每个 传参的方法内部的开头处,添加了注释,如下图所示,第一个java转换器大功告成。