selectExtensionsを使ってみる

この記事はKnockoutJSアドベントカレンダーの2日目です。
1日目は@tan_go238さんのKnockoutJSの紹介でした。

KnockoutJSではoptionバインディングでselectタグ内のoptionを生成することができるのですが、optGroupを含めた物を生成することは出来ません。というわけで、selectExtensionsとカスタムバインディングを使って自作しようというのが今回の記事です。

具体的にはこんな感じで使えるバインディングを作成します。

<select data-bind="optgroup: {options:categoryArray, optGroups:categoryGroupArray, optionsText:'name', optionsValue:'id' }, selectedOptions: selectedCategoryArray" multiple="true" ></select>

optionsにはoptGroupには含めないoptionの配列、optGroupsにはoptGroupのラベルと子のoption配列を持つオブジェクトを指定します。optionsTextとoptionsValueについては標準のoptionsバインディングと同じ役割のものです。


出来上がったHaxeのコードはこちらになります。
なお、コンパイルする場合knockout.hxが必要になるのでhaxelibでインストールしておいてください。

class BindingHandlers {

    public static var OPTGROUP = {
    init: function(element:js.html.Node, valueAccessor:Void -> Dynamic, allBindingsAccessor:Void -> Dynamic, viewModel:Dynamic, bindingContext:BindingContext):Void {
    },
    update: function(element:js.html.Node, valueAccessor:Void -> Dynamic, allBindingsAccessor:Void -> Dynamic, viewModel:Dynamic, bindingContext:BindingContext):Void {
        var value:Dynamic = Utils.unwrapObservable(valueAccessor());

        var options:Array<Dynamic> = Utils.unwrapObservable(value.options);
        var optionsText:String = value.optionsText;
        var optionsValue:String = value.optionsValue;
        var optGroups:Array<Dynamic> = Utils.unwrapObservable(value.optGroups);

        Utils.setHtml(element, "");
        var nodes = createOption(options, optionsText, optionsValue);
        for(o in nodes){
            element.appendChild(o);
        }

        for (g in optGroups) {
            var label = g.label;
            var optgroup = Browser.document.createElement("optgroup");
            optgroup.setAttribute("label", label);
            
            var options = createOption(g.options, optionsText, optionsValue);
            for( o in options){
                optgroup.appendChild(o);
            }
            element.appendChild(optgroup);
        }
    }
    };

    private static function createOption(options:Array<Dynamic>, optionsText:String, optionsValue:String):Array<Element> {
        var array = [];
        for (o in options) {
            var text = if (optionsText == null) {
                o;
            } else {
                Reflect.field(o, optionsText);
            }

            var value = if (optionsValue == null) {
                o;
            } else {
                Reflect.field(o, optionsValue);
            }
            
            var opt = Browser.document.createElement("option");
            setText(opt, text);
            Knockout.selectExtensions.writeValue(opt, value);
            array.push(opt);
        }
        return array;
    }

    private static function setText(element:js.html.Element, text:String) {
        if (element.textContent != null) {
            element.textContent = text;
        } else {
            element.innerText = text;
        }
    }
}
Knockout.bindingHandlers["optgroup"] = BindingHandlers.OPTGROUP;

DynamicとReflectを結構使ってるためHaxeのありがたみがあまりないのはさておき、ここで注目してもらいたいのはcreateOptionメソッド内でoptionタグのvalue属性を設定するのにKnockout.selectExtensionsを使用している点です。
そもそも、optionタグのvalue属性は数値型であっても文字列型として設定されてしまうという問題があります。そのため、selectedOptionsに設定したObservableArrayには常に文字列型の値が渡ってくるので場合によっては、その後の値の比較などで問題が発生することがあります。
そこで、このselectExtensionsのwriteValueを使用すると、単にoptionタグのvalue属性を設定するだけでなく同時にKnockoutJS内部に値を保持してくれるため、selectedOptionsには元の型で値を渡すことができるようになるというわけですね。

