ASTParserとは †さてさて、いままで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("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メソッドでそのフィールドのモデルの解析を行います*1。 ここで実際に解析を行っているのが、
です。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?(ソースコード)から情報を得る †また、コンストラクタで渡される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 ただ 完全修飾クラス名 を取得するメソッドは、そのファイルのクラス名を取得しているのではなく*2、elementのパッケージ名に引数のクラス名を連結してるだけ、、みたいです。 この辺の(ASTParserでなく)ICompilationUnit?周りは Javaプロジェクトを操作する にまとめました。 ソースを渡して、解析させてみる †ソースを渡すのは拡張ポイント使ってなんとでもできるんで後回し。以下のサンプルソースをさっきのリーダに渡して解析させてみます。
実行結果 †実行結果は以下のようになりました。 [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 この結果から、以下のようになってることがわかりますね。
ASTVisitorの他のメソッドもOverrideしてみる †ASTVisitorには他のメソッドたとえば public boolean visit(ImportDeclaration node); などOverrideできる物がたくさんあります。これらをOverrideしてどのような情報が取得できるか試してみます。 ImportDeclaration? †インポート文を表現するクラスですね。 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? †パッケージを表現するクラスですね。 実行結果は以下の通り。 [main] - PackageDeclaration: nu.mine.kino パッケージ名が取得できました。 ちなみに logger.debug("PackageDeclaration: " + node); とするとpackage nu.mine.kino; のpackageまで取得可能です。 コンテンツ一覧 †
関連リンク †
この記事は
現在のアクセス:45820 |