かずきのBlog
C#やJavaやRubyとメモ書き

目次

Blog 利用状況
  • 投稿数 - 441
  • 記事 - 1
  • コメント - 475
  • トラックバック - 113
ニュース
  • 思い出は、いっぱい。
コメント
プログラマ的自己紹介
  • C#とRubyを趣味で。Javaを仕事で使ってやってます。 WPFをコツコツ勉強中。 IDE大好き。Visual Studio, Eclipse, NetBeansを使用中
お気に入りのツール/IDE
  • Visual Studio 2008 std
  • Eclipse
  • NetBeans6.0以降
  • 自作のツール
プロフィール
  • 大田 一希
  • 1981年1月30日産まれ
  • AB型
  • 左利き
経歴
  • 1993年 海田中学校 入学
  • 1996年 広島県立安芸南高等学校 入学
  • 1999年 某大学 環境情報学科 入学
  • 2003年 某大学 大学院 環境学研究科 入学
  • 2005年 就職して上京
  • 今に至る
アクセサリ
  • あわせて読みたい
  • ログ解析ネット証券

書庫

日記カテゴリ

 

結構前にも同じようなことを題材に書いたような気がするけど、人間とは忘れる生き物です。
もれなく俺も人間なので、きれいさっぱり忘れてしまった。

ということで復習してみよう!

ちなみに、やろうとしてることは、NetBeansのプラグインを作って、そこからプロジェクト内の選択されたJavaのクラスのフィールドやらメソッドやらをとってしまおうという感じのものです。

とりあえず、プロジェクトを作成して、必要そうなライブラリを追加しておく。

image

因みに、参考にしてるのはこっちのページです。
さくっとToolbarあたりにボタンを1つ追加する。

image

アイコンも参考にしてるサイトから虹っぽいやつを拝借させてもらった。
これでクリックしたら何かできるとっかかりができた。ここにいろいろ書いていく。
とりあえず、動作確認のために簡単なのを書いてみた。

    protected void performAction(Node[] activatedNodes) {
        DataObject dataObject = activatedNodes[0].getLookup().lookup(DataObject.class);
        StatusDisplayer.getDefault().setStatusText("こんにちは世界!!");
    }

この状態で実行すると、NetBeansのToolbarに新しいボタンが追加される。
何かファイルとかを選択すると、押せるようになるので、押してみるとステータスバーにこんにちは世界!!と表示される。
よし、とりあえずの動きはよさそうだ。

image

とりあえず、選択されたファイルから、Javaのクラスの情報とるための下準備のコードを書いていく。
まず、DataObjectからFileObjectを取得して、そこからJavaSourceを取得するところまで書いてみる。

JavaSourceが取得できれば、そこから色々なことができるようになって夢広がるっぽい。
早速、さっきの「こんにちは世界!!」を消して下のコードを書いてみたよ!

    protected void performAction(Node[] activatedNodes) {
        DataObject dataObject = activatedNodes[0].getLookup().lookup(DataObject.class);
        
        FileObject selectedFile = dataObject.getPrimaryFile();
        JavaSource selectedJavaSource = JavaSource.forFileObject(selectedFile);
        if (selectedJavaSource != null) {
            StatusDisplayer.getDefault().setStatusText(selectedJavaSource + "が取得できた");
        } else {
            StatusDisplayer.getDefault().setStatusText("Javaのクラスじゃない…");
        }
    }

実行するとこんな感じ。
左は、パッケージを選択した状態でボタンを押したときのメッセージ。右は、Main.javaを選択した状態でボタンを押したときのメッセージ。
image image

ここまでは、順調にできてる!!
さて、ちょいと話が変わるけど、今回ツールバーに追加したボタンは、Javaのファイルを選択したときにしか押してもらいたくない。
んで、さっきのコードの動きを見る限り、Javaのファイル以外のときは、JavaSourceの取得のところでnullになる。

ってことで、enableメソッドをオーバーライドしてちょろっとコードを書く。

    /**
     * 有効・無効の判断をする。JavaSourceが取得できるものは有効です。
     */
    @Override
    protected boolean enable(Node[] arg) {
        if (super.enable(arg)) {
            return getSelectedJavaSource(lookupDataObject(arg)) != null;
        }
        return false;
    }
    /**
     * DataObjectからJavaSourceを取得する。
     */
    private JavaSource getSelectedJavaSource(DataObject dataObject) {
        FileObject selectedFile = dataObject.getPrimaryFile();
        return JavaSource.forFileObject(selectedFile);        
    }
    /**
     * 選択されてるノードから、DataObjectを取得する。
     */
    private DataObject lookupDataObject(Node[] activatedNodes) {
        return activatedNodes[0].getLookup().lookup(DataObject.class);
    }