ちなみにknockout.jsのソース内でも以下のようにコメントされています。

Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
that are arbitrary objects.

今回の本題ではないのでカスタムバインディングHaxe、knockout.hxの説明を省略していますが、selectExtensionsの紹介でした。

明日は前日に続き @tan_go238 さんの「Components を使ってみる」です。

Javaのラムダとスレッドセーフ

Java8のlambda構文がどのようにクロージャーではないかを読んで思ったのは、ラムダの外側の変数に対する処理がスレッドセーフなのかどうかという点です。というわけで早速試してみました。

スレッドセーフでない ArrayList クラスに対して並列で値を追加するというコードです。

    public void test() {
        Integer[] array = new Integer[100000];
        Arrays.fill(array,0);

        List<Integer> nums = Arrays.asList(array);

        Stream<Integer> stream = nums.stream();

        List<Integer> list = new ArrayList<Integer>();

        stream.parallel().forEach(s -> list.add(s));

        System.out.println(list.size());
    }

試した限りでは確実に例外が出て完走出来ませんでした。

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 244
	at java.util.ArrayList.add(ArrayList.java:416)
	at Test.lambda$0(Test.java:32)
	at Test$$Lambda$3.accept(Unknown Source)
	at java.util.stream.op.ForEachOp$1.accept(ForEachOp.java:51)
	at java.util.Arrays$ArrayIterator.forEach(Arrays.java:4316)


では、ArrayListを synchronizedList でラップしてやりましょう。これで list に対する値の追加はスレッドセーフです。

    public void test() {
        Integer[] array = new Integer[100000];
        Arrays.fill(array,0);

        List<Integer> nums = Arrays.asList(array);

        Stream<Integer> stream = nums.stream();

        List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());

        stream.parallel().forEach(s -> list.add(s));

        System.out.println(list.size());
    }

完走出来ました。

100000

というわけで、安易にラムダの外側の変数の中身を書き換えるようなコードを書くと危険かも?ラムダを引き渡した後の奥深くで並列で呼び出されちゃったりとか・・・・

読める!クラスファイル

この記事は変態アドベントカレンダーの24日目です。
23日目はkshojiさんの実機TAS-codingコントローラを作るでした。
KPT!KPT!

さて、周りの変態たちを見ていると、事あるごとにクラスファイルを逆アセンブルするのが嗜みのようなのでこのエントリではクラスファイルについて解説します。


まずは以下の様なクラスを準備してコンパイルしましょう!

package adventcalendar.hentai;

public class Hentai {

    public static void main(String[] args) {
        System.out.println("Hello bytecode!");
    }

}


そして出来上がった.classファイルをSublime Text2で開いたのがこちらです。エディターは適当なバイナリエディタでもOKです。

cafe babe 0000 0031 0022 0a00 0600 1409
0015 0016 0800 170a 0018 0019 0700 1a07
001b 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 124c 6f63
616c 5661 7269 6162 6c65 5461 626c 6501
0004 7468 6973 0100 1e4c 6164 7665 6e74
6361 6c65 6e64 6172 2f68 656e 7461 692f
4865 6e74 6169 3b01 0004 6d61 696e 0100
1628 5b4c 6a61 7661 2f6c 616e 672f 5374
7269 6e67 3b29 5601 0004 6172 6773 0100
135b 4c6a 6176 612f 6c61 6e67 2f53 7472
696e 673b 0100 0a53 6f75 7263 6546 696c
6501 000b 4865 6e74 6169 2e6a 6176 610c
0007 0008 0700 1c0c 001d 001e 0100 0f48
656c 6c6f 2062 7974 6563 6f64 6521 0700
1f0c 0020 0021 0100 1c61 6476 656e 7463
616c 656e 6461 722f 6865 6e74 6169 2f48
656e 7461 6901 0010 6a61 7661 2f6c 616e
672f 4f62 6a65 6374 0100 106a 6176 612f
6c61 6e67 2f53 7973 7465 6d01 0003 6f75
7401 0015 4c6a 6176 612f 696f 2f50 7269
6e74 5374 7265 616d 3b01 0013 6a61 7661
2f69 6f2f 5072 696e 7453 7472 6561 6d01
0007 7072 696e 746c 6e01 0015 284c 6a61
7661 2f6c 616e 672f 5374 7269 6e67 3b29
5600 2100 0500 0600 0000 0000 0200 0100
0700 0800 0100 0900 0000 2f00 0100 0100
0000 052a b700 01b1 0000 0002 000a 0000
0006 0001 0000 0003 000b 0000 000c 0001
0000 0005 000c 000d 0000 0009 000e 000f
0001 0009 0000 0037 0002 0001 0000 0009
b200 0212 03b6 0004 b100 0000 0200 0a00
0000 0a00 0200 0000 0600 0800 0700 0b00
0000 0c00 0100 0000 0900 1000 1100 0000
0100 1200 0000 0200 13

