あなたとジャバ、今すぐダウンロード

　やっはろー。これは、D言語 Advent Calendar 2012の24日目の記事です。D言語から Java のライブラリを使ってみた、という話をします。

　ごく一部の人から Java に似ていると評判の D言語ですが、実際その評判に違わず Java のクラスを扱うことができます。こんな感じに。

```d
void main() {
    auto arguments = JavaVMInitArgs();
    arguments.version_ = 0x00010006; // JNI_VERSION_1_6
    arguments.nOptions = 0;
    arguments.ignoreUnrecognized = true;

    JavaVM* javavm = null;
    JNIEnv* jnienv = null;
    JNI_CreateJavaVM(&javavm, cast(void**)&jnienv, &arguments);
    scope (exit) javavm.DestroyJavaVM();

    auto Integer = jnienv.FindClass("java/lang/Integer");
    auto ctor = jnienv.GetMethodID(Integer, "<init>", "(I)V");
    auto intValue = jnienv.GetMethodID(Integer, "intValue", "()I");

    auto integer = jnienv.NewObject(Integer, ctor, 42);
    auto n = jnienv.CallIntMethod(integer, intValue);

    writeln(n);
}
```

　Java には [Java Native Interface (JNI)](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/) というのがあって、Java のコードとネイティブのコードを互いに呼び出すことができるようになっています。つまり、そういうことです。

