@blog.justoneplanet.info

日々勉強

ZipInputStreamから文字列を取り出す

メモ。

■ZipInputStream

Zipはzip化されたデータとヘッダー情報から成る。

public static String readZIPStream(InputStream inputStream) throws IOException {
    String str = "";
    String readed;
    ZipInputStream zis = new ZipInputStream(inputStream);
    InputStreamReader inputStreamReader = new InputStreamReader(zis);
    BufferedReader bufferReader = new BufferedReader(inputStreamReader);
    while (zis.getNextEntry() != null) {
        while ((readed = bufferReader.readLine()) != null) {
            str += readed + "\n";
        }
        zis.closeEntry();
    }
    bufferReader.close();
    inputStreamReader.close();
    zis.close();
    return str;
}

■GZIPInputStream

GZipはzip化されたデータ本体で、GZipの場合は勝手が違う。

public static String readGZIPStream(InputStream inputStream) throws IOException {
    String str = "";
    GZIPInputStream gzis = new GZIPInputStream(inputStream);
    InputStreamReader inputStreamReader = new InputStreamReader(gzis);
    BufferedReader bufferReader = new BufferedReader(inputStreamReader);
    String readed;
    while ((readed = bufferReader.readLine()) != null) {
        str += readed;
    }
    bufferReader.close();
    inputStreamReader.close();
    gzis.close();
    return str;
}

BufferedReaderをインスタンス化するときにサイズを指定しない場合は8192となる。

AWS SDK for PythonでRoute53を操作する

以下のスクリプトをサーバー起動時に実行する事でホスト名を固定する事ができる。

#!/usr/bin/python
import sys
import boto
from boto.route53.record import ResourceRecordSets
import boto.ec2
import json
import pprint

key = 'your key'
secret = 'your secret'
domain_name = 'zone name'

document = boto.utils.get_instance_identity()["document"]
region = document["region"]
instance_id = document["instanceId"]

ec2_conn = boto.ec2.connect_to_region(
  region,
  aws_access_key_id=key,
  aws_secret_access_key=secret
)
reservations = ec2_conn.get_all_instances(instance_ids=[instance_id])
instance = reservations[0].instances[0]


route53_conn = boto.connect_route53(key, secret)
zone = route53_conn.get_zone(domain_name)
record = zone.get_cname(instance_id + "." + domain_name)
if record is not None:
  zone.delete_cname(instance_id + "." + domain_name)
zone.add_cname(instance_id + "." + domain_name, instance.public_dns_name)

以下の処理で自身のinstance IDを取得できる。

document = boto.utils.get_instance_identity()["document"]
region = document["region"]
instance_id = document["instanceId"]

以下のようにして削除する前に存在するか確認する必要がある。

record = zone.get_cname(instance_id + "." + domain_name)
if record is not None:
  zone.delete_cname(instance_id + "." + domain_name)

以下のようにしてCNAMEを追加する。

zone.add_cname(instance_id + "." + domain_name, instance.public_dns_name)

参考

apkからソースを読んでみる

Proguardを使ったアプリのapkを逆コンパイルしてソースを読んでみる。

unzip Hello.apk
wget https://dex2jar.googlecode.com/files/dex2jar-0.0.9.15.zip
unzip dex2jar-0.0.9.15.zip

classes.dexをjarに変換する。

./dex2jar.sh ~/Hello/classes.dex

面倒なのでJava Decompilerのツールでjarをそのまま開く。

■比較

定数は以下のように展開されて削除されている。

private static final int HELLO_ID = 1;
// nothing

staticでなければ残るが変数名は変えられる。

public final int HELLO_CODE = 1;
public final int rk = 1;

ギャラリーから取得したuriをパス表記にする以下のメソッドは

  protected String convertMediaUriToPath(Uri uri) {
    String [] proj = {MediaStore.Images.Media.DATA};
    Cursor cursor = getContentResolver().query(uri, proj, null, null, null);
    if (cursor != null) {// prevent from NullPointerException of the stack trace
      int index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
      cursor.moveToFirst();
      String path = cursor.getString(index); 
      cursor.close();
      return path;
    }
    return null;
  }