まずは最初の4バイトの cafe babeは、このファイルがjavaのクラスファイルだということを表すマジックナンバーです。 あ、あとクラスファイルのバイナリはビッグエンディアンですね。

cafe babe 

続く4バイト 0000 0031 はクラスファイルのマイナーバージョンとメジャーバージョンを表します。マイナーバージョンは 0 メジャーバージョンは 49 なのでJava5ですね。

0022

次の2バイトはコンスタントプール内のエントリ数を表します。ここからあとに 34 - 1 コ分のエントリが続きます。コンスタントプールの0番目はJVM側で予約されているためクライスファイル内では1番目から始まります。

0a

ここからコンスタントプールの定義です。まず最初の2バイトはこのコンスタントプール構造体のtagを示します。
0x0a は 10なので CONSTANT_Methodref ですね。

CONSTANT_Methodrefは以下のように定義されています。

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

なので、続く2バイトはこのメソッドの定義されたクラスを表す CONSTANT_Class のコンスタントプール内のインデックスです。

00 06

なので、6 です。

続く2 バイトはこのメソッドのシグネチャを表す CONSTANT_NameAndType のコンスタントプール内のインデックスです。

14

なので、20です。


私は変態じゃないのでそろそろ辛くなって来ました。そもそもjavapすればいいと思います。気になる人だけJava仮想マシン仕様を見ながら読み進めてみましょう!「jvm spec」などでググれば出てくると思います。


最後に、明日はクリスマスということでバイトコード大好きな変態さん達にクリスマスプレゼントを用意しました。ぜひ遊んでみてくださいね!
WE LOVE JAVA BYTECODE
クラスファイルを開くとこんなかんじで見れます。これなら読める!

github はこちら。
hakurai/javap.js GitHub


次は@toshi__yaさんです。

いろふさん絵描き歌 by JavaFX Scene Builder

いろふ Advent Calendarの16日目です。
私の世界線が異なるのか、残念ながら1/1スケールいろふプラモデルが手にはいらったかったのでJavaFX Scene Builderでいろふさんを描きます。

最初にとりあえずArcを配置します。


右側のプロパティでFillをWhiteに、StartAngleを186に、Lengthを316に変更します。


右下のそれっぽい位置に移動します。


Circleを追加して、それっぽい大きさでそれっぽい位置に移動します。


Rectangleを追加して、Arc Widthを48に変更してカドを丸くします。


LabelをRectangleの中央に追加します。

CubicCurveを追加して、それっぽい形に変更します。

微妙に違う気がする。

ArcのTypeをOpenに変更して口?をLineで書きなおしてみました。
それっぽくなった気がする!


明日は@ravencodingさんです。

JavaFX Advent Calendar 2012 2日目 javafx.concurrent.Task

このエントリはJavaFX Advent Calendar 2012の2日目です。
1日目は@aoetkさんのJavaFXでのマルチタッチアプリケーション開発でした。
まだまだ余裕があるので、JavaFXに興味のある方/持った方はぜひ登録してください!


