Top / Eclipse / プラグイン開発のTIPS集 / org.eclipse.jdt.core.ICompilationUnit

org.eclipse.jdt.core.ICompilationUnit? はパッケージエクスプローラ上のソースコード一つに対応するというべきインタフェースです。このインタフェースをパッケージエクスプローラなどからアクション経由で取得して、ソースコードの解析などを行うことができます。

やってみる

次のサンプルコードは、Eclipseのアクションクラス(ハンドラクラス)経由でパッケージエクスプローラからICompilationUnit?を取得し、そのインタフェースの実装メソッド

IJavaElement[] getChildren() throws JavaModelException;

を使って、コードを解析していきます。

public Object execute(ExecutionEvent event) throws ExecutionException {
  ISelection selection = HandlerUtil.getActiveMenuSelectionChecked(event);
  if (selection instanceof IStructuredSelection) {
    IStructuredSelection sselection = (IStructuredSelection) selection;
    Object firstElement = sselection.getFirstElement();
    if (firstElement instanceof ICompilationUnit) {
      ICompilationUnit unit = (ICompilationUnit) firstElement;
      try {
        if (!unit.isStructureKnown()) {
          return null;
        }
        // まずは子要素を取得。取得されるのは、パッケージ宣言とか、クラス型とか
        // クラスが複数定義されている場合もあるし。
        IJavaElement elements[] = unit.getChildren();
        for (IJavaElement javaElement : elements) {
          // ↓型だったらば、ITypeにキャスト。
          if (javaElement.getElementType() == IJavaElement.TYPE) {
            IType type = (IType) javaElement;
            // メソッド一覧を取得。
            IMethod[] methods = type.getMethods();
            // IMethod method = methods[methods.length - 1];
            for (IMethod method : methods) {
              System.out.println(method.getElementName());
            }
          }
        }
      } catch (JavaModelException e) {
        e.printStackTrace();
      }
    }
  }
  return null;
}
  • Sample.java
    package hoge;
    
    public class Sample {
      public void setHoge(String hoge){
        this.hoge = hoge;
      }
    
      private String hoge;
      
      public String getHoge() {
        return hoge;
      }
    
    }
    こんなコードに対して上のハンドラを実行したところ、
    setHoge
    getHoge
    と表示されました。

TIPS集

配下のIJavaElement?の変数から、ICompilationUnit? を取得する。

ITypeなIJavaElement?の変数とかからそれを包含しているICompilationUnit? を取得する方法です。

ICompilationUnit unit = (ICompilationUnit) element
    .getAncestor(IJavaElement.COMPILATION_UNIT);

ソースコードのIResourceインスタンスを取得する。

JDT周りをやってるとIResouce系を忘れてしまいそうですが、ありました。

// ソースコードに対応するファイルのIResource
IResource resource = cu.getCorrespondingResource();

これでOKですね。

ある要素(フィールドとか、メソッドとか)のオフセット位置を取得する

EclipseのJDTのソース見てると、ファイルの先頭からの文字数をカウントして、その場所にコード挿入するとか、割と地味ーなコードがよくあります。

それを実装する基本として、ソースの先頭からある要素(ここではメソッド)までのオフセット*1位置を取得してみます。解析対象のソースは以下の通りです。

  • Sample.java
    package hoge;
    
    public class Sample {
      public void setHoge(String hoge){ <-このメソッドの「p」の位置は、先頭から38文字目。
        this.hoge = hoge;
      } <-また「p」前の位置からこのカッコ終わりまでの長さは、56文字
    
      private String hoge;
      
      public String getHoge() { <-このメソッドの「p」の位置は、先頭から121文字目。
        return hoge;
      } <-また「p」前の位置からこのカッコ終わりまでの長さは、43文字
    
    }

オフセットを取得するコードは以下の通り。

public Object execute(ExecutionEvent event) throws ExecutionException {
  --- 先と同じなので、省略 ---
  if (firstElement instanceof ICompilationUnit) {
    ICompilationUnit unit = (ICompilationUnit) firstElement;
    try {
      if (!unit.isStructureKnown()) {
        return null;
      }
      IJavaElement elements[] = unit.getChildren();
      for (IJavaElement javaElement : elements) {
        if (javaElement.getElementType() == IJavaElement.TYPE) {
          IType type = (IType) javaElement;
          IMethod[] methods = type.getMethods();
          for (IMethod method : methods) {
            ISourceRange sourceRange = method
                .getSourceRange();
            int offset = sourceRange.getOffset();
            System.out.println(offset);
          }
        }
      }
    } catch (JavaModelException e) {
      e.printStackTrace();
      // TODO: handle exception
    }
  }
  return null;
}

この通り、IMethod method を取得して、そのインスタンスに対し

ISourceRange sourceRange = method.getSourceRange();
int offset = sourceRange.getOffset();

とすることでオフセット位置を取得することができます。ちなみに実行結果は、

38
121

となりました。

またそのメソッドの長さ(オフセット位置から、そのメソッドの終わりまでの文字数)は、

int length = sourceRange.getLength();

とやって取得できます。実行結果は、

56
43

です。これでメソッド終わり場所のオフセット位置なども取得できますね。

Eclipse JDTではどうやってるか

なるほどねーと思って上のやり方を見てたのですが、コメント文とかがある場合は、メソッドの先頭のオフセット位置を取ろうとメソッドを実行すると、コメントの上部分の位置を返してきます。

コメントの下の、ホントにメソッドの開始はどう取るんだろうとEclipse JDTのソースを見てみると、TokenScanner? というInternalなクラスを使って実装がされていました。

private int getMemberStartOffset(IMethod lastMethod, IDocument document)
    throws JavaModelException {
  ISourceRange sourceRange = lastMethod.getSourceRange();
  int memberStartOffset = sourceRange.getOffset();
  // 通常は上のオフセットを返すので問題ないけど、コメントとかがあると、コメントの上の位置を返してくる。
  // ホントにメソッドの直上にする場合は、このようにする必要がある。
  TokenScanner scanner = new TokenScanner(document, lastMethod.getJavaProject());

  try {
    return scanner.getNextStartOffset(memberStartOffset, true); // read
    // to the first real non comment token
  } catch (CoreException e) {
    // ignore
  }
  return memberStartOffset;
}

TokenScanner?ってやつにまかせてるんですね:-)。

ソースコードを開く。

unitがICompilationUnit?だとして

// open the editor, forces the creation of a working copy
IEditorPart editor = JavaUI.openInEditor(unit);

でエディタでソースコードを開くことができます。ICompilationUnit?へのポインタがなかったとしても、

IEditorPart editor = JavaUI.openInEditor(element.getAncestor(IJavaElement.COMPILATION_UNIT));

などでIJavaElement?からICompilationUnit?を取得して開くことができますね。

関連リンク


この記事は

選択肢 投票
おもしろかった 2  
そうでもない 0  

Top / Eclipse / プラグイン開発のTIPS集 / org.eclipse.jdt.core.ICompilationUnit

現在のアクセス:13241


*1 基準点からの距離って訳ががよい?

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2015-12-21 (月) 00:21:45 (3043d)