ラベル Java scripting engine の投稿を表示しています。 すべての投稿を表示
ラベル Java scripting engine の投稿を表示しています。 すべての投稿を表示

2015-11-23

Java8のjrunscriptでshowdown.js 1.3.0を使ってみる

過去に「Java6のjrunscriptでshowdown.jsを使ってみる」で書いたことをJava8で試してみる。

まず、input.txtに以下のように書いておく。

Markdown *rocks*.

showdown.js 1.3.0のzipをダウンロードして展開し、コマンドプロンプト(Windowsの場合)でshowdown.jsのあるディレクトリに移動した後、以下の1行を入力する。ちなみに、showdown.jsはconsole.logメソッドを使用するがNashornは提供していないので、最初の-eオプションで代わりのconsoleオブジェクトを作成する必要がある。

jrunscript -e "var console={log:function(s){print(s);}};" -f showdown.js -e "print('\n'+new showdown.Converter().makeHtml(read(new java.lang.StringBuilder(),true)));" < input.txt

すると以下のHTMLが出力される。1行目はread関数の第1引数で指定したプロンプト(空の行)、2行目からがshowdown.jsの出力。

(空の行)
<p>Markdown <em>rocks</em>.</p>

ちなみに、上記においてread関数の第1引数にnew StringBuilder()を渡しているのは、read関数が入力ファイルの行数と同じ数だけプロンプトを出力するのを見せないための醜いやり方なので、read関数ではなく入力ファイルの内容をStringに変える短い関数を自作した方が良いと思う。

--
showdown.jsは、以下のGitHubからダウンロード可能:
showdownjs/showdown · GitHub
https://github.com/showdownjs/showdown

Javaでshowdown.jsを使う方法は、以下の記事を参考にさせていただきました:
Markdown記法をJavaで扱いたい...Scripting for the Java と showdownを組み合わせる - Object Design
http://osima.jp/blog/showdown-on-java.html

2012-09-29

Java1.7のRhinoスクリプトエンジンはRhino1.7相当の機能を使用できる

jrunscriptに-qオプションを付けてRhinoのバージョンを確認したところ、 Java1.6の時点ではRhino1.6だったが、Java1.7からRhino1.7と表示されるようになった。
これはJava1.7付属のRhinoでようやくletを使えるということなので、 varで宣言した変数のスコープに悩まされてきた身としては大変ありがたい。
特にfor文でループ変数のスコープを最小限にするために、 いちいち (function(){})() で囲ってやるのは手間だった。 Rhino1.7相当なら、最初から for (let i = 0; i < count; i++) と書くことができる。

1. 実行するスクリプト(let.js):
var stdout = java.lang.System.out;

{
    let n = 2;
    for (let i = 0; i < n; i++) {
        stdout.print(" " + i);
    }
    stdout.println();
    stdout.println("i is " + typeof i);
}
stdout.println("n is " + typeof n);

2. 実行する:
>jrunscript let.js
 0 1
i is undefined
n is undefined

2012-09-17

最近のRhinoはjava.lang.System.inと書ける

Rhino1.7R4をダウンロードしてみたところ、 "in"というプロパティ名をドット記法で書けるようになっていた。
Rhino1.6R7やjrunscript(Java1.7)で同じ書き方をするとエラーになるので、 もし複数の環境に対応したスクリプトを書こうとしている場合は注意が必要だ。

1. 実行するスクリプト(system-in.js):
print(java.lang.System["in"]);
print(java.lang.System.in);

2-1. Rhino1.7R4で実行すると、プロパティ名がinでもエラーにならない:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main -w -debug system-in.js
java.io.BufferedInputStream@110b205
java.io.BufferedInputStream@110b205

2-2. Rhino1.6R7で実行した場合、プロパティ名inをドット記法で書いている箇所がエラーになる:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main -w -debug system-in.js
js: "system-in.js", line 2: missing name after . operator
js: print(java.lang.System.in);
js: .........................^
js: "system-in.js", line 1: Compilation produced 1 syntax errors.