さてさて、4年間付き合ったSwingとは仕事での縁がなくなり、RunnableやFutureといったクラスをめっきり見なくなってしまいました。
ですが!JavaFXなどのGUIアプリケーションでは、快適に動作するアプリケーションを開発する(画面描画をストップさせない)ために複数のスレッドを使用した非同期処理が非常に重要です。
そんなわけで、javafx.concurrentパッケージのTaskクラスについて紹介します。


さてこのTaskクラスですが、ざっくり説明すると非同期に実行する処理を実装するためのクラスです。
このクラスは抽象クラスになっており、callメソッドが抽象メソッドとして宣言されているので、この中に実際に非同期で実行する処理を実装します。
なお、Javadocでも触れられていますが、callメソッドはJavaFXの描画スレッドとは別のスレッドで実行されることになるので、このメソッド内で既にアクティブな状態のJavaFXのUIコントロール等を操作してはいけません。


例えば、Backlogというプロジェクト管理サービスから特定のユーザの課題一覧を取得する場合、このようなコードになります。

public class GetIssueTask extends BacklogTaskBase<List<Issue>> {

    @Inject
    private BacklogClient backlogClient;
    @Inject
    private User user;

    @Override
    protected List<Issue> call() throws Exception {
        List<Project> projects = backlogClient.getProjects().execute();

        final List<Issue> issueList = new ArrayList<>();

        for (Project project : projects) {
            List<Issue> findIssues = backlogClient.findIssue()
                    .setProjectId(project.getId())
                    .addAssignerId(user.getId())
                    .execute();

            issueList.addAll(findIssues);
        }

        return issueList;
    }
}

このGetIssueTaskを実行するには、このクラスのインスタンスをスレッド、またはjava.util.concurrentパッケージのExecutorServiceに渡して実行します。

Thread th = new Thread(new GetIssueTask());
th.setDaemon(true);
th.start();

または

Executors.newSingleThreadExecutor().submit(new GetIssueTask());


さて、これだけだとRunnableクラスやFutureTaskクラスを使うのと同じでなんのありがたみもありません。
以上のクラスとの違いは、TaskクラスがJavaFXで非同期処理を実行する時に便利なプロパティをいくつか持っていることです。
Javadocより抜粋すると以下の14個です。

  • ReadOnlyObjectProperty exception
  • ReadOnlyStringProperty message
  • ObjectProperty> onCancelled
  • ObjectProperty> onFailed
  • ObjectProperty> onRunning
  • ObjectProperty> onScheduled
  • ObjectProperty> onSucceeded
  • ReadOnlyDoubleProperty progress
  • ReadOnlyBooleanProperty running
  • ReadOnlyObjectProperty state
  • ReadOnlyStringProperty title
  • ReadOnlyDoubleProperty totalWork
  • ReadOnlyObjectProperty value
  • ReadOnlyDoubleProperty workDone

ご覧の通り、これらのプロパティはすべてjavafx.beans.propertyで宣言されているため、これらのプロパティ自体をJavaFXのUIコントロールのプロパティとバインドして値を表示したり、コールバックハンドラを登録することが可能です。便利ですね!!


また、これらのプロパティを更新するためにupdateProgressやupdateMessageといったメソッドも定義されています。
これらのメソッドは内部でPlatform.runLaterを呼び出して更新を行います。どういうことかというと、JavaFXの描画スレッドではないスレッドで実行されるcallメソッド内で、スレッドを意識せずに直接呼び出すことが可能です。

public class GetIssueTask extends BacklogTaskBase<List<Issue>> {

    @Inject
    private BacklogClient backlogClient;
    @Inject
    private User user;

