LoginSignup
4
5

More than 5 years have passed since last update.

【Eclipse】エディタで開いているJavaソースコードをプログラマブルに書き換えるメソッド

Last updated at Posted at 2016-06-11

メソッドの実装

まずはじめに、プラグインプロジェクトを作成する。次に、必須プラグインに以下のプラグインを追加する。

プラグイン 使用するクラス
org.eclipse.core.resources IFile
org.eclipse.jdt.core ICompilationUnit JavaCore AST ASTParser ASTVisitor CompilationUnit
org.eclipse.jface.text BadLocationException IDocument MalformedTreeException TextEdit
org.eclipse.ui.editors ITextEditor
org.eclipse.ui.ide IFileEditorInput

最後に、以下のインポート宣言とメソッド定義を適切な場所に記述する。

import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.texteditor.ITextEditor;
public void manipulateJavaSource(ExecutionEvent event, ASTVisitor visitor) {

    ITextEditor editor = (ITextEditor)HandlerUtil.getActiveEditor(event);
    IFileEditorInput input = (IFileEditorInput)editor.getEditorInput();
    IDocument document = editor.getDocumentProvider().getDocument(input);
    IFile file = input.getFile();
    ICompilationUnit source = JavaCore.createCompilationUnitFrom(file);

    ASTParser parser = ASTParser.newParser(AST.JLS8);
    parser.setSource(source);
    CompilationUnit root = (CompilationUnit)parser.createAST(null);

    root.recordModifications();
    root.accept(visitor);

    TextEdit textEdit = root.rewrite(document, null);
    try {
        textEdit.apply(document);
    } catch (BadLocationException | MalformedTreeException e) {
        throw new RuntimeException(e);
    }
}

メソッドの概要

  • エディタがテキストエディタでない場合は、例外が発生して異常終了する。
  • エディタの編集対象がJavaファイルでない場合は、例外が発生して異常終了する。

使用例:ゲッターとセッターの生成

変更前
public class Student {
    private int id;
    private String name;
    private int age;
}
変更後
public class Student {
    private int id;
    private String name;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
実装部(インポート宣言)
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jdt.core.dom.*;
実装部(イベントハンドラ)
public Object execute(ExecutionEvent event) throws ExecutionException {
    manipulateJavaSource(event, new CreatePropertyVisitor());
    return null;
}
実装部(Visitor定義)
private class CreatePropertyVisitor extends ASTVisitor {
    @Override
    public boolean visit(FieldDeclaration node) {
        TypeDeclaration parent = (TypeDeclaration)node.getParent();
        AST ast = parent.getAST();

        VariableDeclarationFragment var = (VariableDeclarationFragment)node.fragments().get(0);
        String name = var.getName().getIdentifier();

        // public Type getProperty() {
        //     return property;
        // }
        MethodDeclaration getter = ast.newMethodDeclaration();
        {
            // public
            getter.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD));

            // Type
            getter.setReturnType2((Type)ASTNode.copySubtree(ast, node.getType()));

            // getProperty
            getter.setName(ast.newSimpleName("get" + StringUtils.capitalize(name)));

            // {
            Block block = ast.newBlock();
            {
                // return property;
                ReturnStatement returnStatement = ast.newReturnStatement();
                {
                    returnStatement.setExpression(ast.newSimpleName(name));
                }
                block.statements().add(returnStatement);
            }
            getter.setBody(block);
            // }
        }

        // public void setProperty(Type property) {
        //     this.property = property;
        // }
        MethodDeclaration setter = ast.newMethodDeclaration();
        {
            // public
            setter.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD));

            // setProperty
            setter.setName(ast.newSimpleName("set" + StringUtils.capitalize(name)));

            // (Type property)
            SingleVariableDeclaration singleVariable =  ast.newSingleVariableDeclaration();
            singleVariable.setType((Type)ASTNode.copySubtree(ast, node.getType()));
            singleVariable.setName(ast.newSimpleName(name));
            setter.parameters().add(singleVariable);

            // {
            Block block = ast.newBlock();
            {
                // this.property = property;
                Assignment assignment = ast.newAssignment();
                {
                    // this.field
                    FieldAccess fieldAccess = ast.newFieldAccess();
                    fieldAccess.setExpression(ast.newThisExpression());
                    fieldAccess.setName(ast.newSimpleName(name));
                    assignment.setLeftHandSide(fieldAccess);

                    // =
                    assignment.setOperator(Assignment.Operator.ASSIGN);

                    // field
                    assignment.setRightHandSide(ast.newSimpleName(name));
                }
                block.statements().add(ast.newExpressionStatement(assignment));
            }
            setter.setBody(block);
            // }
        }

        parent.bodyDeclarations().add(getter);
        parent.bodyDeclarations().add(setter);

        return super.visit(node);
    }
}
  • singleVariable.setType(node.getType())はエラーになる。これは、node.getType()によって得られるTypeオブジェクトはnodeオブジェクトの子ノードであり、ノードは複数の親を持つことができないため。singleVariable.setType()によって設定できるのは、親を持たないノードのみ。ASTNode.copySubtree()によって複製されたノードは親を持たないため、singleVariable.setType()に渡してもエラーにならない。
  • visit()のブロック内でFieldDeclarationオブジェクトを追加した場合は無限ループとなる。
  • AST Viewを使うことで、Javaソースコードの抽象構文木を視覚的に捉えることができる。更に各ノードのクラスの名前がわかるため、Visitorを作るときには必須のプラグインといってよい。
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5