· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
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. 사용법

여기에 나온 사용법은 0.2 버전 기준입니다. 0.1.1 과는 많이 다릅니다. 0.2 버전은 현재 소스코드 저장소에서 직접 소스를 다운받으면 됩니다.

4.1. 전체적인 구조

java 의 소스코드는 트리구조로 표현됩니다. 따라서 jcodegen 도 내부적으로 트리구조가 되며 jcodegen의 대부분의 구성요소는 생성자의 첫번째 매개변수로 자신의 부모를 받습니다.

4.2. 소스 파일 생성

jcodegen 에서 소스 파일은 net.kldp.jcodegen.code.JavaSourceFile 클래스로 표현됩니다.처음 생성할 때 이 파일이 속하는 패키지명을 매개변수로 받습니다.

소스 파일을 쓰려면 write 메소드를 호출합니다. 이 메소드는 소스 파일을 만들 디렉토리 경로를 받습니다.

4.3. 클래스, 메소드, 필드 생성

클래스, 메소드, 필드 정의를 만들 때는 net.kldp.jcodegen.code.type 패키지에 있는 클래스를 이용하면 됩니다.

4.4. 제어 구문

if , for 등의 제어구문과 관련된 것은 net.kldp.jcodegen.code 패키지에 있습니다.

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.IfBlocknet.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()을 호출한 결과로 치환합니다.


5. 진행상황

2007/04/05 0.2 버전 릴리즈


ID
Password
Join
The person you rejected yesterday could make you happy, if you say yes.


sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2007-09-07 21:21:25
Processing time 0.0070 sec