jcodegen
1. 개요 ¶jcodegen 은 java 소스코드를 생성하는 java 라이브러리입니다.
2. 만든 이유 ¶예전에 자바 소스코드를 프로그램을 이용해서 만들어야 했던 적이 있습니다. 그래서 관련 라이브러리를 찾아보았는데 저는 eclipse 의 AST 라이브러리를 알아보았습니다. 그때 제가 보았던 문서는
http://help.eclipse.org/help31/topic/org.eclipse.jdt.doc.isv/guide/jdt_api_manip.htm?resultof="ast"
인데, 중간쯤에 보면 Hello World 를 출력하는 소스코드를 만드는 예제가 나옵니다. 저는 그 예제를 보고 "직접 만들어야겠다."는 결심을 했습니다.
eclipse AST 라이브러리는 소스코드의 모든 부분을 객체화해서 처리합니다. 그 결과 이미 있는 소스코드를 분석하여 뭔가를 하는데는 좋지만 백지 상태에서 소스코드를 만들어야 할 경우 코딩량이 상당히 많아 집니다.
jcodegen 의 기본 방침은 적당히 쓰기 편하게 입니다. 블럭레벨(클래스, 메소드 선언, 각종 제어구조 등) 은 객체를 이용하지만 개별문장에는 문자열을 씁니다. 그 결과 jcodegen 은 소스코드를 만드는데는 좋지만 이미 있는 소스코드를 분석하여 뭔가를 하는 것은 거의 불가능합니다.
3. Hello World 예제 ¶
import java.io.IOException;
import net.kldp.jcodegen.Modifier;
import net.kldp.jcodegen.Visibility;
import net.kldp.jcodegen.code.JavaSourceFile;
import net.kldp.jcodegen.code.Statement;
import net.kldp.jcodegen.code.type.ClassType;
import net.kldp.jcodegen.code.type.MethodBlock;
public class CreateHelloWorld {
public static void main(String[] args) throws IOException {
// 소스 파일 생성
JavaSourceFile source = new JavaSourceFile("example") ;
// 클래스 생성
ClassType hello = new ClassType(source , Visibility.PUBLIC , "HelloWorld") ;
hello.addCommentLine("'Hello World' printing example program.") ;
// main 메소드
MethodBlock main = new MethodBlock(hello , Visibility.PUBLIC , "main") ;
main.addModifier(Modifier.STATIC) ;
main.setReturnType(void.class) ;
main.addParam(String[].class, "args", "command line args") ;
// 출력문
new Statement(main , "System.out.println(\"Hello \"+\"World\")") ;
source.write(System.getenv("TEMP")) ;
}
}
위의 코드는 $TEMP/example/HelloWorld.java 소스코드를 만듭니다.
package example ;
/**
* 'Hello World' printing example program.
*/
public class HelloWorld {
/**
*
* @param args command line args
*/
public static void main (String[] args) {
System.out.println("Hello "+"World");
}
}
4.1. 전체적인 구조 ¶java 의 소스코드는 트리구조로 표현됩니다. 따라서 jcodegen 도 내부적으로 트리구조가 되며 jcodegen의 대부분의 구성요소는 생성자의 첫번째 매개변수로 자신의 부모를 받습니다.
4.2. 소스 파일 생성 ¶jcodegen 에서 소스 파일은
net.kldp.jcodegen.code.JavaSourceFile 클래스로 표현됩니다.처음 생성할 때 이 파일이 속하는 패키지명을 매개변수로 받습니다.
소스 파일을 쓰려면
write 메소드를 호출합니다. 이 메소드는 소스 파일을 만들 디렉토리 경로를 받습니다.
4.4.1. if 사용 ¶if 문을 단독으로 사용할 때는
net.kldp.jcodegen.code.IfBlock 를 그냥 쓰면 됩니다.
IfBlock is_0 = new IfBlock(parent , "num == 0") ; new Statement (is_0 , "System.out.println(\"num is 0\")") ;
if(num == 0) {
System.out.println("num is 0") ;
}
만약 else if 나 else 도 필요한 경우
net.kldp.jcodegen.code.IfWrapper 를 만든다음 이 객체에
net.kldp.jcodegen.code.IfBlock 나 net.kldp.jcodegen.code.ElseBlock 를 추가해야 합니다.
String num_name = "num" ;
new Statement(main , "int @[num_name] = 3").setVariable("num_name", num_name) ;
IfWrapper if_list = new IfWrapper(main) ;
for(int i = 0 ; i < 5 ; ++i) {
IfBlock num_check = new IfBlock(if_list , "@{num_name} == @{i}") ;
num_check.setVariable("num_name", num_name) ;
num_check.setVariable("i", i) ;
Statement print = new Statement(num_check , "System.out.println(\"@{i}\")") ;
print.setVariable("i", i) ;
}
ElseBlock else_block = new ElseBlock(if_list) ;
new Statement(else_block , "System.out.println(\"Unknown\")") ;
int num = 3;
if(num == 0) {
System.out.println("0");
}
else if(num == 1) {
System.out.println("1");
}
else if(num == 2) {
System.out.println("2");
}
else if(num == 3) {
System.out.println("3");
}
else if(num == 4) {
System.out.println("4");
}
else {
System.out.println("Unknown");
}
jcodegen 에는 else if 를 나타내는 클래스는 따로 없고 IfBlock 이 추가된 순서에 따라 if 인지 else if 인지가 결정됩니다.
4.4.2. try 사용 ¶try , catch , finally 는 각각
net.kldp.jcodegen.code.TryBlock , net.kldp.jcodegen.code.CatchBlock , net.kldp.jcodegen.code.FinallyBlock 로 나타냅니다. 사용할 때는 먼저 net.kldp.jcodegen.code.TryWrapper 객체를 만든 뒤 여기에 추가해야 합니다.
TryWrapper trys = new TryWrapper(main) ;
TryBlock try_block = new TryBlock(trys) ;
new Statement(try_block , "System.out.println(\"TRY\")") ;
CatchBlock catch_runtime = new CatchBlock(trys , RuntimeException.class , "e") ;
new Statement(catch_runtime , "System.out.println(\"CATCH\")") ;
FinallyBlock final_block = new FinallyBlock(trys) ;
new Statement(final_block , "System.out.println(\"FINALLY\")") ;
try {
System.out.println("TRY");
}
catch (RuntimeException e) {
System.out.println("CATCH");
}
finally {
System.out.println("FINALLY");
}
4.5. import 문 처리하기 ¶jcodegen 은 내부적으로 import 문과 관련된 것을 자동으로 처리합니다.
예를 들어 특정 메소드가
java.util.ArrayList 타입을 매개변수로 받는 경우
method.addParam(java.util.ArrayList.class , "list" , "") ;
이렇게 하면 ArrayList 에 대한 import 문이 적절하게 추가됩니다.
어떤 경우에는 문자열 내에서 타입명을 사용해야 할 경우가 있습니다. 예를 들어
List list = new ArrayList() ;
이런 문장을 만들어야 할 경우가 있습니다. 이런 경우를 위해 jcodegen 은 net.kldp.jcodegen.base.TypeName 클래스를 제공합니다.
TypeName list = TypeName.create(parent , java.util.List.class) ;
TypeName arr_list = TypeName.create(parent , java.util.ArrayList.class) ;
new Statement(parent , list + " list = new " + arr_list + "()") ;
TypeName 객체에 대해 toString() 메소드가 호출되면, 내부적으로 import 에 대한 처리를 한 후
소스코드에서 사용할 수 있는 타입명을 리턴합니다.
TypeName 클래스는 generic type 도 지원합니다.
TypeName list_of_string = TypeName.create(parent , java.util.List.class , java.lang.String.class) ;
이 코드는 소스코드에 List<String> 을 만듭니다.
TypeName 의 팩토리 메소드는 Class 객체나 타입의 문자열이름을 받습니다. generic type 부분에는 다른
TypeName 객체를 받는 것도 가능합니다.
4.6. 변수명 평가 기능 사용 ¶예를 들어
List<String> list = new ArrayList<String>() ;이런 문장을 만드는 경우 단순히 문자열 연결 연산을 이용하면
TypeName list_of_string = TypeName.create(parent , java.util.List.class , java.lang.String.class) ;
TypeName arr_list_of_string = TypeName.create(parent , "java.util.ArrayList" , "java.lang.String") ;
new Statement(parent , list_of_string + " list = new " + arr_list_of_string + "()") ;
이렇게 됩니다. 하지만 이경우 문자열 덧셈 연산 때문에 생성되는 코드가 한눈에 들어오지 않습니다. 이는 생성해야 하는 코드가 복잡해질수록 더 심해질 것입니다. 이런 경우를 위해 변수명 평가 기능을 제공합니다.
문자열 내에 @{} 로 변수가 사용되는 부분을 만든 다음, setVariable 로 해당 변수에 치환되는 객체를 설정하면 됩니다.
TypeName list_of_string = TypeName.create(parent , java.util.List.class , java.lang.String.class) ;
TypeName arr_list_of_string = TypeName.create(parent , "java.util.ArrayList" , "java.lang.String") ;
Statement stmt = (parent , "@{list} = new @{arr_list}()") ;
stmt.setVariable("list" , list_of_string) ;
stmt.setVariable("arr_list" , arr_list_of_string)
setVariable의 키는 올바른 자바 변수명 문자열이면 되고, 값은 임의의 java 객체가 됩니다.
내부적으로 키에 대한 값을 찾아 toString()을 호출한 결과로 치환합니다.
|
You are scrupulously honest, frank, and straightforward. |