2-3. jrunscript(Java1.7)で試した場合、Rhino1.6R7と同様のエラーが発生する:
>jrunscript system-in.js
script error in file system-in.js : sun.org.mozilla.javascript.internal.EvaluatorException: missing name after . operator (system-in.js#2) in system-in.js at line number 2

2011-10-08

Java6のjrunscriptでshowdown.jsを使ってみる

MarkdownをJavaから使う方法を調べてみたら、showdown.jsをJava 6付属のRhinoで実行できるようなので、試してみた。

例えばC:\input.txtに以下のように書いておく。

Markdown *rocks*.

showdownのアーカイブをダウンロードして、コマンドプロンプト(Windowsの場合)でshowdown.jsのあるディレクトリに移動した後、以下の1行をコマンド入力する。

jrunscript -f showdown.js -e "print('\n'+new Showdown.converter().makeHtml(read('',true)));" < C:\input.txt

すると以下のHTMLが出力される。(1行目の>>はjrunscriptのプロンプト。2行目からがshowdown.jsの出力)

>>
<p>Markdown <em>rocks</em>.</p>

ちなみに、上記のやり方だと、入力するテキストの行数だけjrunscriptのプロンプトが1行目に出力されて何となく目に付く。標準入力(< C:\input.txt)ではなく普通に入力ファイル名と出力ファイル名を指定するやり方に変えれば解消するはず。

--
showdown.jsは、以下のGitHubからダウンロード可能:
coreyti-showdown - GitHub
https://github.com/coreyti/showdown

showdown.jsがサポートするMarkdown文法についての短く分かりやすい説明:
Showdown - Markdown in Javascript
http://pamgau.net/showdown/

Javaでshowdown.jsを使う方法は、以下の記事を参考にさせていただきました:
Markdown記法をJavaで扱いたい...Scripting for the Java と showdownを組み合わせる - Object Design
http://osima.jp/blog/showdown-on-java.html

2010-09-05

jrunscriptはいつか試験的でなくなるのかしらん

jrunscriptの使い勝手というよりも、Javaでスクリプト例外が発生した際のエラー報告の仕組みがもう少し何とかなれば(特にエラーの発生したスクリプトの行番号)...。
developerWorks Japan: 今まで知らなかった 5 つの事項: Java スクリプト API

「注: このツールは試験的なものであり、将来の JDK のバージョンでは利用できなくなる可能性があります。」
JDK(TM) 6 ドキュメント: jrunscript - コマンド行スクリプトシェル

2008-08-31

jrunscriptでGUIスクリプトを起動してみて気づいたこと

  1. jrunscriptのバッチモード(-fオプション)でAWTやSwingのウィンドウを表示するスクリプトファイルを起動した場合、ウィンドウが一瞬だけ表示されて終了してしまう。
  2. スクリプトの最後にjava.lang.Thread.currentThread().sleep(1000);を付け足すと、スレッドを待機させている間はウィンドウが表示された。
  3. どうやらスクリプトを処理するメインスレッドが、スクリプトを読み終えた時点で(GUIのスレッドが生きていても)exitしているように見える。
  4. そこで、wait-notifyの組合せを使って、ウィンドウがクローズされるまでメインスレッドを待機させることを思い付いた。
  5. Rhinoで同期処理を上手くやる方法はあったっけ?
  6. "バッチモード"という位なので、これはこれで正しいのかもしれない、と勝手に自分を納得させる。
  7. あえなく完。
  8. Rhino組み込みのsync関数に気付き、sync(function() {this.wait();})の返す同期化された関数を使ってみる。
  9. wait関数が見つからないとか言われた...。確かにtypeof this.waitはundefinedだった。
  10. やっぱり完。
  11. スクリプトの最後に while (frame.isDisplayable()) {java.lang.Thread.currentThread().sleep(1000);} を付けたところ、GUI側でウィンドウクローズ時にちゃんとdispose()してれば想定通りに動く。
  12. 何か納得いかないものの完。
ちなみに、対話モードでload('GUIスクリプトファイル名');を使うなら問題なし。

イベントアダプタを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);

2008-07-22

(x == undefined)と(typeof x == 'undefined')は同じだと思ってた

