« 2010年12月 | メイン | 2011年02月 »

2011年01月 アーカイブ

2011年01月04日

Java Scripting APIからAppleScriptを使う

「Java Scripting API」は、JavaScriptやRuby等の動的言語をJavaから呼び出すためのAPI。
以下のURLが詳しい。
日本語のドキュメントだと「JavaスクリプトAPI」と訳されててJavaScriptと混乱してややこしいのはなんとかならないのか...。
JavaScriptとAppleScriptを、Java Scripting APIを使って実行する場合の違いを調べたのが下のコード(Mac OS X 10.5 & JavaSE-1.6で確認)。
コードを見てもらえばわかるが、AppleScript用のスクリプトエンジンは、グローバル変数やグローバル関数の状態を維持しない。
(各スクリプト言語向けのスクリプトエンジンの多くは状態を維持するが、Java Scripting APIは状態の維持を必須としていないらしい。)
また、Invocableインターフェイスを実装していない。
つまり、AppleScriptをJava Scripting API経由で利用する場合は、基本的にScriptEngine.evalの引数内に一度に全ての処理を記述するのが良さそう。
AppleScriptから一時ファイルにデータを書き出したり、他のアプリケーションに何らかの状態を出力する場合は、処理を複数に分けることができるが、あまりお手軽ではないと思う。
特に、エラー処理が面倒くさそう。
まあ、処理を全て一度に実行させるのか分割して実行させるかは、用途次第と言ったところか...。
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.Invocable;
import javax.script.ScriptException;

public class ScriptSample {

  public static void main(String[] args) {
    new ScriptSample();
  }

  public ScriptSample() {
    try {
      trialEvalByJavaScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      trialEvalByAppleScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      trialEvalAndEvalByJavaScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      trialEvalAndEvalByAppleScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      trialPutAndEvalByJavaScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      trialPutAndEvalByAppleScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      trialInvocableByJavaScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      trialInvocableByAppleScript();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  void trialEvalByJavaScript() throws ScriptException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    String str=(String)engine.eval(
        "var str = \"javascript/eval\";\n"+
        "str;\n"
    );
    System.out.println(str); //=> "javascript/eval"
  }

  void trialEvalByAppleScript() throws ScriptException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AppleScript");
    String str=(String)engine.eval(
        "set str to \"applescript/eval\"\n"+
        "get str\n"
    );
    System.out.println(str); //=> "applescript/eval"
  }

  void trialEvalAndEvalByJavaScript() throws ScriptException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    engine.eval(
        "var str = \"javascript/eval_and_eval\";\n"
    );
    String str=(String)engine.eval(
        "str;\n"
    );
    System.out.println(str); //=> "javascript/eval_and_eval"
  }

  void trialEvalAndEvalByAppleScript() throws ScriptException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AppleScript");
    engine.eval(
        "set str to \"applescript/eval_and_eval\"\n"
    );
    String str=(String)engine.eval(
        "get str\n"
    ); //=> javax.script.ScriptException "str変数は定義されていません。"
    System.out.println(str);
  }

  void trialPutAndEvalByJavaScript()
      throws ScriptException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    ScriptResult result = new ScriptResult();
    engine.put("obj",result);
    engine.eval(
        "obj.setValue(\"javascript/put_and_eval\");"
    );
    System.out.println(result.getValue()); //=> "javascript/put_and_eval"
  }

  void trialPutAndEvalByAppleScript()
      throws ScriptException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AppleScript");
    ScriptResult result = new ScriptResult();
    engine.put("obj",result);
    engine.eval(
        "tell obj\n"+
        "setValue(\"applescript/put_and_eval\")\n" +
        "end tell\n"
    ); //=> javax.script.ScriptException "obj変数は定義されていません。"
    System.out.println(result.getValue());
  }

  void trialInvocableByJavaScript()
      throws ScriptException,NoSuchMethodException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    ScriptResult result = new ScriptResult();
    engine.put("obj", result);
    engine.eval(
        "function f(val){\n"+
        "obj.setValue(val);\n"+
        "}\n");
    Invocable invocable = (Invocable) engine;
    invocable.invokeFunction(
        "f",
        "javascript/invocable"
    );
    System.out.println(result.getValue()); //=> javascript/invocable
  }

  void trialInvocableByAppleScript()
      throws ScriptException, NoSuchMethodException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AppleScript");
    ScriptResult result = new ScriptResult();
    engine.put("obj",result);
    engine.eval(
        "on f(val)\n"+
        "tell obj\n"+
        "setValue(val)\n"+
        "end tell\n"+
        "end f\n"
    );
    Invocable invocable = (Invocable) engine; //=> java.lang.ClassCastException "apple.applescript.AppleScriptEngine cannot be cast to javax.script.Invocable"
    invocable.invokeFunction(
        "f",
        "applescript/invocable"
    );
    System.out.println(result.getValue());
  }

  class ScriptResult {
    private String value = "initial value";
    public void setValue(String value) {
      this.value = value;
    }
    public String getValue() {
      return value;
    }
  }

}

2011年01月19日

Rubyはトップレベルで宣言したメソッドがインスタンスメソッドになる(追記:と思ったのは気のせいかも...。)

2011/01/19 追記
トップレベルで宣言されたメソッドは例外的にprivateになるようです。
このエントリーのコードは、irb上で実行して確認したんですが、コードをファイルに保存して実行すると、確かにプライベートメソッドになっていて、NoMethodErrorが発生しました。
著者もirbで確認したってことでしょうか。
追記ここまで
「メタプログラミングRuby」を読んでいたら次のような記述があった。
トップレベルにメソッドを定義すると、メソッドがObjectのインスタンスメソッドになるのはこのためだ。
で、試しに書いてみたコードがこれ。
class C
  def m
    f
  end
  def to_s
    "output"
  end
end

o = C.new

def f
  to_s+"!!!"
end

"hello".f # => "hello!!!"
123.f     # => "123!!!"
o.m       # => "output!!!"
マジすか...。
Google

タグ クラウド