これで、Javaのファイルを選択したときだけツールバーのボタンが有効になる。
段々、目的の処理を書くところに近づいてる。

JavaSourceについて、何か処理をするときは、runUserActionメソッドを呼び出してやるっぽい。
そこには、Task<CompilationController>を渡して、そのrunメソッドで処理を書く。
というわけで処理の中身を書く一歩手前まで書いてみる。

    protected void performAction(Node[] activatedNodes) {
            DataObject dataObject = lookupDataObject(activatedNodes);
            JavaSource selectedJavaSource = getSelectedJavaSource(dataObject);
        try {
            selectedJavaSource.runUserActionTask(new Task<CompilationController>() {
                public void run(CompilationController arg0) throws Exception {
                    // ここに色々書いていく
                }
            }, true);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

これで準備は整ったはず!ガンガン書いていく!
まず、runメソッドの引数のCompilationControllerをELEMENT_RESOLVEまで持っていってクラス名とかくらいがとれるようにしておく。

クラス名、メソッド名の一覧の取得のためのコードだけど…
結構長いことになった。とりあえずコードを張っておく。

    protected void performAction(Node[] activatedNodes) {
            DataObject dataObject = lookupDataObject(activatedNodes);
            JavaSource selectedJavaSource = getSelectedJavaSource(dataObject);
        try {
            final StringBuilder sb = new StringBuilder();
            selectedJavaSource.runUserActionTask(new Task<CompilationController>() {
                public void run(final CompilationController compilationController) throws Exception {
                    // 要素が見つかるくらいまでは処理してねと
                    compilationController.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
                    new TreePathScanner<Void, Void>(){
                        @Override
                        public Void visitClass(ClassTree tree, Void arg) {
                            TreePath path = getCurrentPath();
                            Element element = compilationController.getTrees().getElement(path);
                            TypeElement type = (TypeElement) element;
                            sb.append(type.getQualifiedName()).append("\n");
                            List<? extends Element> enclosedElements = type.getEnclosedElements();
                            for (Element elm : enclosedElements) {
                                sb.append("\t").append(elm.getKind()).append(": ").append(elm.getSimpleName()).append("\n");
                            }
                            return null;
                        }
                    }.scan(compilationController.getCompilationUnit(), null);
                    
                    InputOutput io = IOProvider.getDefault().getIO("Dump", true);
                    io.getOut().println(sb);
                    io.getOut().close();
                }
            }, true);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

肝になるのは、TreePathScannerのvisitClassをオーバーライドしてるあたり。
このメソッド内にいるってことは、現在解析がクラスの定義まで進んでるってことだから、getCurrentPathで現在位置をとってきてgetElementでクラス定義を表すTypeElementがとってこれる(と信じてる)。

あとは、getEnclosedElementsでElementの中身をStringBuilderに追加していってる。
最後に、Outputウィンドウに結果を出力してる。

適当にフィールドやメソッドを作ったクラスにたいして、このコードを走らせるとOutputウィンドウに下のような感じで表示された。

javaapplication1.Main
        CONSTRUCTOR: <init>
        FIELD: value
        METHOD: getValue
        METHOD: setValue
        METHOD: main


ちゃんととれてるっぽい。
後は、フィールドの型や、メソッドの引数や戻り値がとれれば大体大丈夫かな?
今日は、もう眠いのでここらへんでダウン。

ってか、このエントリーかくのに、のべ4時間くらい使ってそうな予感…
TreePathScannerを使わないで、同じ情報取得する方法がないかな~って探し回ってたのが敗因だった。
だって、TreePathScannerをいちいち継承するのがめんどくさい!でも、今日調べた範囲だと、ほかにいい方法がみつからなかった。

誰か、素敵な方法を知ってたら教えてくれないかなぁ

投稿日時 : 2008年4月28日 0:45
コメント
  • # re: [Java][NetBeans]プロジェクト内のJavaのクラスの情報とってくるよ
    凪瀬
    Posted @ 2008/04/28 10:47
    > TreePathScannerを使わないで、同じ情報取得する方法がないかな~って探し回ってたのが敗因だった。

    確かにVisitorパターンの実装は面倒ですからねぇ。
    プラグイン作るならやっぱりNetBeansの方が楽そうですよね。
  • # re: [Java][NetBeans]プロジェクト内のJavaのクラスの情報とってくるよ
    かずき
    Posted @ 2008/04/28 11:53
    確かに楽っちゃ楽なんですけど、日本語情報がEclipseに比べて、まだ少ないってのがネックになりますね~。
    ここら辺改善しようと、今頑張ってるみたいですけど。
タイトル  
名前  
Url
コメント