| |
在Eclipse中创建新的重构功能 |
|
时间: 2005-12-05 来自:ibm |
 |
|
|
135d
使用AST构造Change对象
当我们找到了修改的位置时,我们有两个选择:
1. 通过IScanner接口扫描代码,然后通过IBuffer接口直接修改代码
2. 通过遍历和编辑AST树进行结构化的修改
DeveloperWorks提供的文章《扩展Eclipse的Java开发工具》中,给出了使用IBuffer接口的例子。现在我们要讲述使用AST来遍历和修改Java源代码的方法。
AST是abstract syntax tree的缩写。它是Eclipse中的Java开发环境(JDT)为我们提供的极为强大的源代码解析和编辑工具。
在使用AST树提供的功能之前,我们首先要创建AST树。由于AST树的构建是一项费时的操作,JDT缺省情况下不将源代码解析为AST树。下面的代码演示了获得一个ICompilationUnit对应的AST树的过程。在JDT提供的API中,ICompilationUnit接口用于表示一个可以被编译的源代码文件。在我们提供的例子程序中,我们通过下面的代码将整个文件解析成为了一颗AST树。
清单 9
AST树中的每个节点都是ASTNode类型,通过Visit模式,我们可以访问一个ASTNode包含的所有节点。下面的代码演示了访问一个AST节点并获得其中所有的MethodDeclaration节点的方法。
清单 10
private void getMethods(ASTNode cuu, final List methods) { cuu.accept(new ASTVisitor() { public boolean visit(MethodDeclaration node) { methods.add(node); return false; } }); } | 在收集到了所有的MethodDeclaration节点之后,我们就可以通过向AST树中插入和删除节点或者修改已有的节点的方法来修改AST树了。下面的代码演示了使用AST工具为方法添加@Test Annotation的功能。
清单 11
private boolean collectChanges(CompilationUnit root,MethodDeclaration method) { if (method.getName().getFullyQualifiedName().startsWith("test")) { AST ast = method.getAST(); if (needTimeout) { NormalAnnotation na = ast.newNormalAnnotation(); na.setTypeName(ast.newSimpleName("Test")); MemberValuePair pair = ast.newMemberValuePair(); pair.setName(ast.newSimpleName("timeout")); pair.setValue(ast.newNumberLiteral("500")); na.values().add(pair); method.modifiers().add(0, na); } else { MarkerAnnotation na = ast.newMarkerAnnotation(); na.setTypeName(ast.newSimpleName("Test")); method.modifiers().add(0, na); } return true; } return false; } | 在Refactoring框架中,我们要求对AST树的修改并不立刻反映到源代码中。相反,我们需要一个能记录整个修改过程的Change对象。Refactoring框架将利用这个Change对象来显示Priveiw窗口、进行Undo和Redo等操作。大致上,我们记录对一个AST树的修改从而生成Change对象的过程如以下代码所示。
清单 12
root.recordModifications();
//在这里修改AST树…
TextEdit edits = root.rewrite(document, cu.getJavaProject() .getOptions(true)); TextFileChange change = new TextFileChange("", (IFile) cu .getResource()); change.setEdit(edits); | 最后,由于Refactoring类的createChange方法仅返回一个Change对象,如果我们需要对多个源代码文件进行修改,我们可以利用CompositeChange类将多个Change对象封装成一个Change对象。这个过程可能类似如下代码所执行的流程
清单 13
public Change createChange(IProgressMonitor pm) throws CoreException,OperationCanceledException { Change[] changes = new Change[fChangeManager.size()]; System.arraycopy(fChangeManager.toArray(), 0, changes, 0,fChangeManager.size()); CompositeChange change = new CompositeChange("Add @Override Annotation", changes); return change; } |
15b
|
|
|
|
|
|
|
|