ICompilationUnitを、変更するコードパターン

Doltengでユーティリティ化している強引なコーディングパターン。
これを見れば、IDocumentとITextFileBufferの関係が見えてくる筈デス。
見えなければ感じて下さい。念を込めて。


ここで、重要なのは、JDTのモデルを触る時には、以下の点について考慮しないといけない事です。

  • 現在エディタで開かれているかどうか
  • ファイルの実体を変更するかどうか。つまり、エディタ上でCtrl+Zで戻れる様にしたいかどうか。
  • IProgressMonitor も忘れずに。要は、進捗報告と、処理のキャンセル可能性を忘れない事。
public class TypeUtil {
    public static void modifyType(final ICompilationUnit unit,
            IProgressMonitor monitor, ModifyTypeHandler handler) {

        IDocument document = null;
        ITextFileBuffer buffer = null;
        try {
            final ASTParser parser = ASTParser.newParser(AST.JLS3);
            parser.setSource(unit);
            ASTNode node = parser.createAST(new SubProgressMonitor(monitor, 1));

            if (unit.getOwner() != null) {
                document = new Document(unit.getBuffer().getContents());
            } else {
                buffer = TextFileBufferUtil.acquire(unit);
                document = buffer.getDocument();
            }

            final ASTRewrite rewrite = ASTRewrite.create(node.getAST());
            final ImportsStructure imports = new ImportsStructure(unit);

            handler.modify(node, rewrite, imports);

            MultiTextEdit edit = imports.getResultingEdits(document, monitor);
            edit.addChild(rewrite.rewriteAST(document, unit.getJavaProject()
                    .getOptions(true)));
            edit.apply(document);
            if (buffer != null) {
                buffer.commit(new SubProgressMonitor(monitor, 1), true);
            }
        } catch (Exception e) {
            DoltengCore.log(e);
            if (buffer != null) {
                TextFileBufferUtil.release(unit);
            }
        }
    }

    public interface ModifyTypeHandler {
        void modify(ASTNode node, ASTRewrite rewrite, ImportsStructure imports);
    }
}
public class TextFileBufferUtil {

    public static ITextFileBuffer acquire(ICompilationUnit cu) {
        return acquire(cu.getResource());
    }

    public static ITextFileBuffer acquire(IResource resource) {
        try {
            if (resource != null && resource.getType() == IResource.FILE) {
                final IPath path = resource.getFullPath();
                FileBuffers.getTextFileBufferManager().connect(path,
                        new NullProgressMonitor());
                return FileBuffers.getTextFileBufferManager()
                        .getTextFileBuffer(path);
            }
        } catch (CoreException e) {
            DoltengCore.log(e);
        }
        return null;
    }

    public static void release(ICompilationUnit cu) {
        release(cu.getResource());
    }

    public static void release(IResource resource) {
        try {
            if (resource != null && resource.getType() == IResource.FILE) {
                FileBuffers.getTextFileBufferManager().disconnect(
                        resource.getFullPath(), new NullProgressMonitor());
            }
        } catch (CoreException e) {
            DoltengCore.log(e);
        }
    }
}

eclipseは、1つの実体であるファイルを、
複数のエディタで開いていたり、
複数のスレッドが同時に変更を行ったりしようとするので、まぁ、何ぞ大変デスナと言う話。
色んな所で開き直って「知らんガナ!」と言ってしまうのも、重要だと思うデス。