かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

MimeLookup

ということで次はMimeLookup。
どういうときに使うかというと、ぱっと読んだ雰囲気だと選んでるファイルの種類に応じて何かしら処理をさせたいときとかに使える。
選んでるファイルの種類はFileObjectのgetMIMETypeによって調べれるっぽい。
どんなファイルタイプがあるかというのは、ImportantFiles/Xml Layer//Editorsの下にある。
textフォルダを展開するとcssやhtmlやx-javaなど色々ある。

ここにフォルダやファイルを追加することで、何やらゴニョゴニョできるようになるそうです。
今回の例では、Javaのファイルを選択した状態でメニューを押すと標準出力に対してpublicと書いてある行のみ出力していくというものを作ってみます。

じゃぁ、こんなのに汎用性を持たせるのもどうかと思うけど別タイプのファイルやprivateのみ表示させるのとかにも使えるように軽くインターフェイスを切ります。

package okazuki.mimelookup;

import org.openide.filesystems.FileObject;

/**
 *
 * @author Kazuki
 */
public interface OkazukiParser {

    void parse(FileObject file);

}

FileObjectを受け取ってなんかするってやつです。

じゃぁこれを実装してpublicのある行だけ標準出力に出すものを実装します。

package okazuki.mimelookup;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.openide.filesystems.FileObject;
import org.openide.ErrorManager;
/**
 *
 * @author Kazuki
 */
public class JavaFileParser implements OkazukiParser {
    
    public JavaFileParser() {
    }
    
    public void parse(FileObject file) {
        if (!"text/x-java".equals(file.getMIMEType())) {
            System.out.println("not support: " + file.getMIMEType());
            return;
        }
        
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(file.getInputStream()));
            for (String line = null; (line = br.readLine()) != null;) {
                // publicがあれば出力
                if (line.indexOf("public") != -1) {
                    System.out.println(line);
                }
            }
        } catch (IOException ex) {
            ErrorManager.getDefault().notify(ex);
        } finally {
            try {
                br.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
    
}

これもたいした事は無いと思う。
足りないクラスは適時Librariesに依存関係を追加していってください。

これで、Javaのファイルを食わせれば標準出力に出す機能はできた!!
じゃぁ、これをlayer.xmlに登録します。
中身はこんな感じ。

    <folder name="Editors">
        <folder name="text">
            <folder name="x-java">
                <folder name="OkazukiParser">
                    <file name="okazuki-mimelookup-JavaFileParser.instance"/>
                </folder>
            </folder>
        </folder>
    </folder>

Editors/text/x-javaの下にOkazukiParser(適当でOK)というフォルダを作って、そこにJavaFileParserクラスのインスタンスをあらわすfileを作ります。
これに後1工夫を加えると、Javaファイルを選択してるときにJavaFileParserのインスタンスを取得できるようになります。


お次は、適当に作ったOkazukiParserフォルダをNetBeansに教えてあげないといけない。
そのためには、org.netbeans.spi.editor.mimelookup.Class2LayerFolderを実装したクラスが必要。
このフォルダに、こんなクラスあるよ!っていうのを教えてあげる雰囲気を出してる。

package okazuki.mimelookup;

import org.netbeans.spi.editor.mimelookup.Class2LayerFolder;
import org.netbeans.spi.editor.mimelookup.InstanceProvider;

public class OkazukiFolderMapper implements Class2LayerFolder {

    public OkazukiFolderMapper() {
    }

    public Class getClazz() {
        return OkazukiParser.class;
    }

    public String getLayerFolderName() {
        return "OkazukiParser";
    }

    public InstanceProvider getInstanceProvider() {
        return null;
    }

}

後は、META-INF.servicesの下にorg.netbeans.spi.editor.mimelookup.Class2LayerFolderというファイルを作って中にokazuki.mimelookup.OkazukiFolderMapperを足せばOK。
裏でLookupしてるんだろうなぁコレ。

META-INF/services/org.netbeans.spi.editor.mimelookup.Class2LayerFolderファイル

okazuki.mimelookup.OkazukiFolderMapper

これで下準備完了!!!
適当なメニューを作って下のようなコードを書く!!

package okazuki.mimelookup;

import java.util.Collection;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.openide.filesystems.FileObject;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.actions.CallableSystemAction;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;

public final class ShowPublicAction extends CallableSystemAction {
    
    public void performAction() {
        // 現在選択されてるDataObjectを取得
        DataObject dobj = Utilities.actionsGlobalContext().lookup(DataObject.class);
        if (dobj == null) {
            System.out.println("DataObject is null");
            return;
        }
        // そこからファイルを取得して
        FileObject fo = dobj.getPrimaryFile();
        
        // MIMETypeのLookupを作って
        Lookup lookup = MimeLookup.getLookup(MimePath.get(fo.getMIMEType()));
        // OkazukiParserクラスをlookupして(ここで今までした作業が裏で生きてるんだと思う!)
        Collection<? extends OkazukiParser> parsers = lookup.lookupAll(OkazukiParser.class);
        for (OkazukiParser p : parsers) {
            // パースしてもらう
            p.parse(fo);
        }
    }
    
    public String getName() {
        return NbBundle.getMessage(ShowPublicAction.class, "CTL_ShowPublicAction");
    }
    
    protected void initialize() {
        super.initialize();
        // see org.openide.util.actions.SystemAction.iconResource() javadoc for more details
        putValue("noIconInMenu", Boolean.TRUE);
    }
    
    public HelpCtx getHelpCtx() {
        return HelpCtx.DEFAULT_HELP;
    }
    
    protected boolean asynchronous() {
        return false;
    }
    
}

MimeLookupしてるあたりがポイント。
MimeLookup.getLookupは、NetBeans5.5までは文字列を渡してたみたい。
NetBeans6から、ちゃんとMimeあらわすクラス作ったからそれを使えという方針らしい。


とりあえず、このモジュールを組み込んだNetBeansで適当にJavaアプリケーションを起こしてデフォで作られるMainクラスを選択してメニューをぽちっと。

public class Main {
    public Main() {
    public static void main(String[] args) {

おぉ〜ちゃんと出た!!
2回か3回くらいトライアンドエラー繰り返すつもりだったけどイッパツで出た!!
今日はめでたいぞ。