DynamicRealmを使って、Realm Browserを作ってみましょう！

最近realmはDynamic Reamsを含んだrealm0.86をリリースしました。Dynamic Realmsによって私たちはRealm Schemaにアクセスし、そしてオブジェクトを作り、文字列（モデルネームとフィールドネーム）を使ってフィールドに動的にアクセスできます。

## Concept

Realm browserはテーブル、構造、コンテンツを見ることに使われます。このブラウザはAndroidデバイスによって提供されるローカルホストからアクセスすることができます。

## NanoHTTP

[NanoHTTP](http://nanohttpd.org/)は他のアプリケーションに埋め込まれた軽量のHTTPサーバーで、修正BSDライセンスの下でリリースされました。

### NanoHTTPD sample

```
public class HelloServer extends NanoHTTPD {

    public static void main(String[] args) {
        ServerRunner.run(HelloServer.class);
    }

    public HelloServer() {
        super(8080);
    }

    @Override
    public Response serve(IHTTPSession session) {
        String msg = "<html><body><h1>Hello server</h1></body></html>";

        return newFixedLengthResponse(msg);
    }
}
```

このシンプルなコードは`Hello server`テキストでHTMLページを作りそれはローカルホストのポート8080からアクセスできます。

## DynamicRealm

最初に言ったように、Dynamic Realmsは従来のRealmsの柔軟なバージョンと言えます。Dynamic Realmsは従来のRealmで使っていた物と同じconfigurationを使えるが、どんなschema定義、migration、schemaのバージョンも無視します。

Dynamic Realmsを使ったら、モデル名とフィールド名が分かればコンテンツをアックセスすることができます。

```
DynamicRealmObject user = realm.createObject("User");
String name = user.getString("name");
int age = user.getString("age");
```

DynamicRealmは柔軟性のために型安全とパフォーマンスを犠牲にしています。

## RealmBrowser

### 実装に関して

重要なコードだけを説明するので、フルコードを見たい場合は私の[github](https://github.com/NikoYuwono/realm-browser-android)リポジトリを見てください。

最初に前回のセッションからパラメーターがあるかないかチェックします。

```
Map<String, String> params = session.getParms();
String className = params.get("class_name");
String selectedView = params.get("selected_view");
```

そして、そのクラスがあるかないかチェックし、そのクラスがRealmObjectのサブクラスかじゃないかチェックします。

```
Class clazz = Class.forName(className);
if (RealmObject.class.isAssignableFrom(clazz)) {
	// Show structure or content
}
```

そのクラス名から普通のqueryを実行して、resultsを取ります。

```
RealmResults<DynamicRealmObject> realmResults = dynamicRealm.where(simpleTableName).findAll();
```

RealmSchemaからフィールド名を取れます。

```
RealmObjectSchema realmObjectSchema = dynamicRealm.getSchema().get(simpleTableName);
Set<String> fieldNames = realmObjectSchema.getFieldNames();
```

今フィールドタイプを直接取れるAPIはまだ実装されていませんが、私は[github issue](https://github.com/realm/realm-java/issues/1883)を登録して、realmチームが解決してくれたので次のバージョン(0.87)のリリースに入っているはずです。
そして、フィールドタイプを取るために私は他の方法を考えて、一つ一つのオブジェクトをタイプチェックを行います。

```
private RealmFieldType checkRealmFieldType(Object obj) {
    int nativeValue = -1;
    if (obj instanceof Long || obj instanceof Integer || obj instanceof Short || obj instanceof Byte) {
        nativeValue = 0;
    } else if (obj instanceof Boolean) {
        nativeValue = 1;
    } else if (obj instanceof String) {
        nativeValue = 2;
    } else if (obj instanceof byte[] || obj instanceof ByteBuffer) {
        nativeValue = 4;
    } else if (obj == null || obj instanceof Object[][]) {
        nativeValue = 5;
    } else if (obj instanceof Mixed) {
        nativeValue = 6;
    } else if (obj instanceof Date) {
        nativeValue = 7;
    } else if (obj instanceof Float) {
        nativeValue = 9;
    } else if (obj instanceof Double) {
        nativeValue = 10;
    }
    return RealmFieldType.fromNativeValue(nativeValue);
}
```

そして、さっきのresultsから一番目のオブジェクトを取って、さっき定義した機能でタイプチェックを行います。

```
int index = 0;
for (String fieldName : fieldNameArray) {
    Object object = realmResults.get(0).get(fieldName);
    realmFieldTypes[index] = checkRealmFieldType(object);
    index++;
}
```

最後にコンテンツを取るために、テーブルを反復して、フィールドタイプによってgetter機能でvalueを取ります。

```
for (int i = 0; i < tableSize; i++) {
    DynamicRealmObject dynamicRealmObject = realmResults.get(i);
    for (int j = 0; j < columnCount; j++) {
        String columnName = fieldNameArray[j];
        String value = "";
        switch (realmFieldTypes[j]) {
            case INTEGER:
                value = String.valueOf(dynamicRealmObject.getLong(columnName));
                break;
            case BOOLEAN:
                value = String.valueOf(dynamicRealmObject.getBoolean(columnName));
                break;
            case FLOAT:
                value = String.valueOf(dynamicRealmObject.getFloat(columnName));
                break;
            case DOUBLE:
                value = String.valueOf(dynamicRealmObject.getDouble(columnName));
                break;
            case DATE:
                value = dynamicRealmObject.getDate(columnName).toString();
                break;
            case STRING:
                value = dynamicRealmObject.getString(columnName);
                break;
        }
    }
}
```

### 使い方

このライブラリーの使い方はRealmBrowserのインスタンスを作って、`RealmBrowser.start();`という機能を呼んだらRealmBrowserが始まります。ポートを設定したい場合に、パラメーターを入れてください。使い終わったら `RealmBrowser.stop();`を呼んでください。

ブラウザからアックセスしたい場合に、`realmBrowser.showServerAddress(this);` という機能を呼んで、logcatでRealmBrowserのaddressが表示されます。

注意：AndroidデバイスとPCやMacが同じWi-Fiに接続していないとアクセスすることができません。

### 最後に

このライブラリーは改善の余地がまだまだあると思います。今週中に検索機能を追加したいと今度HTML Pageの作り方も直したいと思います。
今フルコードは[github](https://github.com/NikoYuwono/realm-browser-android)にあります。フィードバックや問題やプルリクエストは誠にありがたいです。