    @Override
    protected List<Issue> call() throws Exception {

        List<Project> projects = backlogClient.getProjects().execute();

        final List<Issue> issueList = new ArrayList<>();

        int progressMax = projects.size();
        int progress = 0;
        for (Project project : projects) {
            List<Issue> findIssues = backlogClient.findIssue()
                    .setProjectId(project.getId())
                    .addAssignerId(user.getId())
                    .execute();

            issueList.addAll(findIssues);

            // 進捗率を更新 
           	// ProgressBarのprogressプロパティ同士バインドするとプログレスバーが進捗に応じて更新される!
            updateProgress(progress, progressMax); 
        }

        return issueList;
    }
}

javafx.concurrentにはもうひとつ、Serviceという便利なクラスもあるのですが、良い感じの長さのエントリになったのでこちらは又の機会or本日以降の人ということで。

参考サイト
http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm
http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html

JavFXで半透明のウィンドウを表示する


JavaFXでこんな感じの半透明ウィンドウを表示する方法です。

public void test() {
    Stage primaryStage = new Stage(StageStyle.TRANSPARENT);
    StackPane root = new StackPane();
    Scene scene = new Scene(root, 300, 250);
    scene.setFill(null);

    Button btn = new Button();
    btn.setText("Say 'Hello World'");
    btn.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            System.out.println("Hello World!");
        }
    });

    BorderPane borderPane = new BorderPane();
    borderPane.setStyle("-fx-background-radius: 10;-fx-background-color: rgba(0,0,0,0.3);");
    borderPane.setCenter(btn);
        
    root.getChildren().add(borderPane);
        
    primaryStage.setScene(scene);
    primaryStage.show();

}

重要なのはこの3箇所です。

Stage primaryStage = new Stage(StageStyle.TRANSPARENT);
scene.setFill(null);
borderPane.setStyle("-fx-background-radius: 10;-fx-background-color: rgba(0,0,0,0.3);");

まずはStage のコンストラクタで StageStyle に TRANSPARENT を指定してウィンドウ自体を透明にします。
次に、scene に対しては setFill() で null を指定して、こちらも透明にします。
最後に、 borderPane に対してはスタイルシートで背景色のアルファ値に 1 以下の値(この例では 0.3 )を指定することで
borderPane が透けて見えるようになります。

IntelliJにJSTestDriverプラグインをインストールしてみた

JavaScriptという未知の領域に足を踏み入れて右往左往していたら、IntelliJに「テスト駆動JavaScript」でも紹介されているJSTestDriverのプラグインがあったので、とりあえずインストールしてみました。

まずは、IntteliJの「Settings」画面の「Plugins」から、「Browse repositories...」をクリックして表示されるプラグインの一覧から「JSTestDriver Plugin」を選択してインストールします。


インストールが完了すると、再起動する必要があるので再起動します。


インストールしただけではつまらないので、テストを実際に動かしてみましょう。
今回は、jetbrainsのページで公開されていた「greeter-sample.zip」を使用しました。
http://confluence.jetbrains.net/display/WI/Getting+Started+with+JsTestDriver+IntelliJ+plugin


Community Editionだと、Webのモジュールが無いのでJavaのモジュールを作成して、「greeter-sample.zip」の中身を配置しておきます。


さて、IntteliJの右下に、「JsTestDriver Server」という項目が追加されています。

これをクリックして開くと、こんな感じの画面が表示されます。
左上の緑の三角が起動ボタンです。クリックするとJSTestDriverが起動します。


次に、JSTestDriverからブラウザに接続します。
真ん中に並んでいるブラウザのアイコンがボタンになっているので、クリックするとブラウザが立ち上がって接続されます。
私の環境では、デフォルト設定のままで動きました。

ブラウザはこんな感じの表示になります。


これで準備は整いました。それでは、テストを実行してみましょう。
プロジェクトから、「greeter.jstd」ファイルを右クリックし、「Run greeter.jstd」の項目をクリックするとテストが開始します。

ChromeSafariOperaでテストが実行されました!
それぞれ1つ失敗しているのはサンプルのテストコードの仕様ですね。
成功した項目が表示されない場合はソートボタンの左側の逆三角な感じのボタンをクリックすると表示されますよ。