#author("2019-04-06T00:02:39+00:00","","")
#author("2021-09-20T14:25:02+00:00","","")
// 下階層用テンプレート
#topicpath
----
//ここにコンテンツを記述します。
#contents

**ASTParserとは [#e7a29459]
さてさて、いままでEclipseのウィジェットとか、リスナーとか比較的IDEよりでないところを見てきましたが、今回はソースコードを解析するパーサ、ASTParserを見てみたいと思います。

ASTParserはパッケージエクスプローラなどに表示されているJavaソースからソースコードを取得し解析するとか、ユーザ入力値に応じてJavaのソースコードを生成するとか、Javaソースをプログラマブルに操作するときに使用します。


**やってみる [#nd9bad95]
触ってみましょう。ソースコード情報はパッケージエクスプローラから取得するとして、そのクラスを操作するリーダを作ってみました。

 package nu.mine.kino.plugin.astsampless;
 
 import java.util.Iterator;
 
 import org.apache.log4j.Logger;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.jdt.core.ICompilationUnit;
 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.jdt.core.dom.Javadoc;
 import org.eclipse.jdt.core.dom.TagElement;
 
 /**
  * Javaソースを解析するリーダーです。
  * 
  * @author Masatomi KINO
  * @version $Revision$
  */
 public class SourceReader {
 
   private static final Logger logger = Logger.getLogger(SourceReader.class);
 
   private final ICompilationUnit element;
 
   public SourceReader(ICompilationUnit element) {
     this.element = element;
   }
 
   /**
    * 渡されたソースコードの解析を行います。
    */
   public void read() {
     logger.debug("read() - start");
     
     ASTParser parser = ASTParser.newParser(AST.JLS3);
     // parser.setResolveBindings(true);
     parser.setSource(element);
     
     CompilationUnit unit = (CompilationUnit) parser
      .createAST(new NullProgressMonitor());
     
     unit.accept(new ASTVisitorImpl());
     
     logger.debug("ファイル名: " + element.getElementName());// ファイル名
     String sourceName = element.getElementName().substring(0,
      element.getElementName().lastIndexOf("."));
     logger.debug("クラス名: " + sourceName);
     logger.debug("クラスの完全修飾クラス名: "
      + element.getType(sourceName).getFullyQualifiedName());
     logger.debug("パッケージ名: " + element.getParent().getElementName());
     
     logger.debug("read() - end");
   }
 
   /**
    * ソースを走査するVisitorの実装クラスです。
    * 
    * @author Masatomi KINO
    * @version $Revision$
    */
   class ASTVisitorImpl extends ASTVisitor {
     private final Logger logger = Logger.getLogger(ASTVisitorImpl.class);
 
     public boolean visit(Javadoc node) {
       logger.debug("visit(Javadoc) - start");
       logger.debug(node);
       Iterator iterator = node.tags().iterator();
       while (iterator.hasNext()) {
         TagElement element = (TagElement) iterator.next();
         logger.debug("型:" + element.getClass().getName());
         logger.debug("tagname: " + element.getTagName());
         logger.debug(element);
       }
       logger.debug("visit(Javadoc) - end");
       return super.visit(node);
     }
   }
 }

このリーダは、コンストラクタでわたってくるJavaソースモデル(ICompilationUnit)をフィールドに保持しておき、readメソッドでそのフィールドのモデルの解析を行います((コンストラクタで渡される場合ってのはパッケージ・エクスプローラのSelectionなどから渡されるとか。テキスト文字列や、ファイルを直接読み込んで云々とかの場合は、parser.setSource(char[] chars);を使用します。char[]はString#toCharArray()などで生成します。))。

ここで実際に解析を行っているのが、
-org.eclipse.jdt.core.dom.ASTParser;
-org.eclipse.jdt.core.dom.ASTVisitor;

です。ASTParserはJavaのソースを、コード部分やコメント部分、JavaDoc部分や、パッケージやimportなどの要素に分解するパーサです。ASTVisitorインタフェースはいわゆるビジタパタンのビジターインタフェースです。ソースコードをASTParserが解析してくれた結果、ソースは様々な要素に分解されるのですが、このASTVisitorインタフェースはその要素ごとのvisitメソッドがあり、それらを実装することで各要素への処理を行うようになっています。具体的に上のASTVisitorの実装クラスを見てみると
 class ASTVisitorImpl extends ASTVisitor {
   private final Logger logger = Logger.getLogger(ASTVisitorImpl.class);
   public boolean visit(Javadoc node) {
     logger.debug("visit(Javadoc) - start");
     logger.debug(node);
     Iterator iterator = node.tags().iterator();
     while (iterator.hasNext()) {
       TagElement element = (TagElement) iterator.next();
       logger.debug("型:" + element.getClass().getName());
       logger.debug("tagname: " + element.getTagName());
       logger.debug(element);
     }
     logger.debug("visit(Javadoc) - end");
     return super.visit(node);
   }
 }
となっていて、ここではJavaDoc要素に関する処理だけを実装してあります。

***ICompilationUnit(ソースコード)から情報を得る [#g0545711]
また、コンストラクタで渡されるICompilationUnitからも情報を取得することができます。
 logger.debug("ファイル名: " + element.getElementName());// ファイル名
 String sourceName = element.getElementName().substring(0,
 element.getElementName().lastIndexOf("."));
 logger.debug("クラス名: " + sourceName);
 logger.debug("クラスの完全修飾クラス名: "
   + element.getType(sourceName).getFullyQualifiedName());
 logger.debug("パッケージ名: " + element.getParent().getElementName());
あたりですね。elementがICompilationUnitのインスタンスです。実行結果は以下の通り。
 ファイル名: BL.java
 クラス名: BL
 クラスの完全修飾クラス名: nu.mine.kino.BL
 パッケージ名: nu.mine.kino

ただ 完全修飾クラス名 を取得するメソッドは、そのファイルのクラス名を取得しているのではなく((引数にクラス名渡してるくらいだから))、elementのパッケージ名に引数のクラス名を連結してるだけ、、みたいです。

この辺の(ASTParserでなく)ICompilationUnit周りは [[Javaプロジェクトを操作する>Eclipse/プラグイン開発のTIPS集/Javaプロジェクトを操作する#r9ed8ac6]] にまとめました。


**ソースを渡して、解析させてみる [#y654fc7d]
ソースを渡すのは拡張ポイント使ってなんとでもできるんで後回し。以下のサンプルソースをさっきのリーダに渡して解析させてみます。

-サンプル
 package nu.mine.kino;
 
 import java.util.Date;
 import java.util.List;
 
 /**
  * クラスコメントです。
  * クラスのコメントを記述しています。
  * @author m-kino
  * @see java.lang.String
  *
  */
 public class TestMain {
   public static void main(String[] args) {
     Date date = new Date();
     List list = null;
     System.out.println("Main.");
   }
 
   /**
    * helloメソッドのJavaDocコメントです。
    * 改行してみた
    * @return 文字列。
    * @param name 名前。
    */
   public String hello(String name) {
     System.out.println("Hello "+ name +" .");
     return "Hello "+ name +" .";
   }
 
   /*
    * hello2メソッドのJavaDocコメントです。
    * 改行してみた
    * @return 文字列。
    * @param name 名前。
    */
   public String hello2(String name) {
     System.out.println("Hello "+ name +" .");
     return "Hello "+ name +" .";
   }
 }

***実行結果 [#p8538c5a]
実行結果は以下のようになりました。

 [main] DEBUG  - visit(Javadoc) - start
 [main] DEBUG  - /** 
 * クラスコメントです。
 * クラスのコメントを記述しています。
 * @author m-kino
 * @see java.lang.String
 */
 
 [main] DEBUG  - 型:org.eclipse.jdt.core.dom.TagElement
 [main] DEBUG  - tagname: null
 [main] DEBUG  - 
 * クラスコメントです。
 * クラスのコメントを記述しています。
 [main] DEBUG  - 型:org.eclipse.jdt.core.dom.TagElement
 [main] DEBUG  - tagname: @author
 [main] DEBUG  - 
 * @author m-kino
 [main] DEBUG  - 型:org.eclipse.jdt.core.dom.TagElement
 [main] DEBUG  - tagname: @see
 [main] DEBUG  - 
 * @see java.lang.String
 [main] DEBUG  - visit(Javadoc) - end
 [main] DEBUG  - visit(Javadoc) - start
 [main] DEBUG  - /** 
 * helloメソッドのJavaDocコメントです。
 * 改行してみた
 * @return 文字列。
 * @param name 名前。
 */
 
 [main] DEBUG  - 型:org.eclipse.jdt.core.dom.TagElement
 [main] DEBUG  - tagname: null
 [main] DEBUG  - 
 * helloメソッドのJavaDocコメントです。
 * 改行してみた
 [main] DEBUG  - 型:org.eclipse.jdt.core.dom.TagElement
 [main] DEBUG  - tagname: @return
 [main] DEBUG  - 
 * @return 文字列。
 [main] DEBUG  - 型:org.eclipse.jdt.core.dom.TagElement
 [main] DEBUG  - tagname: @param
 [main] DEBUG  - 
 * @param name 名前。
 [main] DEBUG  - visit(Javadoc) - end
 [main] DEBUG nu.mine.kino.plugin.astsampless.SourceReader - execute() - end
 [main] DEBUG nu.mine.kino.plugin.astsampless.SampleAction - ----------
 [main] DEBUG nu.mine.kino.plugin.astsampless.SampleAction - run(IAction) - end

この結果から、以下のようになってることがわかりますね。
-JavaDocひとつが、org.eclipse.jdt.core.dom.Javadoc インスタンスになっている。

-コメント
 /** 
  * クラスコメントです。
  * クラスのコメントを記述しています。
  * @author m-kino
  * @see java.lang.String
  */
は
 * クラスコメントです。
 * クラスのコメントを記述しています。
と
 * @author m-kino
と
 * @see java.lang.String
に分割される。この分割されたオブジェクトはorg.eclipse.jdt.core.dom.Javadoc#tags() で取得可能(org.eclipse.jdt.core.dom.TagElementを格納したListが返る)

**ASTVisitorの他のメソッドもOverrideしてみる [#ac5dfaa4]
ASTVisitorには他のメソッドたとえば
 public boolean visit(ImportDeclaration node);
などOverrideできる物がたくさんあります。これらをOverrideしてどのような情報が取得できるか試してみます。

***ImportDeclaration [#j85c5cae]
インポート文を表現するクラスですね。
  public boolean visit(ImportDeclaration node) {
    logger.debug("ImportDeclaration: " + node.getName());
    return super.visit(node);
  }
の実行結果は以下の通り。
 [main] DEBUG - ImportDeclaration: java.util.Date
 [main] DEBUG - ImportDeclaration: java.util.List
一つのimport文に対して一回 public boolean visit(ImportDeclaration node) が呼ばれるんですね。ちなみに
 logger.debug("ImportDeclaration: " + node);
とするとimport xxxx; のimportまで取得可能です。

***PackageDeclaration [#nd74c8d6]
パッケージを表現するクラスですね。
実行結果は以下の通り。
 [main]  - PackageDeclaration: nu.mine.kino
パッケージ名が取得できました。
ちなみに
 logger.debug("PackageDeclaration: " + node);
とするとpackage nu.mine.kino; のpackageまで取得可能です。


**コンテンツ一覧 [#m1338e28]
#ls2


**関連リンク [#pc0a258d]
-Eclipse/プラグイン開発のTIPS集/Javaプロジェクトを操作する
-[[EclipseのASTParserを試す>http://www-06.ibm.com/jp/developerworks/opensource/050422/j_os-ast.html]]
-[[各種のJavaエレメントを取得する>http://www13.plala.or.jp/observe/JDT/JavaElement.html]]
-[[JavaDocのパースはこちらの方がよいような..>Java/XJavaDoc]]
-[[Abstract Syntax Tree>http://www.eclipse.org/articles/Article-JavaCodeManipulation_AST/index.html]]
-[[org.eclipse.stp.sc.common.utils.JDTUtilsのソースコード>http://www.koders.com/java/fid2B98637163E013FC6A839DBBC715098E53CF7897.aspx]]
-[[/trunk/org.jalcedo.client.ui/src/org/jalcedo/client/ui/popup/actions/WebServiceClients.java - Jalcedo - Trac>http://www.jalcedo.org/browser/trunk/org.jalcedo.client.ui/src/org/jalcedo/client/ui/popup/actions/WebServiceClients.java?rev=190]]
-[[Eclipseプラグイン開発: 型の仲間達(サブクラス,スーパークラス)の取得方法>http://yoichiro.cocolog-nifty.com/eclipse/2004/03/post_5.html]]
-[[Java > Open Source Codes > org > eclipse > jdt > internal > corext > dom > ScopeAnalyzer _ Java API By Example, From Geeks To Geeks.>http://kickjava.com/src/org/eclipse/jdt/internal/corext/dom/ScopeAnalyzer.java.htm]]
-[[EclipseでAPTするには! - okazukiの日記>http://d.hatena.ne.jp/okazuki/20060927/1159337407]]


----
この記事は
#vote(おもしろかった[9],そうでもない[3])
#vote(おもしろかった[10],そうでもない[3])
- すてきな記事。 -- [[いがぴょん]] &new{2019-04-05 (金) 12:40:16};
- ありがとうございます。。記事自体はもう、、数年前に書いたモノですがorz -- [[きの]] &new{2019-04-06 (土) 09:02:39};

#comment
#topicpath


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

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