自分の速さで

調べたこと、忘れそうなことをゆるゆると書いていく

static イニシャライザを握りつぶす

はじめに

JUnitでテストコードを書くにあたって、JMockitを使用して適宜モック化してテストを実施していました。 staticイニシャライザにテストとは関係ないコードがあったので実行しないようにモック化したかったのですが、JMockitによるモック化はできなそうだったのでJavassistを使用して解決することにしました。

解決方法

CtClassからstaticイニシャライザオブジェクトを取得できるので、setBodyメソッドを実行してメソッドの中身を書き換えればOK

Javassistそのものの説明は省きます

Hoge.java

package cpp0302.test;

/** モック化対象のクラス */
public class Hoge {

    private static int count;

    static {
        // 検証用に無理やり例外を発生させる
        count = 1 / 0;
    }

    public static void printCount() {
        System.out.println("count: " + count);
    }
}


CrushStaticInitializer.java

package cpp0302.test;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

public class CrushStaticInitializer {

    public static void main(String[] args) throws Exception {

        ClassPool cp = ClassPool.getDefault();
        cp.appendSystemPath();

        CtClass hogeClass = cp.get("cpp0302.test.crush.Hoge");
        CtConstructor classInitializer = hogeClass.getClassInitializer();

        // staticイニシャライザの内容を変更
        classInitializer.setBody("{ count = 78; System.out.println(\"staticイニシャライザを握りつぶした\"); }");

        hogeClass.toClass();

        // クラスメソッド実行前にstaticイニシャライザが実行される
        Hoge.printCount();
    }

}


実行結果

staticイニシャライザを握りつぶした
count: 78

これでテストコード実行に関係ない処理を握りつぶすことができました

Git Bash で svn status を実行すると日本語のファイル名が文字化けする & 対処法

はじめに

Windows環境でSubversionでマージを自動で実行する仕組みを作りたかったのですが、今回はGit Bashでシェルを実行する方針で作成を進めていました。

マージする前に不要なファイルのゴミ掃除をしたかったので、下記を参考にバージョン管理外のファイルを削除しようと思ったのですが、うまく行かなかったのでその原因と解決策をメモ。

kakakakakku.hatenablog.com

エラー内容

テストとして、Subversion管理外のファイルを下記のフォルダ構成で配置しています。

current directory/
  ├src/
  │  └ abc.txt
  ├ test.txt
  ├ 新しいテキスト ドキュメント (2).txt
  └ 新しいテキスト ドキュメント.txt

なので、以下のコマンドを実行した場合

$ svn status | grep ^?
?       src\abc.txt
?       test.txt
?       新しいテキスト ドキュメント (2).txt
?       新しいテキスト ドキュメント.txt

上記の結果であれば、cutで9文字目を切り出しxargsで対象を全て削除することができるのですが…

実際は下記の内容が出力されました

$ svn status
?       src\abc.txt
?       test.txt
?       ▒V▒▒▒▒▒e▒L▒X▒g ▒h▒L▒▒▒▒▒▒▒g (2).txt
?       ▒V▒▒▒▒▒e▒L▒X▒g ▒h▒L▒▒▒▒▒▒▒g.txt

解決策

Subversionの出力が明らかに文字化けしているので、文字コードをShift-JISからUTF-8に変換してみました。

$ svn status | iconv -f Shift-JIS -t UTF-8 | grep ^?
?       src\abc.txt
?       test.txt
?       新しいテキスト ドキュメント (2).txt
?       新しいテキスト ドキュメント.txt

これで想定通り検出できました。

そもそもSubversionの出力の文字コードUTF-8に変更できたらこんなことはする必要ありませんね。(変更できるかどうかは調べていません。。)

おわりに

Git bash 上でバージョン管理外のファイルを削除するコマンドは、最終的に以下のようになってます。

svn status | sed -e 's@\\@/@g' | iconv -f Shift-JIS -t UTF-8 | grep ^? | cut -c9- | xargs -I{} rm -rf "{}"

理由は以下のとおりです。

  • Git Bashなので、フォルダ区切り文字を \ から / に変える必要がある
  • ファイル名にスペースが入っている場合うまく消せないので、rm するときはダブルクォーテーションで囲む必要あり

【Spring】Streamを使用したらArrayIndexOutOfBoundsException

はじめに

新規プロジェクトのJavaのバージョンは1.8というとだったので、早速Streamを使おうと思ったら java.lang.ArrayIndexOutOfBoundsException が発生したので修正内容をメモ。

エラー内容

原因

いろいろ探した結果下記がヒット
spring - ArrayOutOfBoundsException on Bean creation while using Java 8 constructs - Stack Overflow

Springのバージョンが3以下だとStreamは使えないとのこと。
ということで現在のプロジェクトのバージョンをチェック

pom.xml

f:id:cpp_0302:20160308141037p:plain

Springのバージョンは3.1.1.RELEASEでした。
そもそも何でこんな古いバージョンでプロジェクトを作ってたんだ。。。

修正内容

ということで、Springのバージョンを4以降にバージョンアップします。

pom.xml

f:id:cpp_0302:20160308143550p:plain

これでうまくいきました。

Mac OS X 10.11 環境下でiReportを動かす

はじめに

会社でJasparReportsを使うことになったのでデザイナであるiReportをインストールしたところ、いくらアイコンをダブルクリックしてもアプリが起動しなかったので起動するまでの設定をメモ。

手順

Java6をインストール

iReportを動かすためにはJava6が必要らしいので、Java6をインストールします。
Mac版のJava6はAppleから提供されているので、以下からインストール
ダウンロード - Java for OS X 2015-001

ireport.confを編集

iReportの設定ファイルにjdkhomeを設定する箇所があるので、インストールしたJava6を指定します。

iReportのアプリ配下にある

Contents/Resources/ireport/etc/ireport.conf

を開いてjdkhomeの設定値を書き換えます

jdkhome="/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home"

これで動きました。

TypeScriptとKnockout.jsを組み合わせる際の注意点(thisの扱いについて)

TypeScript + Knockout.js

TypeScriptとKnockout.js(ES5)を組み合わせた場合のサンプルは以下を参照してください。

kojs.sukobuto.com

上記の記事では、JavaScriptの問題児thisと、その解決法としてFunction.bindによるthisの束縛を提案しています。
これには別の解決法もあるので、メモ。

アロー関数式

TypeScriptにおいて、関数の表現の仕方は以下の2つあります。

  • function式
  • アロー関数式

どちらも関数を表すものですが、アロー関数式はthisの扱い方が異なります。

アロー関数式を使用すると、以下のようなJavaScriptに変換されます。

TypeScript
class hogeClass {
    message: string;

    constructor() {
        this.message = "Hello, World!";
    }

    start() {
        setTimeout(() => { alert(this.message); }, 3000);
    }
}
JavaScript
var hogeClass = (function () {
    function hogeClass() {
        this.message = "Hello, World!";
    }
    hogeClass.prototype.start = function () {
        var _this = this;
        setTimeout(function () { alert(_this.message); }, 3000);
    };
    return hogeClass;
})();

アロー関数内におけるthisは、_thisを参照するように変換してくれるので、関数の呼び出し元に関係なく、インスタンスを参照します。これでthis問題が一気に解決します。

サンプルもこのように書けばbindなしでも動きます。

class GiftSetViewModel {
    constructor(public gifts: ItemViewModel[]) {
        ko.track(this); // プロパティ (gifts) を監視できるようにする
    }

    addGift = () => {
        this.gifts.push({
            name: "",
            price: 0
        });
    }

    ...
}

TypeScriptのenumをfor-in文で使用した時の挙動について

はじめに

最近Cordova+TypeScriptでハイブリッドアプリを作っているのですが、列挙型をfor-inしたときにハマったのでメモ。

現象

試しにこんなプログラムがあったとして

enum Hoge {
    Foo,
    Bar
}

for(var hoge in Hoge) {
    console.log(hoge);
}

こう出力されてほしいのですが

Foo
Bar

実際はこう出力されます

0
1
Foo
Bar

解説

何でこんな挙動になるか調べたら下記にヒットしました。

stackoverflow.com

enumjavascriptコンパイルすると

var Hoge;
(function (Hoge) {
    Hoge[Hoge["Foo"] = 0] = "Foo";
    Hoge[Hoge["Bar"] = 1] = "Bar";
})(Hoge || (Hoge = {});

と変換されるため、Hogeオブジェクトは以下となります。

Object {0: "Foo", 1: "Bar", Foo: 0, Bar: 1}

これによって、以下のように列挙型の値から文字列を取得したり、逆に文字列から列挙型の値を取得できるようになるわけですが、これのせいでfor-inが想定した結果を出力してくれないのです。

console.log(Hoge[0])        // "Foo"
console.log(Hoge[Hoge.Foo]) // "Foo"
console.log(Hoge["Bar"])    // 1

解決策

要はキーまたは値のうち、取りたくないほうを無視すればいいわけなので、stackoverflowでも示されている通り

文字列を取得したい場合

for(var key in Hoge) {
    if(!isNaN(key)) {
        console.log(key);
    }
}

列挙型の値を取得したい場合

for(var value in Hoge ){
    if(isNaN(value)) {
        console.log(value);
    }
}

とすればOKです。それぞれ出力結果は

0
1
Foo
Bar

となります。また、
TypeScript クイックガイド - phyzkit.net
にも書いてありますが、for文で繰り返すか、linq.jsのEnumerable.RangeToを使う手もあります。こっちの方がすっきりしますが、列挙型の値を自分で設定している、かつ、値に欠番がある場合は使えないので注意が必要です。

for(var i: Hoge = Hoge.Foo; i <= Hoge.Bar; i++){
    console.log(Hoge[i]);
}
Enumerable.RangeTo(Hoge.Foo, Hoge.Bar)
.ForEach(x => {
    console.log(Hoge[x]);
});

neocomplcacheからneocompleteへの乗り換え

はじめに

vimの環境整備をしていてneosnippetを入れたところ、スニペットの入力補完としてneocomplcacheを入れたはいいが、こちらはすでに更新を行わないらしい。ということでneocompleteを入れることにしました。

前提条件

has('lua')を有効にする必要があるらしいです。
ということでluaオプションが有効であるか確認して、無効だった場合はluaオプションを有効にしたvimを再インストールします。

現状の確認

早速luaオプションが有効かどうか確認。

# vim --version | grep lua
+listcmds +localmap -lua +menu +mksession +modify_fname +mouse -mouseshape

私のMacにデフォルトでインストールされているvimには入っていないみたいです。
ということで、vimを再インストールして上の出力結果にある-lua+luaにするところから始めます。

Luaをインストール

そもそもLuaを入れてなかったのでBrew経由でインストール

# brew install lua

vimをインストール

インストールオプションの確認

Macの場合はBrewでインストールできるので楽ちん。インストールする前にオプションを確認。

# brew options vim | grep lua
 7:--with-lua
 8:	Build vim with lua support
 9:--with-luajit
10:	Build with luajit support

7行目の--with-luaオプションを使用すればOK。

他にも、後で必要になったときにまた再インストールするのが面倒なので--with-python3のオプションも有効にすることに。

インストール

調べたオプションを使用してvimをインストール。
システムで使用してるvimを別にインストールすると設定が面倒そうなので、今回は、--override-system-viを使用して既存のviを上書きしました。

# brew install vim --override-system-vi --with-lua --with-python3

完了したらターミナルを再起動すればインストールしたvimが使えるようになります。

改めてvimのオプションを確認すると

# vim --version | grep lua
+dialog_con      +lua             +rightleft       +windows

ちゃんと+luaになってるので、これでvimのインストール完了。

neocompleteの導入

あとはvim上で

  • NeoBundleClean
  • NeoBUndleInstall

をすればneocomplcacheが削除されてneocompleteがインストールされます。