以下のようにメソッド名が簡潔になり、変数名も簡略化される。Proguardを使わなくても定数は展開される。

protected String a(Uri paramUri)
  {
    String[] arrayOfString = { "_data" };
    Cursor localCursor = getContentResolver().query(paramUri, arrayOfString, null, null, null);
    String str = null;
    if (localCursor != null)
    {
      int i = localCursor.getColumnIndexOrThrow("_data");
      localCursor.moveToFirst();
      str = localCursor.getString(i);
      localCursor.close();
    }
    return str;
  }

以下のようにActivityのクラス名は変わらない。

public class HelloActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
public class HelloActivity extends Activity {
  protected void onCreate(Bundle savedInstanceState) {

これはProguardの設定に以下のような記述があるからである。

vim ~/android-sdk-mac_x86/tools/proguard/proguard-android.txt
# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

その他にもproguard-android.txtには、manifestファイルに記述するクラスはクラス名が変更されないような記述が書いてある。

Mozcの記号辞書に全角チルダを追加する

全角波ダッシュが一部の環境で文字化けするために全角チルダも使えるようにする。

// JavaScript
unescape('%uff5e');// 全角チルダ:~
unescape('%u301c');// 波ダッシュ:〜

以下のファイルを開き

vim data/symbol/symbol.tsv

波ダッシュについては以下のようになっているので

記号    〜  にょろ なみ から ー - ~ きごう より 波ダッシュ      GENERAL

以下のように追記すればよい。

記号    ~  にょろ ちるだ から ー - ~ きごう より   全角チルダ      GENERAL

macでは見栄えに全く差が無いので、一応vimで検索して確かめておく。

:/\%u301c
:/\%uff5e

ちなみにこのtsvの形式は以下のようになっている。

POS CHAR    Reading (space separated)   description additional description  category    memo    unicode

Xcode 5.1にアップデートするとビルドできなくなる

arm64に対応していないものが原因である。

■Build Settings>Architectures

CocoaPodsに対応するため以下の部分を

$(ARCHS_STANDARD)

以下のように修正する。

$(ARCHS_STANDARD_32_BIT)

変更後、以下のコマンドを実行する。

pod update

■JSONKit

isa is deprecatedと表示されてビルドできないので、Podfileに以下を追記する。ちなみにcompiler flagに-ferror-limit=0を設定してもビルドできない。

post_install do |installer_representation|
    installer_representation.project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['CLANG_WARN_DIRECT_OBJC_ISA_USAGE'] = 'YES'
        end
    end
end

JSONKitをやめてNSJSONSerializationにしたいのだが某SDKが使用しているので諦める。

■OpenSSL

OpenSSL-for-iPhoneを再ビルド

cd OpenSSL-for-iPhone/
vim ./build-libssl.sh

以下のように修正する。というかmasterからpullすると7.1になっている。

VERSION="1.0.1e"                                                          #
SDKVERSION="7.1"           

以下のコマンドでビルドしてできるlibcrypto.aとlibssl.aで上書きする。

./build-libssl.sh

参考

Mozc for Androidのテストを読んでみる

EasyMockを使っている部分があるのでそこに関する自分用のメモ

public class MozcViewTest extends InstrumentationTestCaseWithMock

以下のようにしてモックを作る。

@SmallTest
public void testStartAnimation() {
    MozcView mozcView = createViewMockBuilder(MozcView.class)
        .addMockedMethods("getCandidateView", "getSymbolInputView", "expandDropShadowAndBackground", "collapseDropShadowAndBackground")
        .createMock();
    CandidateView candidateView = createViewMockBuilder(CandidateView.class)
        .addMockedMethods("getVisibility")
        .addMockedMethod("setVisibility", int.class)
        .createMock();
    SymbolInputView symbolInputView = createViewMockBuilder(SymbolInputView.class)
        .addMockedMethods("getVisibility")
        .addMockedMethod("setVisibility", int.class)
        .createMock();

以下のようにテストする。

    {
      resetAll();// 今までのexpectで登録されたものをリセット

      expect(mozcView.getCandidateView()).andReturn(candidateView);
      // (a)を実行した時、モックmozcViewのgetCandidateViewが呼ばれ返り値がcandidateViewでとなる事を想定

      candidateView.setVisibility(View.GONE);
      expect(mozcView.getSymbolInputView()).andReturn(symbolInputView);
      // (a)を実行した時、モックmozcViewのgetSymbolInputViewが呼ばれ返り値がsymbolInputViewでとなる事を想定

      expect(symbolInputView.getVisibility()).andReturn(View.VISIBLE);
      // (a)を実行した時、モックsymbolInputViewのgetVisibilityが呼ばれ返り値がView.VISIBLEでとなる事を想定

      replayAll();// 登録したモックをリプレイモードにする
      mozcView.startCandidateViewOut();// (a)…startCandidateViewOutを実行する
      verifyAll();// 登録したモックを全て検証する(想定通りになったかどうか検証)
    }

startCandidateViewOutは以下のようになっているとする。

  void startCandidateViewOut() {
    getCandidateView().setVisibility(View.GONE);
    if (getSymbolInputView().getVisibility() != VISIBLE && isDropShadowExpanded) {
      collapseDropShadowAndBackground();
    }
  }

この時にstartCandidateViewOutで、expectしていないメソッドが呼ばれたり、定義した回数以上呼ばれたりなどするとテストが失敗する。

参考

mozcで「かお」の変換時に顔文字が表示されないようにする

現状、かおの変換候補表示にタイムラグがある。

rewriter/emoticon_rewriter.cc

bool RewriteCandidate(Segments *segments)

以下の部分を削除する。

      // "かお"
    } else if (key == "\xE3\x81\x8B\xE3\x81\x8A") {
      // When key is "かお", expand all candidates in conservative way.
      const EmbeddedDictionary::Token *token
          = Singleton<EmoticonDictionary>::get()->GetDictionary()->AllToken();
      CHECK(token);
      // first 6 candidates are inserted at 4 th position.
      // Other candidates are pushed to the buttom.
      value = token->value;
      value_size = token->value_size;
      initial_insert_pos = 4;
      initial_insert_size = 6;

rewriter/emoticon_rewriter_test.ccに2箇所ほど「かお」の変換をテストしている箇所があるので削除する。

TizenでUUIDを取得する

void
UUIDManager::Get(String &uuid) {
	result r = E_SUCCESS;
	String keyUUID("key_uuid");
	AppRegistry* appRegistry = Application::GetInstance()->GetAppRegistry();
	r = appRegistry->Get(keyUUID, uuid);
	if (r == E_KEY_NOT_FOUND) {
		String u;
		Tizen::Base::UuId* pUUID = Tizen::Base::UuId::GenerateN();
		u.Append(pUUID->ToString().GetPointer());
		appRegistry->Add(keyUUID, u);
		delete pUUID, pUUID = NULL;
	}
	r = appRegistry->Get(keyUUID, uuid);
}

Tizenでデータを永続化する

SQLiteを使うまでのデータでない場合に、SharedPreferencesやNSUserDefaultのようなものが欲しくなる。Tizenでは以下のようにする。

#include <FApp.h>
using namespace Tizen::App;
result r = E_SUCCESS;
String hogeKey("tmp_hoge_key");
String hogeValue("tmp_hoge_value");
String value("");
AppRegistry* appRegistry = Application::GetInstance()->GetAppRegistry();
r = appRegistry->Get(hogeKey, value);
if (r == E_KEY_NOT_FOUND) {
	AppLog("value:%ls", value.GetPointer());
	appRegistry->Add(hogeKey, hogeValue);
}
r = appRegistry->Get(hogeKey, value);
AppLog("value:%ls", value.GetPointer());

以下のようなログが表示される。

virtual result HogeHoge::OnInitializing()(45) > value:
virtual result HogeHoge::OnInitializing()(50) > value:tmp_hoge_value
virtual result HogeHoge::OnInitializing()(50) > value:tmp_hoge_value