2012-09-19

Rhinoでprintfやformatに整数フォーマット(%d)を使うとエラー

以下の記述はIllegalFormatConversionExceptionを引き起こす(大量のスタックトレースが出力されるので注意)。

a-1. エラーを引き起こすスクリプト:
java.lang.String.format("%d", [10]); // error
java.lang.System.out.printf("%d", [10]); // error

a-2. エラーメッセージ(抜粋):
org.mozilla.javascript.WrappedException: Wrapped java.util.IllegalFormatConversionException: d != java.lang.Double

数値(Number)をパラメータ配列に直接入れた結果、 整数フォーマット(%d)に対してjava.lang.Doubleが渡されてしまったようだ。
対策として、数値をjava.lang.Integer.valueOf()メソッドでjava.lang.Integerに変換してから パラメータ配列に入れることにする。

b-1. 実行するスクリプト(printf-format.js):
var stdout = java.lang.System.out;
var n = 10; // Number
try {
    stdout.println(java.lang.String.format("%d", [n])); // error
} catch (e) {
    stdout.println(e.message);
}
try {
    stdout.printf("%d\n", [n]); // error
} catch (e) {
    stdout.println(e.message);
}
stdout.println(java.lang.String.format("%d", [java.lang.Integer.valueOf(n)]));
stdout.printf("%d\n", [java.lang.Integer.valueOf(n)]);

b-2-1. Rhino1.7で実行する:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main -w -debug printf-format.js
java.util.IllegalFormatConversionException: d != java.lang.Double
java.util.IllegalFormatConversionException: d != java.lang.Double
10
10

b-2-2. jrunscript(Java1.7)で実行する:
>jrunscript printf-format.js
java.util.IllegalFormatConversionException: d != java.lang.Double
java.util.IllegalFormatConversionException: d != java.lang.Double
10
10

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

Rhino起動時の-classpathオプションに"."を入れ忘れてエラー

書き捨てスクリプトからカレントディレクトリのJavaクラスを呼び出そうとして、 うっかり-classpathにjs.jarだけ指定していたためにエラーになったことが何度かあった。
それ以来、Rhino起動時は、まず"."と"js.jar"をクラスパスに含めるようにしている。

1. Javaソースファイル(fruits/Fruit.java):
package fruits;
public class Fruit {
    public String name;
}

2. Javaクラスファイルを作成する(fruits/Fruit.class):
>javac fruits/Fruit.java

3. スクリプトファイル(fruit.js):
print(Packages.fruits.Fruit);
var fruit = new Packages.fruits.Fruit();
fruit.name = "banana";
print("fruit=" + fruit.name);

4-1. -classpathオプションに"."を入れ忘れた場合、指定されたJavaクラスが見つからないのでエラー:
>java -classpath js.jar org.mozilla.javascript.tools.shell.Main -w -debug fruit.js
[JavaPackage fruits.Fruit]
js: "fruit.js", line 2: uncaught JavaScript runtime exception: TypeError: [JavaPackage fruits.Fruit] is not a function, it is object.
        at fruit.js:2

4-2. -classpathオプションに"."を含めると、期待通りJavaクラスを使える:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main -w -debug fruit.js
[JavaClass fruits.Fruit]
fruit=banana

デバッグ向けの設定でRhino Shellを使う

-wオプションと-debugオプションを指定すると、 スクリプトに問題があった時に詳しいメッセージが出力されるので、よく使っている。

1. 実行するスクリプト(warning-and-error.js):
eval({}); // warning
a; // error

2-1. オプションなしの場合、警告メッセージやエラー発生位置が出力されない:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main warning-and-error.js
js: uncaught JavaScript runtime exception: ReferenceError: "a" is not defined.

2-2. -wオプションと-debugオプションを指定した場合、警告メッセージとエラー発生位置が出力される:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main -w -debug warning-and-error.js
js: warning: "warning-and-error.js", line 1: Calling eval() with anything other than a primitive string value will simply return the value. Is this what you intended?
js: "warning-and-error.js", line 2: uncaught JavaScript runtime exception: ReferenceError: "a" is not defined.
        at warning-and-error.js:2

2012-01-15

java.util.Scannerでコメント文をスキップさせたい

スクリプト言語のコメントをjava.util.Scannerに処理させたい。
commandA(myVar, 10)
; コメント
commandB(200)
区切りパターンにそれらしい正規表現を入れたら上手くいった。
scanner.useDelimiter("(\\s|;.*(\r|\n|\r\n))+");

java.util.Scannerでスクリプトの命令文を分割したい

スクリプトの命令文「command(myVar, 100)」を
"command" "(" "myVar" "," "100" ")"
の6つに分割したい。カッコもコンマもスクリプトの要素として意味があるので、分割結果として欲しい。が、java.util.Scannerをそのまま使うと
"command(myVar," "100)"
の2つに分割されてしまう。Scannerにテキストの区切りパターン(正規表現)を設定することは可能だが、カッコやカンマを区切りパターンに追加すると、今度は分割結果から無くなってしまう。
"command" "myVar" "100"
そんな場合は、Scanner#findInLine(Pattern pattern)で、さらに細かく分割すれば良いようだ。
Pattern pattern = Pattern.compile("[(),]|[a-zA-Z0-9]+");
Scanner scanner = new Scanner("command(myVar, 100)");
while (scanner.hasNext()) {
    for (;;) {
        String token = scanner.findInLine(pattern);
        if (token == null) {
            break;
        }
        System.out.println(token);
    }
}
以下の結果を得ることが出来た。
command
(
myVar
,
100
)

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