　Java のライブラリも使えそうだということで、日本語形態素解析ライブラリである [Kuromoji](http://www.atilika.org/) でワードカウントをしてみました。次の記事を参考にしています。

> [Clojure/kuromoji でテキストマイニング入門　～形態素解析からワードカウントまで～ - あんちべ！](http://antibayesian.hateblo.jp/entry/2013/09/10/231334)

```d
// dmd 2.063.2

import core.stdc.stdarg;
import std.array, std.algorithm, std.ascii, std.conv, std.file, std.format, std.range, std.stdio, std.string;

extern (C) {
    alias jboolean = bool;
    alias jint = int;

    struct _jobject;
    alias jobject = _jobject*;

    struct _jclass;
    alias jclass = _jclass*;

    struct _jmethodID;
    alias jmethodID = _jmethodID*;

    alias jstring = jobject;

    struct JNIInvokeInterface {
        void*[3] dummy0;
        jint function(JavaVM* vm) DestroyJavaVM;
    }

    struct JavaVM {
        const JNIInvokeInterface* functions;

        jint DestroyJavaVM() {
            return functions.DestroyJavaVM(&this);
        }
    }

    struct JNINativeInterface {
        void*[4] dummy0;
        void function()[ 2] dummy1;
        jclass function(JNIEnv*, const(char)*) FindClass;
        void function()[22] dummy2;
        jobject function(JNIEnv*, jclass, jmethodID, va_list) NewObjectV;
        void function()[ 3] dummy3;
        jmethodID  function(JNIEnv*, jclass, const(char)*, const(char)*) GetMethodID;
        void function()[ 1] dummy4;
        jobject function(JNIEnv*, jobject, jmethodID, va_list) CallObjectMethodV;
        void function()[14] dummy5;
        jint function(JNIEnv*, jobject, jmethodID, va_list) CallIntMethodV;
        void function()[62] dummy6;
        jmethodID function(JNIEnv*, jclass, const(char)*, const(char)*) GetStaticMethodID;
        void function()[ 1] dummy7;
        jobject function(JNIEnv*, jclass, jmethodID, va_list) CallStaticObjectMethodV;
        void function()[51] dummy8;
        jstring function(JNIEnv*, const(char)*) NewStringUTF;
        void function()[ 1] dummy9;
        const(char)* function(JNIEnv*, jstring, jboolean*) GetStringUTFChars;
        void function(JNIEnv*, jstring, const(char)*) ReleaseStringUTFChars;
    }

    struct JNIEnv {
        const JNINativeInterface* functions;

        jclass FindClass(string name) {
            return functions.FindClass(&this, name.toStringz);
        }

        jobject NewObject(jclass clazz, jmethodID methodID, ...) {
            va_list args;
            va_start(args, __va_argsave);
            auto result = functions.NewObjectV(&this, clazz, methodID, args);
            va_end(args);
            return result;
        }

        jmethodID GetMethodID(jclass clazz, string name, string sig) {
            return functions.GetMethodID(&this, clazz, name.toStringz, sig.toStringz);
        }

        jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) {
            va_list args;
            va_start(args, __va_argsave);
            auto result = functions.CallObjectMethodV(&this, obj, methodID, args);
            va_end(args);
            return result;
        }

        jint CallIntMethod(jobject obj, jmethodID methodID, ...) {
            va_list args;
            va_start(args, __va_argsave);
            auto result = functions.CallIntMethodV(&this, obj, methodID, args);
            va_end(args);
            return result;
        }

        jmethodID GetStaticMethodID(jclass clazz, string name, string sig) {
            return functions.GetStaticMethodID(&this, clazz, name.toStringz, sig.toStringz);
        }

        jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...) {
            va_list args;
            va_start(args, __va_argsave);
            auto result = functions.CallStaticObjectMethodV(&this, clazz, methodID, args);
            va_end(args);
            return result;
        }

        jstring NewStringUTF(string utf) {
            return functions.NewStringUTF(&this, utf.toStringz);
        }

        string GetStringUTFChars(jstring str) {
            auto dst = functions.GetStringUTFChars(&this, str, null);
            scope (exit) functions.ReleaseStringUTFChars(&this, str, dst);

            return dst.to!string;
        }
    }

    struct JavaVMOption {
        char* optionString;
        void* extraInfo;
    }

    struct JavaVMInitArgs {
        jint version_;

        jint nOptions;
        JavaVMOption* options;
        jboolean ignoreUnrecognized;
    }

    jint JNI_CreateJavaVM(JavaVM** pvm, void** penv, void* args);
}

void main(string[] args) {
    auto option = JavaVMOption();
    option.optionString = cast(char*)("-Djava.class.path=./kuromoji-0.7.7.jar".toStringz);

    auto arguments = JavaVMInitArgs();
    arguments.version_ = 0x00010006; // JNI_VERSION_1_6
    arguments.nOptions = 1;
    arguments.options = &option;
    arguments.ignoreUnrecognized = true;

    JavaVM* javavm = null;
    JNIEnv* jnienv = null;
    JNI_CreateJavaVM(&javavm, cast(void**)&jnienv, &arguments);
    scope (exit) javavm.DestroyJavaVM();

    auto Tokenizer = jnienv.FindClass("org/atilika/kuromoji/Tokenizer");
    auto builder = jnienv.GetStaticMethodID(Tokenizer, "builder", "()Lorg/atilika/kuromoji/Tokenizer$Builder;");
    auto tokenize = jnienv.GetMethodID(Tokenizer, "tokenize", "(Ljava/lang/String;)Ljava/util/List;");

    auto Builder = jnienv.FindClass("org/atilika/kuromoji/Tokenizer$Builder");
    auto build = jnienv.GetMethodID(Builder, "build", "()Lorg/atilika/kuromoji/Tokenizer;");

    auto Token = jnienv.FindClass("org/atilika/kuromoji/Token");
    auto getSurfaceForm = jnienv.GetMethodID(Token, "getSurfaceForm", "()Ljava/lang/String;");
    auto getPartOfSpeech = jnienv.GetMethodID(Token, "getPartOfSpeech", "()Ljava/lang/String;");

    auto List = jnienv.FindClass("java/util/List");
    auto size = jnienv.GetMethodID(List, "size", "()I");
    auto get = jnienv.GetMethodID(List, "get", "(I)Ljava/lang/Object;");

    auto source = jnienv.NewStringUTF(cast(string)read(args[1]));
    auto tokenizer = jnienv.CallObjectMethod(jnienv.CallStaticObjectMethod(Tokenizer, builder), build);

    auto token_list = jnienv.CallObjectMethod(tokenizer, tokenize, source);
    auto word_list =
        iota(jnienv.CallIntMethod(token_list, size))
        .map!(n =>
            jnienv.CallObjectMethod(token_list, get, n))
        .filter!(x =>
            jnienv.GetStringUTFChars(jnienv.CallObjectMethod(x, getPartOfSpeech)).startsWith("名詞"))
        .map!(x =>
            jnienv.GetStringUTFChars(jnienv.CallObjectMethod(x, getSurfaceForm)));

    foreach (x; word_list.array.sort.group.array.sort!"a[1]>b[1]") {
        writeln(format("%4d: %s", x[1], x[0]));
    }
}
```

```sh
# Mac
$ dmd fujiyama.d -L-framework -LJavaVM

# Windows（どこかにある jvm.lib とリンクして jvm.dll にパスを通せばいける、と思う）
$ dmd fujiyama.d jvm.lib
```

とてもつらい。主に、バインディングを書くところと、`std.algorithm.sort` が `MapResult` も `Group` も受け取ってくれないところがつらい。

　[菊池寛訳の奇巌城](http://www.aozora.gr.jp/cards/001121/card46187.html)で実行した結果です。

```
 264: の
 245: ボートルレ
 221: こと
 210: ルパン
 180: 少年
 178: 人
 138: それ
 123: 一
 123: よう
 122: 二
 108: 君
 108: 中
 108: 判事
  96: 何
  89: 時
  83: ん
  81: レイモンド
  63: 僕
  62: 十
  59: 男
  59: もの
  55: 前
  55: 伯爵
  51: 嬢
  51: 私
  50: これ
  50: 方
  50: 男爵
  45: 俺
  45: エイギュイユ
...
```

30億のデバイスで走るジャバは、D言語の上でも走るっぽいです。あなたとジャバ、今すぐダウンロード。

　遅くなりました！ 言い訳すると途中まで書いた原稿はあったけどボツにしたので間に合いませんでした！ すみません！ 次の25日目は、[@repeatedly](http://qiita.com/repeatedly) さんです！ ほんとすみません！

> [JNI で JavaVM 起動 - torutkの日記](http://d.hatena.ne.jp/torutk/20050518/)

> [C/C++ から Java のライブラリを呼ぶ with JNI - &#35;define NO_MONEY 0](http://d.hatena.ne.jp/kayn_koen/20100116/1263608499)

> [Java SE 7 Java Native Interface-related APIs and Developer Guides - ORACLE](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/)

> [Monnoroch/DJni - GitHub](https://github.com/Monnoroch/DJni) （試していないけど使えそう）