さてさて、いままでEclipseのウィジェットとか、リスナーとか比較的IDEよりでないところを見てきましたが、今回はソースコードを解析するパーサ、ASTParserを見てみたいと思います。
ASTParserはパッケージエクスプローラなどに表示されているJavaソースからソースコードを取得し解析するとか、ユーザ入力値に応じてJavaのソースコードを生成するとか、Javaソースをプログラマブルに操作するときに使用します。
触ってみましょう。ソースコード情報はパッケージエクスプローラから取得するとして、そのクラスを操作するリーダを作ってみました。
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("execute() - start"); ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(element); CompilationUnit unit = (CompilationUnit) parser .createAST(new NullProgressMonitor()); unit.accept(new ASTVisitorImpl()); logger.debug("execute() - 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メソッドでそのフィールドのモデルの解析を行います。
ここで実際に解析を行っているのが、
です。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?要素に関する処理だけを実装してあります。
ソースを渡すのは拡張ポイント使ってなんとでもできるんで後回し。以下のサンプルソースをさっきのリーダに渡して解析させてみます。
package nu.mine.kino; /** * クラスコメントです。 * クラスのコメントを記述しています。 * @author m-kino * @see java.lang.String * */ public class TestMain { public static void main(String[] args) { 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 +" ."; } }
実行結果は以下のようになりました。
[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
この結果から、以下のようになってることがわかりますね。
/** * クラスコメントです。 * クラスのコメントを記述しています。 * @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が返る)
この記事は
現在のアクセス:45825