// 下階層用テンプレート
#topicpath
----
#contents

//ここにコンテンツを記述します。

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

**やってみる [#c600ab08]
次のサンプルコードは、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集 [#d2b6421c]
***配下のIJavaElementの変数から、ICompilationUnit を取得する。 [#o516fba4]
ITypeなIJavaElementの変数とかからそれを包含しているICompilationUnit を取得する方法です。
 ICompilationUnit unit = (ICompilationUnit) element
     .getAncestor(IJavaElement.COMPILATION_UNIT);



***ソースコードのIResourceインスタンスを取得する。 [#b9940d2f]
JDT周りをやってるとIResouce系を忘れてしまいそうですが、ありました。
 // ソースコードに対応するファイルのIResource
 IResource resource = cu.getCorrespondingResource();
これでOKですね。


***ある要素(フィールドとか、メソッドとか)のオフセット位置を取得する [#q4860db4]
EclipseのJDTのソース見てると、ファイルの先頭からの文字数をカウントして、その場所にコード挿入するとか、割と地味ーなコードがよくあります。

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

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

コメントの下の、ホントにメソッドの開始はどう取るんだろうと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ってやつにまかせてるんですね:-)。


***ソースコードを開く。 [#s8bdedd8]
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を取得して開くことができますね。



**関連リンク [#w3f6351b]
-Eclipse/プラグイン開発のTIPS集/Javaプロジェクトを操作する 後半でちょっと出てきます。主はIJavaElementの話ですね。
-Eclipse/プラグイン開発のTIPS集/ソースコードを解析するパーサASTParser おもにASTParserとVisitorの話ですが、ICompilationUnit を起点に話が始まります。


----
この記事は
#vote(おもしろかった[1],そうでもない[0])
#vote(おもしろかった[2],そうでもない[0])

#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS