【Eclipse AST】AST的创建

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LoveLion/article/details/20309379

       对于源代码优化过程而言,借助前文所介绍的AST访问与解析,可以自动查找出哪些地方需要优化,或者说能够发现优化或重构“时机”。接下来要做的就是采取相应的优化措施,在AST中创建节点(包括创建新的AST)或修改原有节点。本文将介绍如何创建一棵AST,以及通过AST来生成Java源代码。      


       在创建节点时,需要开发人员对AST中的节点有较为深入的了解。Eclipse AST中的AST类采用了工厂方法模式,针对所有类型的节点都提供了new***()方法来创建新的节点,如newTypeDeclaration()、newMethodInvocation()等等。“创建规则”是从叶子节点开始创建,先创建子节点后创建父节点,再将它们关联起来。这也意味着我们需要为创建一小段Java代码而编写一大段AST操作代码。

       例如创建如下单行变量声明语句:

int i = 1;

       在创建代码对应的AST之前,可以先通过ASTView观察一下此代码由哪些节点组成,如图1所示。

 

  图1 示例代码AST    

       根据创建规则需要从“int”、“i”和“1”开始创建。完整的创建代码如下,其中VariableDeclarationStatement即为所需创建的节点:

SimpleName name = ast.newSimpleName("i");
NumberLiteral number = ast.newNumberLiteral("1");
PrimitiveType type = ast.newPrimitiveType(PrimitiveType.INT);

//创建语句“i = 1”
VariableDeclarationFragment fragment = ast.newVariableDeclarationFragment();
fragment.setName(name);
fragment.setInitializer(number);

//创建“int i = 1;”
VariableDeclarationStatement statement = ast.newVariableDeclarationStatement(fragment);
statement.setType(type);

       但是,根据创建顺序进行编码加大了代码的阅读难度,因此建议采用局部按照创建顺序,整体按照编码顺序进行编写

       下面我们来创建一个完整的如下HelloWorld类:

public class HelloWorld {
  public HelloWorld(){
    System.out.println("Hello World!");
  }
}

        由于是从无到有创建一个新的类,这意味着需要构建一个新的AST,而且创建新节点的任务需要通过该AST对象调用相应的new***()方法来完成,因此首先可以通过解析空的代码来创建一个空的AST

ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource("".toCharArray());

CompilationUnit comp = (CompilationUnit) parser.createAST(null); 
comp.recordModifications();

AST ast = comp.getAST();

       根据编码顺序,我们需要先创建一个公共类,类名为HelloWorld,并把类节点作为编译单元comp的子节点。

/*
*public class HelloWorld {
*
*}
*/
TypeDeclaration classDec = ast.newTypeDeclaration();
classDec.setInterface(false);//设置为非接口

SimpleName className = ast.newSimpleName("HelloWorld");
Modifier classModifier = ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);

//设置类节点
classDec.setName(className);//类名
classDec.modifiers().add(classModifier);//类可见性

//将类节点连接为编译单元的子节点
comp.types().add(classDec);

       然后创建构造函数HelloWorld,并作为类节点classDec的子节点。

/*
*public class HelloWorld {
*  public HelloWorld(){
*    
*  }
*}
*/
MethodDeclaration methodDec = ast.newMethodDeclaration();
methodDec.setConstructor(true);//设置为构造函数

SimpleName methodName = ast.newSimpleName("HelloWorld");
Modifier methodModifier = ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
Block methodBody = ast.newBlock();

//设置方法节点
methodDec.setName(methodName);// 方法名
methodDec.modifiers().add(methodModifier);//方法可见性
methodDec.setBody(methodBody); //方法体

//将方法节点连接为类节点的子节点
classDec.bodyDeclarations().add(methodDec);

       创建方法体中的输出语句,并作为方法节点methodDec的子节点。

/*
*public class HelloWorld {
*  public HelloWorld(){
*    System.out.println("Hello World!");
*  }
*}
*/
MethodInvocation methodInv = ast.newMethodInvocation();

SimpleName nameSystem = ast.newSimpleName("System");
SimpleName nameOut = ast.newSimpleName("out");
SimpleName namePrintln = ast.newSimpleName("println");