単純な比較結果で満足してた。
>jrunscript
js> var a = undefined;
js> if (a == undefined) {println('a == undefined');}
js> a == undefined
js> if (typeof a == 'undefined') {println("typeof a == 'undefined'");}
js> typeof a == 'undefined'
後で変数aをnullにしてみて、気が付いた。
js> a = null;
js> if (a == undefined) {println('a == undefined');}
js> a == undefined
js> if (typeof a == 'undefined') {println("typeof a == 'undefined'");}
js> (変数aは未定義ではないので、何も出力されない)
ちなみに、undefined変数は、Java6付属のRhinoでも書き換え可能だった。
なので、結果が確実そうな(typeof 変数 == 'undefined')を使うとして、文字列比較のパフォーマンスを気にする必要はあるんだろうか?
今はちょっとした書き捨てスクリプトに使っている程度なので、特に問題なさそうだ。

Rhinoのソースファイルを見ると、同じオブジェクト参照同士の同値比較ならJavaの==で片付けてもらえたと思うので、事前に(typeof 変数名)の返す文字列をキャッシュしておいて、以降の比較に使えば速いのかな、とか思ったものの、そんな細工が必要になるほどシビアな使い方をすることは無い...と思う。

2008-07-21

RhinoのContext.evaluateString()は行番号を設定できたのだが

ScriptEngine.eval()に行番号を渡すオーバーロードがなかったので、ちょっとしたフィルタプログラムを作ろうとした時に困った。

自分のWebサイトのHTMLファイルを、埋め込みスクリプトのやり方で静的に生成したいと思った時、当初は埋め込みタグをScriptEngine.eval(java.lang.String)で再帰的に評価する方法を考えていた。

<% //内側の埋め込みスクリプトの結果を外側でさらに利用する
    var ret = "<%=内側のスクリプト%>";
    ...
%>
これは変なやり方だし、フィルタプログラムのソースも込み入ったものになるのだけれど、当時は、わざわざ自作するならC言語のマクロとかでいわれた「プリプロセッサの結果をプリプロセッサに書けることはできない」に対応する必要があるんじゃないかと思っていた。
また、こっちの方が評価される範囲が明示的で、スクリプト例外の起こりうる箇所を(埋め込みタグを飛び越えてforやwhileなどの制御構文を使うよりは)特定しやすい、とも思っていた。

問題は、埋め込みタグを評価する度に行番号が初期値に戻ってしまうので、スクリプト例外が発生した時、問題の発生箇所を示す行番号がファイル全体から見た行番号になっていない、ということだった。

これに対応しようとあれこれ調べたものの、
  • 評価するスクリプトの行番号(のオフセット)をContext.evaluateString()みたいに渡すことはできない
  • ScriptExceptionがコンストラクタの時点で行番号付きメッセージを作成しちゃってる
ので、泥臭い対処法としては、ScriptExceptionのメッセージを取得した後、「行番号っぽい箇所」を本当の行番号で置換してから表示すれば何とかなるようだ。うーん。

結局、最終的に作ったフィルタプログラムは、よくある埋め込みスクリプトと同じものになった。
日付: <%=new java.util.Date()%>
=> g_out.print('日付: ');g_out.print(new java.util.Date());g_out.print(' ');
=> 日付: Mon Jul 21 13:27:17 JST 2008

このやり方だと、(JSPみたいに)うっかり床を踏み抜いた時の問題があるものの、今の使い方はテキストの一部を変数で置き換えたりファイルの日付を自動取得したりしているだけなので、まあ、滞りなく使えている。

2008-07-20

load関数は除かないで欲しかった

従来のRhino(js.jar)からJava6のスクリプトエンジンに切り替えた時に一番ビビった。
いずれ標準的な方法にまとまるのだろうけれど、それまでの間はjs.jarとの互換性からいってもload()みたいな機能が標準で残っていた方がありがたいわけで。

Using JavaScript code modules
http://developer.mozilla.org/ja/docs/Using_JavaScript_code_modules

ScriptEngine#eval(Reader)を使えば20行程度で自作できるものの、一度標準っぽく搭載されていた機能をスクリプトエンジンの利用者それぞれにコピペやら再実装させるのは、悩ましいやり方の気がする。
※jrunscript経由でスクリプトを実行する場合は、スタートアップスクリプトのお陰で使える。スクリプトエンジンからロードできると助かるのだけれど、出来るのかどうか分からない。