Filer — 파일 생성용 인터페이스
Filer — 파일 생성용 인터페이스
Filer는 어노테이션 프로세서가 새 파일을 만들 수 있게 지원하는 인터페이스예요. 이렇게 만들어진 파일은 이 인터페이스를 구현하는 어노테이션 처리 도구에 알려져서, 그 도구가 파일을 더 잘 관리할 수 있게 해줘요.
본문
public interface Filer
이렇게 만들어진 소스·클래스 파일은, 파일 내용을 쓰는 데 사용된 Writer나 OutputStream에 close 메서드가 호출된 후 다음 처리 라운드에서 도구가 처리를 위해 고려해요. 세 종류의 파일이 구분돼요: 소스 파일, 클래스 파일, 보조 리소스 파일.
새로 만들어진 파일이 배치되는 두 개의 구분된 지원 위치(논리 파일 시스템 내 하위 트리)가 있어요: 하나는 새 소스 파일용, 하나는 새 클래스 파일용이에요. (이들은 도구의 명령줄에서 -s와 -d 같은 플래그로 지정될 수 있어요.) 새 소스 파일과 새 클래스 파일의 실제 위치는 도구를 특정 실행할 때 구분되지 않을 수도 있어요. 리소스 파일은 어느 위치에서든 만들 수 있어요.
리소스를 읽고 쓰는 메서드는 상대 이름(relative name) 인자를 받아요. 상대 이름은 '/'로 구분되는 경로 세그먼트의 non-null, non-empty 시퀀스이고, '.'와 '..'는 유효하지 않은 경로 세그먼트예요. 유효한 상대 이름은 RFC 3986 3.3절의 "path-rootless" 규칙과 일치해야 해요.
originating elements
파일 생성 메서드는 가변 인자를 받아서, 발생(originating) 요소들을 도구 인프라에 힌트로 제공해 의존성을 더 잘 관리하게 해줘요. 발생 요소는 어노테이션 프로세서가 새 파일을 만들려고 하게 만든 클래스·인터페이스·패키지(package-info 파일)·모듈(module-info 파일)이에요. 즉 발생 요소는 메서드·필드 선언 같은 미세한 단위가 아니라 컴파일 단위(JLS 7.3절)의 세분성을 가지려는 의도예요. 예를 들어 어노테이션 프로세서가 @Generate public class UserSource {}를 처리할 때 소스 파일 GeneratedFromUserSource를 만들려 한다면, UserSource의 타입 요소를 생성 메서드 호출의 일부로 전달해야 해요.
filer.createSourceFile("GeneratedFromUserSource", eltUtils.getTypeElement("UserSource"));
발생 요소가 없으면 전달할 필요가 없어요. 이 정보는 증분(incremental) 환경에서 프로세서를 다시 실행하거나 생성된 파일을 제거할 필요가 있는지 결정하는 데 사용될 수 있어요. 비증분 환경은 발생 요소 정보를 무시할 수 있어요.
파일 생성 규칙
어노테이션 처리 도구의 각 실행 중에는 주어진 경로 이름의 파일이 한 번만 생성될 수 있어요. 첫 시도 전에 그 파일이 이미 존재하면 이전 내용은 삭제돼요. 실행 중에 같은 파일을 만들려는 이후 시도는 FilerException을 던져요. 같은 타입 이름이나 같은 패키지 이름에 대해 클래스 파일과 소스 파일을 모두 만들려고 해도 마찬가지예요. 도구의 초기 입력은 0번째 라운드에 만들어진 것으로 간주하므로, 그 입력 중 하나에 해당하는 소스·클래스 파일을 만들려고 하면 FilerException이 발생해요. 일반적으로 프로세서는 어떤 프로세서가 만들지 않은 기존 파일을 고의로 덮어쓰려 해서는 안 돼요. Filer는 java.lang.Object 같은 기존 클래스·인터페이스에 해당하는 파일을 열려는 시도를 거부할 수 있어요.
메서드
JavaFileObject createSourceFile(CharSequence name, Element... originatingElements) throws IOException— 새 소스 파일을 만들고 쓰기 위한 객체를 반환해요. 클래스·인터페이스·패키지의 소스 파일을 만들 수 있어요. 파일 이름과 경로(소스 파일용 루트 출력 위치 기준)는 그 파일에 선언될 항목의 이름과 지정된 모듈에 기반해요. 명명된 패키지의 소스 파일을 만들려면 이름 인자를 패키지 이름 뒤에".package-info"를 붙인 것으로, 명명되지 않은 패키지면"package-info"로 해요. 선택적 모듈 이름은 타입 이름·패키지 이름 앞에 붙고'/'로 구분돼요 (예: 모듈foo의 클래스a.B에 대한 소스 파일은 이름 인자"foo/a.B"). 명시적 모듈 접두사가 주어지지 않고 환경이 모듈을 지원하면 적절한 모듈이 추론되고, 추론할 수 없으면FilerException이 발생해요.JavaFileObject createClassFile(CharSequence name, Element... originatingElements) throws IOException— 새 클래스 파일을 만들고 쓰기 위한 객체를 반환해요.FileObject createResource(JavaFileManager.Location location, CharSequence moduleAndPkg, CharSequence relativeName, Element... originatingElements) throws IOException— 쓰기 위한 새 보조 리소스 파일을 만들고 그 파일 객체를 반환해요.FileObject getResource(JavaFileManager.Location location, CharSequence moduleAndPkg, CharSequence relativeName) throws IOException— 기존 리소스를 읽기 위한 객체를 반환해요.
API 참고
파일을 덮어쓰는 효과 중 일부는 데코레이터 스타일 패턴을 사용해 얻을 수 있어요. 클래스를 직접 수정하는 대신, 그 클래스의 슈퍼클래스가 어노테이션 처리로 생성되거나 클래스의 하위 클래스가 어노테이션 처리로 생성되도록 설계해요. 하위 클래스가 생성되는 경우, 부모 클래스는 공개 생성자 대신 팩토리를 사용하도록 설계해서 클라이언트에게 하위 클래스 인스턴스만 제시되게 할 수 있어요.