//连接‘System’和‘out’
//System.out
QualifiedName nameSystemOut = ast.newQualifiedName(nameSystem, nameOut);

//连接‘System.out’和‘println’到MethodInvocation节点
//System.out.println()
methodInv.setExpression(nameSystemOut);
methodInv.setName(namePrintln);

//”Hello World!”
StringLiteral sHelloworld = ast.newStringLiteral();
sHelloworld.setEscapedValue("\"Hello World!\"");

//System.out.println(“Hello World!”)
methodInv.arguments().add(sHelloworld);

//将方法调用节点MethodInvocation连接为表达式语句ExpressionStatement的子节点
//System.out.println(“Hello World!”);
ExpressionStatement es = ast.newExpressionStatement(methodInv);

//将表达式语句ExpressionStatement连接为方法节点的点
methodBody.statements().add(es);


       完整的创建代码如下:

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;

public class HelloWorldBuilder {
	
	public HelloWorldBuilder() {
		build();
	}
	
	private void build() {
		ASTParser parser = ASTParser.newParser(AST.JLS3);
		parser.setSource("".toCharArray());
		
		CompilationUnit comp = (CompilationUnit) parser.createAST(null); 
		comp.recordModifications();
		
		AST ast = comp.getAST();
		
		/*
		*public class HelloWorld {
		*
		*}
		*/
		TypeDeclaration classDec = ast.newTypeDeclaration();
		classDec.setInterface(false);//设置为非接口
		
		SimpleName className = ast.newSimpleName("HelloWorld");
		Modifier classModifier = ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
		
		//设置类节点
		classDec.setName(className);//类名
		classDec.modifiers().add(classModifier);//类可见性
		
		//将类节点连接为编译单元的子节点
		comp.types().add(classDec);		
		
		/*
		*public class HelloWorld {
		*  public HelloWorld(){
		*    
		*  }
		*}
		*/
		MethodDeclaration methodDec = ast.newMethodDeclaration();
		methodDec.setConstructor(true);//设置为构造函数
		
		SimpleName methodName = ast.newSimpleName("HelloWorld");
		Modifier methodModifier = ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
		Block methodBody = ast.newBlock();

		//设置方法节点
		methodDec.setName(methodName);// 方法名
		methodDec.modifiers().add(methodModifier);//方法可见性
		methodDec.setBody(methodBody); //方法体
		
		//将方法节点连接为类节点的子节点
		classDec.bodyDeclarations().add(methodDec);
		
		/*
		*public class HelloWorld {
		*  public HelloWorld(){
		*    System.out.println("Hello World!");
		*  }
		*}
		*/
		MethodInvocation methodInv = ast.newMethodInvocation();
		
		SimpleName nameSystem = ast.newSimpleName("System");
		SimpleName nameOut = ast.newSimpleName("out");
		SimpleName namePrintln = ast.newSimpleName("println");
		
		//连接‘System’和‘out’
		//System.out
		QualifiedName nameSystemOut = ast.newQualifiedName(nameSystem, nameOut);
		
		//连接‘System.out’和‘println’到MethodInvocation节点
		//System.out.println()
		methodInv.setExpression(nameSystemOut);
		methodInv.setName(namePrintln);
		
		//”Hello World!”
		StringLiteral sHelloworld = ast.newStringLiteral();
		sHelloworld.setEscapedValue("\"Hello World!\"");
		
		//System.out.println(“Hello World!”)
		methodInv.arguments().add(sHelloworld);
		
		//将方法调用节点MethodInvocation连接为表达式语句ExpressionStatement的子节点
		//System.out.println(“Hello World!”);
		ExpressionStatement es = ast.newExpressionStatement(methodInv);
		
		//将表达式语句ExpressionStatement连接为方法节点的点
		methodBody.statements().add(es);
		
		//End
		System.out.println(comp.toString());
	}
}


       一个简单的偷笑通过AST来生成代码的完整实例至此结束,虽说生成的代码很简单,实际上该生成过程对于新手来说还是略有点复杂,需要好好理解和掌握!

       与其临渊羡鱼,不如退而结网,大家不妨动手一试!微笑

 

本文作者:刘伟,刘宏韬  http://blog.csdn.net/lovelion

阅读更多
换一批

没有更多推荐了,返回首页