2008-08-31

イベントアダプタをJava Scripting EngineのJavaScriptで真似する

Java1.6付属のRhinoでJavaインタフェースを実装する場合、以下のように書ける(jrunscriptで実行)。
js> var runner = new java.lang.Runnable() { run: function() { print(this); }};
js> new java.lang.Thread(runner).start();
js> [object Object]
問題は、GUIのイベントリスナを書こうとした場合、使いもしないメソッドまで記述することになるので面倒くさい。
Javaで実装する場合はアダプタクラスがあるのだけれど、アダプタクラスはインタフェースではなく抽象クラスなので、下記のようなスクリプトは通らない。
// 変数frameにはjava.awt.Frameが入っているものとする
frame.addWindowListener(new java.awt.event.WindowAdapter() {
    windowClosing: function(evt) {
        java.lang.System.exit(0);
    }
});
// 上記構文はインタフェース用なので、抽象クラスに適用できない
以下のスクリプトは一応通るものの、実装しないでおいたイベントハンドラが後で呼ばれると、java.lang.NoSuchMethodExceptionが発生する。
frame.addWindowListener(new java.awt.event.WindowListener() {
    windowClosing: function(evt) {
        java.lang.System.exit(0);
    }
});
// この後、frameを表示するとwindowActivatedやwindowOpenedが未実装なので例外が発生する
そこで、必要なイベントハンドラの連想配列を渡すとWindowListener(を実装したクラスのインスタンス)を作成して返す関数を書いてみた。イベントハンドラごとにベタベタ似たような処理を書いている箇所がもっとスマートにならないものか...。
// 渡された連想配列からWindowListenerを作成する関数
function createWindowListener(tbl) {
    function ignoreEvent(evt) {}
    function getValue(value, defaultValue) { return value ? value : defaultValue; }
    return new java.awt.event.WindowListener() {
        windowActivated: getValue(tbl['windowActivated'], ignoreEvent),
        windowClosed: getValue(tbl['windowClosed'], ignoreEvent),
        windowClosing: getValue(tbl['windowClosing'], ignoreEvent),
        windowDeactivated: getValue(tbl['windowDeactivated'], ignoreEvent),
        windowDeiconified: getValue(tbl['windowDeiconified'], ignoreEvent),
        windowGainedFocus: getValue(tbl['windowGainedFocus'], ignoreEvent),
        windowIconified: getValue(tbl['windowIconified'], ignoreEvent),
        windowLostFocus: getValue(tbl['windowLostFocus'], ignoreEvent),
        windowOpened: getValue(tbl['windowOpened'], ignoreEvent),
        windowStateChanged: getValue(tbl['windowStateChanged'], ignoreEvent)
    };
}

// 使ってみる(ウィンドウが表示されて、閉じるボタンを押すと終了する)
// jrunscriptから実行する場合は、load関数で読み込ませないと一瞬で終了する
var frame = new java.awt.Frame("Test Frame");
var panel = new java.awt.Panel();
panel.setPreferredSize(new java.awt.Dimension(320, 240));
frame.add(panel);
var handlers = { windowClosing: function(evt) {
    var window = evt.getSource();
    window.setVisible(false);
    window.dispose();
    //java.lang.System.exit(0); 最近のJVMでは不要になったらしい
} };
frame.addWindowListener(createWindowListener(handlers));
frame.pack();
frame.setVisible(true);

0 件のコメント:

コメントを投稿