From 4248fb0083e2e0f2f9b379ea5ce898036b900218 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Sun, 29 Mar 2009 22:05:50 -0700 Subject: reconcile w/p4 --- Android.mk | 26 +- AndroidManifest.xml | 48 + PinyinIME/Android.mk | 25 - PinyinIME/AndroidManifest.xml | 48 - PinyinIME/jni/Android.mk | 32 - ...oid_inputmethod_pinyin_PinyinDecoderService.cpp | 419 ---- PinyinIME/jni/command/Makefile | 33 - PinyinIME/jni/command/pinyinime_dictbuilder.cpp | 56 - PinyinIME/jni/data/rawdict_utf16_65105_freq.txt | Bin 3570346 -> 0 bytes PinyinIME/jni/data/valid_utf16.txt | Bin 32934 -> 0 bytes PinyinIME/jni/include/atomdictbase.h | 269 --- PinyinIME/jni/include/dictbuilder.h | 171 -- PinyinIME/jni/include/dictdef.h | 157 -- PinyinIME/jni/include/dictlist.h | 120 -- PinyinIME/jni/include/dicttrie.h | 233 -- PinyinIME/jni/include/lpicache.h | 62 - PinyinIME/jni/include/matrixsearch.h | 453 ---- PinyinIME/jni/include/mystdlib.h | 32 - PinyinIME/jni/include/ngram.h | 96 - PinyinIME/jni/include/pinyinime.h | 211 -- PinyinIME/jni/include/searchutility.h | 142 -- PinyinIME/jni/include/spellingtable.h | 111 - PinyinIME/jni/include/spellingtrie.h | 258 --- PinyinIME/jni/include/splparser.h | 96 - PinyinIME/jni/include/sync.h | 85 - PinyinIME/jni/include/userdict.h | 428 ---- PinyinIME/jni/include/utf16char.h | 56 - PinyinIME/jni/include/utf16reader.h | 48 - PinyinIME/jni/share/dictbuilder.cpp | 1080 ---------- PinyinIME/jni/share/dictlist.cpp | 446 ---- PinyinIME/jni/share/dicttrie.cpp | 942 -------- PinyinIME/jni/share/lpicache.cpp | 81 - PinyinIME/jni/share/matrixsearch.cpp | 1895 ---------------- PinyinIME/jni/share/mystdlib.cpp | 34 - PinyinIME/jni/share/ngram.cpp | 342 --- PinyinIME/jni/share/pinyinime.cpp | 186 -- PinyinIME/jni/share/searchutility.cpp | 210 -- PinyinIME/jni/share/spellingtable.cpp | 313 --- PinyinIME/jni/share/spellingtrie.cpp | 828 ------- PinyinIME/jni/share/splparser.cpp | 341 --- PinyinIME/jni/share/sync.cpp | 112 - PinyinIME/jni/share/userdict.cpp | 2262 -------------------- PinyinIME/jni/share/utf16char.cpp | 182 -- PinyinIME/jni/share/utf16reader.cpp | 131 -- PinyinIME/lib/Android.mk | 10 - .../inputmethod/pinyin/IPinyinDecoderService.aidl | 51 - PinyinIME/res/drawable/app_icon.png | Bin 4707 -> 0 bytes PinyinIME/res/drawable/arrow_bg.xml | 21 - PinyinIME/res/drawable/arrow_left.png | Bin 362 -> 0 bytes PinyinIME/res/drawable/arrow_right.png | Bin 365 -> 0 bytes PinyinIME/res/drawable/candidate_balloon_bg.9.png | Bin 593 -> 0 bytes PinyinIME/res/drawable/candidate_hl_bg.9.png | Bin 784 -> 0 bytes PinyinIME/res/drawable/candidates_area_bg.9.png | Bin 198 -> 0 bytes .../res/drawable/candidates_vertical_line.png | Bin 130 -> 0 bytes PinyinIME/res/drawable/cands_container_bg.9.png | Bin 151 -> 0 bytes PinyinIME/res/drawable/comma_full_icon.png | Bin 170 -> 0 bytes PinyinIME/res/drawable/comma_full_popup_icon.png | Bin 226 -> 0 bytes PinyinIME/res/drawable/composing_area_bg.9.png | Bin 353 -> 0 bytes PinyinIME/res/drawable/composing_area_cursor.png | Bin 140 -> 0 bytes PinyinIME/res/drawable/composing_hl_bg.9.png | Bin 711 -> 0 bytes PinyinIME/res/drawable/delete_icon.png | Bin 1366 -> 0 bytes PinyinIME/res/drawable/delete_popup_icon.png | Bin 524 -> 0 bytes PinyinIME/res/drawable/dun_icon.png | Bin 175 -> 0 bytes PinyinIME/res/drawable/dun_popup_icon.png | Bin 252 -> 0 bytes PinyinIME/res/drawable/emotion_icon_00.png | Bin 322 -> 0 bytes PinyinIME/res/drawable/emotion_icon_00_popup.png | Bin 477 -> 0 bytes PinyinIME/res/drawable/emotion_icon_01.png | Bin 342 -> 0 bytes PinyinIME/res/drawable/emotion_icon_01_popup.png | Bin 531 -> 0 bytes PinyinIME/res/drawable/emotion_icon_02.png | Bin 336 -> 0 bytes PinyinIME/res/drawable/emotion_icon_02_popup.png | Bin 486 -> 0 bytes PinyinIME/res/drawable/emotion_icon_03.png | Bin 384 -> 0 bytes PinyinIME/res/drawable/emotion_icon_03_popup.png | Bin 494 -> 0 bytes PinyinIME/res/drawable/emotion_icon_04.png | Bin 344 -> 0 bytes PinyinIME/res/drawable/emotion_icon_04_popup.png | Bin 444 -> 0 bytes PinyinIME/res/drawable/emotion_icon_05.png | Bin 499 -> 0 bytes PinyinIME/res/drawable/emotion_icon_05_popup.png | Bin 657 -> 0 bytes PinyinIME/res/drawable/emotion_icon_06.png | Bin 333 -> 0 bytes PinyinIME/res/drawable/emotion_icon_06_popup.png | Bin 492 -> 0 bytes PinyinIME/res/drawable/emotion_icon_10.png | Bin 342 -> 0 bytes PinyinIME/res/drawable/emotion_icon_10_popup.png | Bin 481 -> 0 bytes PinyinIME/res/drawable/emotion_icon_11.png | Bin 374 -> 0 bytes PinyinIME/res/drawable/emotion_icon_11_popup.png | Bin 557 -> 0 bytes PinyinIME/res/drawable/emotion_icon_12.png | Bin 365 -> 0 bytes PinyinIME/res/drawable/emotion_icon_12_popup.png | Bin 534 -> 0 bytes PinyinIME/res/drawable/emotion_icon_13.png | Bin 371 -> 0 bytes PinyinIME/res/drawable/emotion_icon_13_popup.png | Bin 583 -> 0 bytes PinyinIME/res/drawable/emotion_icon_14.png | Bin 195 -> 0 bytes PinyinIME/res/drawable/emotion_icon_14_popup.png | Bin 229 -> 0 bytes PinyinIME/res/drawable/emotion_icon_15.png | Bin 260 -> 0 bytes PinyinIME/res/drawable/emotion_icon_15_popup.png | Bin 291 -> 0 bytes PinyinIME/res/drawable/emotion_icon_16.png | Bin 230 -> 0 bytes PinyinIME/res/drawable/emotion_icon_16_popup.png | Bin 295 -> 0 bytes PinyinIME/res/drawable/emotion_icon_20.png | Bin 199 -> 0 bytes PinyinIME/res/drawable/emotion_icon_20_popup.png | Bin 251 -> 0 bytes PinyinIME/res/drawable/emotion_icon_21.png | Bin 468 -> 0 bytes PinyinIME/res/drawable/emotion_icon_21_popup.png | Bin 731 -> 0 bytes PinyinIME/res/drawable/emotion_icon_22.png | Bin 328 -> 0 bytes PinyinIME/res/drawable/emotion_icon_22_popup.png | Bin 421 -> 0 bytes PinyinIME/res/drawable/emotion_icon_23.png | Bin 225 -> 0 bytes PinyinIME/res/drawable/emotion_icon_23_popup.png | Bin 257 -> 0 bytes PinyinIME/res/drawable/emotion_icon_24.png | Bin 428 -> 0 bytes PinyinIME/res/drawable/emotion_icon_24_popup.png | Bin 540 -> 0 bytes PinyinIME/res/drawable/enter_icon.png | Bin 866 -> 0 bytes PinyinIME/res/drawable/enter_popup_icon.png | Bin 381 -> 0 bytes PinyinIME/res/drawable/ime_en.png | Bin 908 -> 0 bytes PinyinIME/res/drawable/ime_pinyin.png | Bin 969 -> 0 bytes PinyinIME/res/drawable/key_balloon_bg.9.png | Bin 737 -> 0 bytes PinyinIME/res/drawable/light_key_bg.9.png | Bin 860 -> 0 bytes PinyinIME/res/drawable/light_key_hl_bg.9.png | Bin 836 -> 0 bytes PinyinIME/res/drawable/light_key_up_bg.9.png | Bin 926 -> 0 bytes PinyinIME/res/drawable/light_key_up_hl_bg.9.png | Bin 886 -> 0 bytes PinyinIME/res/drawable/miniskb_bg.9.png | Bin 516 -> 0 bytes PinyinIME/res/drawable/normal_key_bg.9.png | Bin 726 -> 0 bytes PinyinIME/res/drawable/normal_key_hl_bg.9.png | Bin 664 -> 0 bytes PinyinIME/res/drawable/num0.png | Bin 1160 -> 0 bytes PinyinIME/res/drawable/num1.png | Bin 506 -> 0 bytes PinyinIME/res/drawable/num2.png | Bin 1778 -> 0 bytes PinyinIME/res/drawable/num3.png | Bin 1676 -> 0 bytes PinyinIME/res/drawable/num4.png | Bin 1540 -> 0 bytes PinyinIME/res/drawable/num5.png | Bin 1417 -> 0 bytes PinyinIME/res/drawable/num6.png | Bin 1952 -> 0 bytes PinyinIME/res/drawable/num7.png | Bin 1997 -> 0 bytes PinyinIME/res/drawable/num8.png | Bin 1605 -> 0 bytes PinyinIME/res/drawable/num9.png | Bin 2173 -> 0 bytes PinyinIME/res/drawable/numalt.png | Bin 1673 -> 0 bytes PinyinIME/res/drawable/numpound.png | Bin 963 -> 0 bytes PinyinIME/res/drawable/numstar.png | Bin 954 -> 0 bytes PinyinIME/res/drawable/period_full_icon.png | Bin 182 -> 0 bytes PinyinIME/res/drawable/period_full_popup_icon.png | Bin 233 -> 0 bytes PinyinIME/res/drawable/period_icon.png | Bin 154 -> 0 bytes PinyinIME/res/drawable/period_popup_icon.png | Bin 183 -> 0 bytes PinyinIME/res/drawable/search_icon.png | Bin 1029 -> 0 bytes PinyinIME/res/drawable/search_popup_icon.png | Bin 501 -> 0 bytes PinyinIME/res/drawable/shift_off_icon.png | Bin 1017 -> 0 bytes PinyinIME/res/drawable/shift_off_popup_icon.png | Bin 437 -> 0 bytes PinyinIME/res/drawable/shift_on_icon.png | Bin 799 -> 0 bytes PinyinIME/res/drawable/shift_on_popup_icon.png | Bin 333 -> 0 bytes PinyinIME/res/drawable/skb_bg.png | Bin 128 -> 0 bytes PinyinIME/res/drawable/skb_container_bg.9.png | Bin 156 -> 0 bytes PinyinIME/res/drawable/smiley_icon.png | Bin 668 -> 0 bytes PinyinIME/res/drawable/smiley_popup_icon.png | Bin 981 -> 0 bytes PinyinIME/res/drawable/space_icon.png | Bin 424 -> 0 bytes PinyinIME/res/drawable/space_popup_icon.png | Bin 223 -> 0 bytes PinyinIME/res/layout/candidates_container.xml | 53 - PinyinIME/res/layout/floating_container.xml | 27 - PinyinIME/res/layout/skb_container.xml | 37 - PinyinIME/res/raw/dict_pinyin.dat | Bin 1068442 -> 0 bytes PinyinIME/res/values-land/dimens.xml | 19 - PinyinIME/res/values-port/dimens.xml | 19 - PinyinIME/res/values-zh/bools.xml | 18 - PinyinIME/res/values/colors.xml | 27 - PinyinIME/res/values/dimens.xml | 18 - PinyinIME/res/values/strings.xml | 61 - PinyinIME/res/xml/method.xml | 21 - PinyinIME/res/xml/settings.xml | 55 - PinyinIME/res/xml/skb_phone.xml | 75 - PinyinIME/res/xml/skb_qwerty.xml | 92 - PinyinIME/res/xml/skb_smiley.xml | 85 - PinyinIME/res/xml/skb_sym1.xml | 88 - PinyinIME/res/xml/skb_sym2.xml | 68 - PinyinIME/res/xml/skb_template1.xml | 123 -- .../android/inputmethod/pinyin/BalloonHint.java | 472 ---- .../android/inputmethod/pinyin/CandidateView.java | 760 ------- .../inputmethod/pinyin/CandidateViewListener.java | 33 - .../inputmethod/pinyin/CandidatesContainer.java | 474 ---- .../android/inputmethod/pinyin/ComposingView.java | 279 --- .../inputmethod/pinyin/EnglishInputProcessor.java | 95 - .../android/inputmethod/pinyin/Environment.java | 221 -- .../inputmethod/pinyin/InputModeSwitcher.java | 821 ------- .../android/inputmethod/pinyin/KeyMapDream.java | 106 - .../inputmethod/pinyin/PinyinDecoderService.java | 326 --- .../com/android/inputmethod/pinyin/PinyinIME.java | 2081 ------------------ .../com/android/inputmethod/pinyin/Settings.java | 101 - .../inputmethod/pinyin/SettingsActivity.java | 115 - .../android/inputmethod/pinyin/SkbContainer.java | 642 ------ .../com/android/inputmethod/pinyin/SkbPool.java | 88 - .../android/inputmethod/pinyin/SkbTemplate.java | 233 -- .../com/android/inputmethod/pinyin/SoftKey.java | 230 -- .../android/inputmethod/pinyin/SoftKeyToggle.java | 283 --- .../android/inputmethod/pinyin/SoftKeyboard.java | 520 ----- .../inputmethod/pinyin/SoftKeyboardView.java | 478 ----- .../android/inputmethod/pinyin/SoundManager.java | 63 - .../inputmethod/pinyin/XmlKeyboardLoader.java | 835 -------- jni/Android.mk | 32 + ...oid_inputmethod_pinyin_PinyinDecoderService.cpp | 441 ++++ jni/command/Makefile | 33 + jni/command/pinyinime_dictbuilder.cpp | 56 + jni/data/rawdict_utf16_65105_freq.txt | Bin 0 -> 3570346 bytes jni/data/valid_utf16.txt | Bin 0 -> 32934 bytes jni/include/atomdictbase.h | 269 +++ jni/include/dictbuilder.h | 171 ++ jni/include/dictdef.h | 157 ++ jni/include/dictlist.h | 120 ++ jni/include/dicttrie.h | 233 ++ jni/include/lpicache.h | 62 + jni/include/matrixsearch.h | 456 ++++ jni/include/mystdlib.h | 32 + jni/include/ngram.h | 96 + jni/include/pinyinime.h | 211 ++ jni/include/searchutility.h | 142 ++ jni/include/spellingtable.h | 111 + jni/include/spellingtrie.h | 258 +++ jni/include/splparser.h | 96 + jni/include/sync.h | 85 + jni/include/userdict.h | 428 ++++ jni/include/utf16char.h | 56 + jni/include/utf16reader.h | 48 + jni/share/dictbuilder.cpp | 1070 +++++++++ jni/share/dictlist.cpp | 446 ++++ jni/share/dicttrie.cpp | 942 ++++++++ jni/share/lpicache.cpp | 81 + jni/share/matrixsearch.cpp | 1958 +++++++++++++++++ jni/share/mystdlib.cpp | 34 + jni/share/ngram.cpp | 342 +++ jni/share/pinyinime.cpp | 186 ++ jni/share/searchutility.cpp | 210 ++ jni/share/spellingtable.cpp | 313 +++ jni/share/spellingtrie.cpp | 828 +++++++ jni/share/splparser.cpp | 341 +++ jni/share/sync.cpp | 112 + jni/share/userdict.cpp | 2262 ++++++++++++++++++++ jni/share/utf16char.cpp | 182 ++ jni/share/utf16reader.cpp | 131 ++ lib/Android.mk | 10 + .../inputmethod/pinyin/IPinyinDecoderService.aidl | 51 + res/drawable/app_icon.png | Bin 0 -> 4707 bytes res/drawable/arrow_bg.xml | 21 + res/drawable/arrow_left.png | Bin 0 -> 362 bytes res/drawable/arrow_right.png | Bin 0 -> 365 bytes res/drawable/candidate_balloon_bg.9.png | Bin 0 -> 593 bytes res/drawable/candidate_hl_bg.9.png | Bin 0 -> 784 bytes res/drawable/candidates_area_bg.9.png | Bin 0 -> 198 bytes res/drawable/candidates_vertical_line.png | Bin 0 -> 130 bytes res/drawable/cands_container_bg.9.png | Bin 0 -> 151 bytes res/drawable/comma_full_icon.png | Bin 0 -> 170 bytes res/drawable/comma_full_popup_icon.png | Bin 0 -> 226 bytes res/drawable/composing_area_bg.9.png | Bin 0 -> 353 bytes res/drawable/composing_area_cursor.png | Bin 0 -> 140 bytes res/drawable/composing_hl_bg.9.png | Bin 0 -> 711 bytes res/drawable/delete_icon.png | Bin 0 -> 1366 bytes res/drawable/delete_popup_icon.png | Bin 0 -> 524 bytes res/drawable/dun_icon.png | Bin 0 -> 175 bytes res/drawable/dun_popup_icon.png | Bin 0 -> 252 bytes res/drawable/emotion_icon_00.png | Bin 0 -> 322 bytes res/drawable/emotion_icon_00_popup.png | Bin 0 -> 477 bytes res/drawable/emotion_icon_01.png | Bin 0 -> 342 bytes res/drawable/emotion_icon_01_popup.png | Bin 0 -> 531 bytes res/drawable/emotion_icon_02.png | Bin 0 -> 336 bytes res/drawable/emotion_icon_02_popup.png | Bin 0 -> 486 bytes res/drawable/emotion_icon_03.png | Bin 0 -> 384 bytes res/drawable/emotion_icon_03_popup.png | Bin 0 -> 494 bytes res/drawable/emotion_icon_04.png | Bin 0 -> 344 bytes res/drawable/emotion_icon_04_popup.png | Bin 0 -> 444 bytes res/drawable/emotion_icon_05.png | Bin 0 -> 499 bytes res/drawable/emotion_icon_05_popup.png | Bin 0 -> 657 bytes res/drawable/emotion_icon_06.png | Bin 0 -> 333 bytes res/drawable/emotion_icon_06_popup.png | Bin 0 -> 492 bytes res/drawable/emotion_icon_10.png | Bin 0 -> 342 bytes res/drawable/emotion_icon_10_popup.png | Bin 0 -> 481 bytes res/drawable/emotion_icon_11.png | Bin 0 -> 374 bytes res/drawable/emotion_icon_11_popup.png | Bin 0 -> 557 bytes res/drawable/emotion_icon_12.png | Bin 0 -> 365 bytes res/drawable/emotion_icon_12_popup.png | Bin 0 -> 534 bytes res/drawable/emotion_icon_13.png | Bin 0 -> 371 bytes res/drawable/emotion_icon_13_popup.png | Bin 0 -> 583 bytes res/drawable/emotion_icon_14.png | Bin 0 -> 195 bytes res/drawable/emotion_icon_14_popup.png | Bin 0 -> 229 bytes res/drawable/emotion_icon_15.png | Bin 0 -> 260 bytes res/drawable/emotion_icon_15_popup.png | Bin 0 -> 291 bytes res/drawable/emotion_icon_16.png | Bin 0 -> 230 bytes res/drawable/emotion_icon_16_popup.png | Bin 0 -> 295 bytes res/drawable/emotion_icon_20.png | Bin 0 -> 199 bytes res/drawable/emotion_icon_20_popup.png | Bin 0 -> 251 bytes res/drawable/emotion_icon_21.png | Bin 0 -> 468 bytes res/drawable/emotion_icon_21_popup.png | Bin 0 -> 731 bytes res/drawable/emotion_icon_22.png | Bin 0 -> 328 bytes res/drawable/emotion_icon_22_popup.png | Bin 0 -> 421 bytes res/drawable/emotion_icon_23.png | Bin 0 -> 225 bytes res/drawable/emotion_icon_23_popup.png | Bin 0 -> 257 bytes res/drawable/emotion_icon_24.png | Bin 0 -> 428 bytes res/drawable/emotion_icon_24_popup.png | Bin 0 -> 540 bytes res/drawable/enter_icon.png | Bin 0 -> 866 bytes res/drawable/enter_popup_icon.png | Bin 0 -> 381 bytes res/drawable/ime_en.png | Bin 0 -> 908 bytes res/drawable/ime_pinyin.png | Bin 0 -> 969 bytes res/drawable/key_balloon_bg.9.png | Bin 0 -> 737 bytes res/drawable/light_key_bg.9.png | Bin 0 -> 860 bytes res/drawable/light_key_hl_bg.9.png | Bin 0 -> 836 bytes res/drawable/light_key_up_bg.9.png | Bin 0 -> 926 bytes res/drawable/light_key_up_hl_bg.9.png | Bin 0 -> 886 bytes res/drawable/miniskb_bg.9.png | Bin 0 -> 516 bytes res/drawable/normal_key_bg.9.png | Bin 0 -> 726 bytes res/drawable/normal_key_hl_bg.9.png | Bin 0 -> 664 bytes res/drawable/num0.png | Bin 0 -> 1160 bytes res/drawable/num1.png | Bin 0 -> 506 bytes res/drawable/num2.png | Bin 0 -> 1778 bytes res/drawable/num3.png | Bin 0 -> 1676 bytes res/drawable/num4.png | Bin 0 -> 1540 bytes res/drawable/num5.png | Bin 0 -> 1417 bytes res/drawable/num6.png | Bin 0 -> 1952 bytes res/drawable/num7.png | Bin 0 -> 1997 bytes res/drawable/num8.png | Bin 0 -> 1605 bytes res/drawable/num9.png | Bin 0 -> 2173 bytes res/drawable/numalt.png | Bin 0 -> 1673 bytes res/drawable/numpound.png | Bin 0 -> 963 bytes res/drawable/numstar.png | Bin 0 -> 954 bytes res/drawable/period_full_icon.png | Bin 0 -> 182 bytes res/drawable/period_full_popup_icon.png | Bin 0 -> 233 bytes res/drawable/period_icon.png | Bin 0 -> 154 bytes res/drawable/period_popup_icon.png | Bin 0 -> 183 bytes res/drawable/search_icon.png | Bin 0 -> 1029 bytes res/drawable/search_popup_icon.png | Bin 0 -> 501 bytes res/drawable/shift_off_icon.png | Bin 0 -> 1017 bytes res/drawable/shift_off_popup_icon.png | Bin 0 -> 437 bytes res/drawable/shift_on_icon.png | Bin 0 -> 799 bytes res/drawable/shift_on_popup_icon.png | Bin 0 -> 333 bytes res/drawable/skb_bg.png | Bin 0 -> 128 bytes res/drawable/skb_container_bg.9.png | Bin 0 -> 156 bytes res/drawable/smiley_icon.png | Bin 0 -> 668 bytes res/drawable/smiley_popup_icon.png | Bin 0 -> 981 bytes res/drawable/space_icon.png | Bin 0 -> 424 bytes res/drawable/space_popup_icon.png | Bin 0 -> 223 bytes res/layout/candidates_container.xml | 53 + res/layout/floating_container.xml | 27 + res/layout/skb_container.xml | 37 + res/raw/dict_pinyin.dat | Bin 0 -> 1068442 bytes res/values-land/dimens.xml | 19 + res/values-port/dimens.xml | 19 + res/values-zh/bools.xml | 18 + res/values/colors.xml | 27 + res/values/dimens.xml | 18 + res/values/strings.xml | 61 + res/xml/method.xml | 21 + res/xml/settings.xml | 55 + res/xml/skb_phone.xml | 75 + res/xml/skb_qwerty.xml | 92 + res/xml/skb_smiley.xml | 85 + res/xml/skb_sym1.xml | 88 + res/xml/skb_sym2.xml | 68 + res/xml/skb_template1.xml | 123 ++ .../android/inputmethod/pinyin/BalloonHint.java | 472 ++++ .../android/inputmethod/pinyin/CandidateView.java | 760 +++++++ .../inputmethod/pinyin/CandidateViewListener.java | 33 + .../inputmethod/pinyin/CandidatesContainer.java | 474 ++++ .../android/inputmethod/pinyin/ComposingView.java | 280 +++ .../inputmethod/pinyin/EnglishInputProcessor.java | 95 + .../android/inputmethod/pinyin/Environment.java | 221 ++ .../inputmethod/pinyin/InputModeSwitcher.java | 825 +++++++ .../android/inputmethod/pinyin/KeyMapDream.java | 106 + .../inputmethod/pinyin/PinyinDecoderService.java | 326 +++ src/com/android/inputmethod/pinyin/PinyinIME.java | 2134 ++++++++++++++++++ src/com/android/inputmethod/pinyin/Settings.java | 101 + .../inputmethod/pinyin/SettingsActivity.java | 115 + .../android/inputmethod/pinyin/SkbContainer.java | 642 ++++++ src/com/android/inputmethod/pinyin/SkbPool.java | 88 + .../android/inputmethod/pinyin/SkbTemplate.java | 233 ++ src/com/android/inputmethod/pinyin/SoftKey.java | 230 ++ .../android/inputmethod/pinyin/SoftKeyToggle.java | 283 +++ .../android/inputmethod/pinyin/SoftKeyboard.java | 520 +++++ .../inputmethod/pinyin/SoftKeyboardView.java | 478 +++++ .../android/inputmethod/pinyin/SoundManager.java | 63 + .../inputmethod/pinyin/XmlKeyboardLoader.java | 835 ++++++++ 362 files changed, 23386 insertions(+), 23251 deletions(-) create mode 100644 AndroidManifest.xml delete mode 100644 PinyinIME/Android.mk delete mode 100644 PinyinIME/AndroidManifest.xml delete mode 100644 PinyinIME/jni/Android.mk delete mode 100644 PinyinIME/jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp delete mode 100644 PinyinIME/jni/command/Makefile delete mode 100644 PinyinIME/jni/command/pinyinime_dictbuilder.cpp delete mode 100644 PinyinIME/jni/data/rawdict_utf16_65105_freq.txt delete mode 100644 PinyinIME/jni/data/valid_utf16.txt delete mode 100644 PinyinIME/jni/include/atomdictbase.h delete mode 100644 PinyinIME/jni/include/dictbuilder.h delete mode 100644 PinyinIME/jni/include/dictdef.h delete mode 100644 PinyinIME/jni/include/dictlist.h delete mode 100644 PinyinIME/jni/include/dicttrie.h delete mode 100644 PinyinIME/jni/include/lpicache.h delete mode 100644 PinyinIME/jni/include/matrixsearch.h delete mode 100644 PinyinIME/jni/include/mystdlib.h delete mode 100644 PinyinIME/jni/include/ngram.h delete mode 100644 PinyinIME/jni/include/pinyinime.h delete mode 100644 PinyinIME/jni/include/searchutility.h delete mode 100644 PinyinIME/jni/include/spellingtable.h delete mode 100644 PinyinIME/jni/include/spellingtrie.h delete mode 100644 PinyinIME/jni/include/splparser.h delete mode 100644 PinyinIME/jni/include/sync.h delete mode 100644 PinyinIME/jni/include/userdict.h delete mode 100644 PinyinIME/jni/include/utf16char.h delete mode 100644 PinyinIME/jni/include/utf16reader.h delete mode 100644 PinyinIME/jni/share/dictbuilder.cpp delete mode 100644 PinyinIME/jni/share/dictlist.cpp delete mode 100644 PinyinIME/jni/share/dicttrie.cpp delete mode 100644 PinyinIME/jni/share/lpicache.cpp delete mode 100644 PinyinIME/jni/share/matrixsearch.cpp delete mode 100644 PinyinIME/jni/share/mystdlib.cpp delete mode 100644 PinyinIME/jni/share/ngram.cpp delete mode 100644 PinyinIME/jni/share/pinyinime.cpp delete mode 100644 PinyinIME/jni/share/searchutility.cpp delete mode 100644 PinyinIME/jni/share/spellingtable.cpp delete mode 100644 PinyinIME/jni/share/spellingtrie.cpp delete mode 100644 PinyinIME/jni/share/splparser.cpp delete mode 100644 PinyinIME/jni/share/sync.cpp delete mode 100644 PinyinIME/jni/share/userdict.cpp delete mode 100644 PinyinIME/jni/share/utf16char.cpp delete mode 100644 PinyinIME/jni/share/utf16reader.cpp delete mode 100644 PinyinIME/lib/Android.mk delete mode 100644 PinyinIME/lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl delete mode 100644 PinyinIME/res/drawable/app_icon.png delete mode 100644 PinyinIME/res/drawable/arrow_bg.xml delete mode 100644 PinyinIME/res/drawable/arrow_left.png delete mode 100644 PinyinIME/res/drawable/arrow_right.png delete mode 100644 PinyinIME/res/drawable/candidate_balloon_bg.9.png delete mode 100644 PinyinIME/res/drawable/candidate_hl_bg.9.png delete mode 100644 PinyinIME/res/drawable/candidates_area_bg.9.png delete mode 100644 PinyinIME/res/drawable/candidates_vertical_line.png delete mode 100644 PinyinIME/res/drawable/cands_container_bg.9.png delete mode 100644 PinyinIME/res/drawable/comma_full_icon.png delete mode 100644 PinyinIME/res/drawable/comma_full_popup_icon.png delete mode 100644 PinyinIME/res/drawable/composing_area_bg.9.png delete mode 100644 PinyinIME/res/drawable/composing_area_cursor.png delete mode 100644 PinyinIME/res/drawable/composing_hl_bg.9.png delete mode 100644 PinyinIME/res/drawable/delete_icon.png delete mode 100644 PinyinIME/res/drawable/delete_popup_icon.png delete mode 100644 PinyinIME/res/drawable/dun_icon.png delete mode 100644 PinyinIME/res/drawable/dun_popup_icon.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_00.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_00_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_01.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_01_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_02.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_02_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_03.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_03_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_04.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_04_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_05.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_05_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_06.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_06_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_10.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_10_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_11.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_11_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_12.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_12_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_13.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_13_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_14.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_14_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_15.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_15_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_16.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_16_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_20.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_20_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_21.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_21_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_22.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_22_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_23.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_23_popup.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_24.png delete mode 100644 PinyinIME/res/drawable/emotion_icon_24_popup.png delete mode 100644 PinyinIME/res/drawable/enter_icon.png delete mode 100644 PinyinIME/res/drawable/enter_popup_icon.png delete mode 100644 PinyinIME/res/drawable/ime_en.png delete mode 100644 PinyinIME/res/drawable/ime_pinyin.png delete mode 100644 PinyinIME/res/drawable/key_balloon_bg.9.png delete mode 100644 PinyinIME/res/drawable/light_key_bg.9.png delete mode 100644 PinyinIME/res/drawable/light_key_hl_bg.9.png delete mode 100644 PinyinIME/res/drawable/light_key_up_bg.9.png delete mode 100644 PinyinIME/res/drawable/light_key_up_hl_bg.9.png delete mode 100644 PinyinIME/res/drawable/miniskb_bg.9.png delete mode 100644 PinyinIME/res/drawable/normal_key_bg.9.png delete mode 100644 PinyinIME/res/drawable/normal_key_hl_bg.9.png delete mode 100644 PinyinIME/res/drawable/num0.png delete mode 100644 PinyinIME/res/drawable/num1.png delete mode 100644 PinyinIME/res/drawable/num2.png delete mode 100644 PinyinIME/res/drawable/num3.png delete mode 100644 PinyinIME/res/drawable/num4.png delete mode 100644 PinyinIME/res/drawable/num5.png delete mode 100644 PinyinIME/res/drawable/num6.png delete mode 100644 PinyinIME/res/drawable/num7.png delete mode 100644 PinyinIME/res/drawable/num8.png delete mode 100644 PinyinIME/res/drawable/num9.png delete mode 100644 PinyinIME/res/drawable/numalt.png delete mode 100644 PinyinIME/res/drawable/numpound.png delete mode 100644 PinyinIME/res/drawable/numstar.png delete mode 100644 PinyinIME/res/drawable/period_full_icon.png delete mode 100644 PinyinIME/res/drawable/period_full_popup_icon.png delete mode 100644 PinyinIME/res/drawable/period_icon.png delete mode 100644 PinyinIME/res/drawable/period_popup_icon.png delete mode 100644 PinyinIME/res/drawable/search_icon.png delete mode 100644 PinyinIME/res/drawable/search_popup_icon.png delete mode 100644 PinyinIME/res/drawable/shift_off_icon.png delete mode 100644 PinyinIME/res/drawable/shift_off_popup_icon.png delete mode 100644 PinyinIME/res/drawable/shift_on_icon.png delete mode 100644 PinyinIME/res/drawable/shift_on_popup_icon.png delete mode 100644 PinyinIME/res/drawable/skb_bg.png delete mode 100644 PinyinIME/res/drawable/skb_container_bg.9.png delete mode 100644 PinyinIME/res/drawable/smiley_icon.png delete mode 100644 PinyinIME/res/drawable/smiley_popup_icon.png delete mode 100644 PinyinIME/res/drawable/space_icon.png delete mode 100644 PinyinIME/res/drawable/space_popup_icon.png delete mode 100644 PinyinIME/res/layout/candidates_container.xml delete mode 100644 PinyinIME/res/layout/floating_container.xml delete mode 100644 PinyinIME/res/layout/skb_container.xml delete mode 100644 PinyinIME/res/raw/dict_pinyin.dat delete mode 100644 PinyinIME/res/values-land/dimens.xml delete mode 100644 PinyinIME/res/values-port/dimens.xml delete mode 100644 PinyinIME/res/values-zh/bools.xml delete mode 100644 PinyinIME/res/values/colors.xml delete mode 100644 PinyinIME/res/values/dimens.xml delete mode 100644 PinyinIME/res/values/strings.xml delete mode 100644 PinyinIME/res/xml/method.xml delete mode 100644 PinyinIME/res/xml/settings.xml delete mode 100644 PinyinIME/res/xml/skb_phone.xml delete mode 100644 PinyinIME/res/xml/skb_qwerty.xml delete mode 100644 PinyinIME/res/xml/skb_smiley.xml delete mode 100644 PinyinIME/res/xml/skb_sym1.xml delete mode 100644 PinyinIME/res/xml/skb_sym2.xml delete mode 100644 PinyinIME/res/xml/skb_template1.xml delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/BalloonHint.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/CandidateView.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/CandidateViewListener.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/CandidatesContainer.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/ComposingView.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/EnglishInputProcessor.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/Environment.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/InputModeSwitcher.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/KeyMapDream.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/PinyinDecoderService.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/PinyinIME.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/Settings.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SettingsActivity.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SkbContainer.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SkbPool.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SkbTemplate.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SoftKey.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyToggle.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboard.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboardView.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/SoundManager.java delete mode 100644 PinyinIME/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java create mode 100644 jni/Android.mk create mode 100644 jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp create mode 100644 jni/command/Makefile create mode 100644 jni/command/pinyinime_dictbuilder.cpp create mode 100644 jni/data/rawdict_utf16_65105_freq.txt create mode 100644 jni/data/valid_utf16.txt create mode 100644 jni/include/atomdictbase.h create mode 100644 jni/include/dictbuilder.h create mode 100644 jni/include/dictdef.h create mode 100644 jni/include/dictlist.h create mode 100644 jni/include/dicttrie.h create mode 100644 jni/include/lpicache.h create mode 100644 jni/include/matrixsearch.h create mode 100644 jni/include/mystdlib.h create mode 100644 jni/include/ngram.h create mode 100644 jni/include/pinyinime.h create mode 100644 jni/include/searchutility.h create mode 100644 jni/include/spellingtable.h create mode 100644 jni/include/spellingtrie.h create mode 100644 jni/include/splparser.h create mode 100644 jni/include/sync.h create mode 100644 jni/include/userdict.h create mode 100644 jni/include/utf16char.h create mode 100644 jni/include/utf16reader.h create mode 100644 jni/share/dictbuilder.cpp create mode 100644 jni/share/dictlist.cpp create mode 100644 jni/share/dicttrie.cpp create mode 100644 jni/share/lpicache.cpp create mode 100644 jni/share/matrixsearch.cpp create mode 100644 jni/share/mystdlib.cpp create mode 100644 jni/share/ngram.cpp create mode 100644 jni/share/pinyinime.cpp create mode 100644 jni/share/searchutility.cpp create mode 100644 jni/share/spellingtable.cpp create mode 100644 jni/share/spellingtrie.cpp create mode 100644 jni/share/splparser.cpp create mode 100644 jni/share/sync.cpp create mode 100644 jni/share/userdict.cpp create mode 100644 jni/share/utf16char.cpp create mode 100644 jni/share/utf16reader.cpp create mode 100644 lib/Android.mk create mode 100644 lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl create mode 100644 res/drawable/app_icon.png create mode 100644 res/drawable/arrow_bg.xml create mode 100644 res/drawable/arrow_left.png create mode 100644 res/drawable/arrow_right.png create mode 100644 res/drawable/candidate_balloon_bg.9.png create mode 100644 res/drawable/candidate_hl_bg.9.png create mode 100644 res/drawable/candidates_area_bg.9.png create mode 100644 res/drawable/candidates_vertical_line.png create mode 100644 res/drawable/cands_container_bg.9.png create mode 100644 res/drawable/comma_full_icon.png create mode 100644 res/drawable/comma_full_popup_icon.png create mode 100644 res/drawable/composing_area_bg.9.png create mode 100644 res/drawable/composing_area_cursor.png create mode 100644 res/drawable/composing_hl_bg.9.png create mode 100644 res/drawable/delete_icon.png create mode 100644 res/drawable/delete_popup_icon.png create mode 100644 res/drawable/dun_icon.png create mode 100644 res/drawable/dun_popup_icon.png create mode 100644 res/drawable/emotion_icon_00.png create mode 100644 res/drawable/emotion_icon_00_popup.png create mode 100644 res/drawable/emotion_icon_01.png create mode 100644 res/drawable/emotion_icon_01_popup.png create mode 100644 res/drawable/emotion_icon_02.png create mode 100644 res/drawable/emotion_icon_02_popup.png create mode 100644 res/drawable/emotion_icon_03.png create mode 100644 res/drawable/emotion_icon_03_popup.png create mode 100644 res/drawable/emotion_icon_04.png create mode 100644 res/drawable/emotion_icon_04_popup.png create mode 100644 res/drawable/emotion_icon_05.png create mode 100644 res/drawable/emotion_icon_05_popup.png create mode 100644 res/drawable/emotion_icon_06.png create mode 100644 res/drawable/emotion_icon_06_popup.png create mode 100644 res/drawable/emotion_icon_10.png create mode 100644 res/drawable/emotion_icon_10_popup.png create mode 100644 res/drawable/emotion_icon_11.png create mode 100644 res/drawable/emotion_icon_11_popup.png create mode 100644 res/drawable/emotion_icon_12.png create mode 100644 res/drawable/emotion_icon_12_popup.png create mode 100644 res/drawable/emotion_icon_13.png create mode 100644 res/drawable/emotion_icon_13_popup.png create mode 100644 res/drawable/emotion_icon_14.png create mode 100644 res/drawable/emotion_icon_14_popup.png create mode 100644 res/drawable/emotion_icon_15.png create mode 100644 res/drawable/emotion_icon_15_popup.png create mode 100644 res/drawable/emotion_icon_16.png create mode 100644 res/drawable/emotion_icon_16_popup.png create mode 100644 res/drawable/emotion_icon_20.png create mode 100644 res/drawable/emotion_icon_20_popup.png create mode 100644 res/drawable/emotion_icon_21.png create mode 100644 res/drawable/emotion_icon_21_popup.png create mode 100644 res/drawable/emotion_icon_22.png create mode 100644 res/drawable/emotion_icon_22_popup.png create mode 100644 res/drawable/emotion_icon_23.png create mode 100644 res/drawable/emotion_icon_23_popup.png create mode 100644 res/drawable/emotion_icon_24.png create mode 100644 res/drawable/emotion_icon_24_popup.png create mode 100644 res/drawable/enter_icon.png create mode 100644 res/drawable/enter_popup_icon.png create mode 100644 res/drawable/ime_en.png create mode 100644 res/drawable/ime_pinyin.png create mode 100644 res/drawable/key_balloon_bg.9.png create mode 100644 res/drawable/light_key_bg.9.png create mode 100644 res/drawable/light_key_hl_bg.9.png create mode 100644 res/drawable/light_key_up_bg.9.png create mode 100644 res/drawable/light_key_up_hl_bg.9.png create mode 100644 res/drawable/miniskb_bg.9.png create mode 100644 res/drawable/normal_key_bg.9.png create mode 100644 res/drawable/normal_key_hl_bg.9.png create mode 100644 res/drawable/num0.png create mode 100644 res/drawable/num1.png create mode 100644 res/drawable/num2.png create mode 100644 res/drawable/num3.png create mode 100644 res/drawable/num4.png create mode 100644 res/drawable/num5.png create mode 100644 res/drawable/num6.png create mode 100644 res/drawable/num7.png create mode 100644 res/drawable/num8.png create mode 100644 res/drawable/num9.png create mode 100644 res/drawable/numalt.png create mode 100644 res/drawable/numpound.png create mode 100644 res/drawable/numstar.png create mode 100644 res/drawable/period_full_icon.png create mode 100644 res/drawable/period_full_popup_icon.png create mode 100644 res/drawable/period_icon.png create mode 100644 res/drawable/period_popup_icon.png create mode 100644 res/drawable/search_icon.png create mode 100644 res/drawable/search_popup_icon.png create mode 100644 res/drawable/shift_off_icon.png create mode 100644 res/drawable/shift_off_popup_icon.png create mode 100644 res/drawable/shift_on_icon.png create mode 100644 res/drawable/shift_on_popup_icon.png create mode 100644 res/drawable/skb_bg.png create mode 100644 res/drawable/skb_container_bg.9.png create mode 100644 res/drawable/smiley_icon.png create mode 100644 res/drawable/smiley_popup_icon.png create mode 100644 res/drawable/space_icon.png create mode 100644 res/drawable/space_popup_icon.png create mode 100644 res/layout/candidates_container.xml create mode 100644 res/layout/floating_container.xml create mode 100644 res/layout/skb_container.xml create mode 100644 res/raw/dict_pinyin.dat create mode 100644 res/values-land/dimens.xml create mode 100644 res/values-port/dimens.xml create mode 100644 res/values-zh/bools.xml create mode 100644 res/values/colors.xml create mode 100644 res/values/dimens.xml create mode 100644 res/values/strings.xml create mode 100644 res/xml/method.xml create mode 100644 res/xml/settings.xml create mode 100644 res/xml/skb_phone.xml create mode 100644 res/xml/skb_qwerty.xml create mode 100644 res/xml/skb_smiley.xml create mode 100644 res/xml/skb_sym1.xml create mode 100644 res/xml/skb_sym2.xml create mode 100644 res/xml/skb_template1.xml create mode 100644 src/com/android/inputmethod/pinyin/BalloonHint.java create mode 100644 src/com/android/inputmethod/pinyin/CandidateView.java create mode 100644 src/com/android/inputmethod/pinyin/CandidateViewListener.java create mode 100644 src/com/android/inputmethod/pinyin/CandidatesContainer.java create mode 100644 src/com/android/inputmethod/pinyin/ComposingView.java create mode 100644 src/com/android/inputmethod/pinyin/EnglishInputProcessor.java create mode 100644 src/com/android/inputmethod/pinyin/Environment.java create mode 100644 src/com/android/inputmethod/pinyin/InputModeSwitcher.java create mode 100644 src/com/android/inputmethod/pinyin/KeyMapDream.java create mode 100644 src/com/android/inputmethod/pinyin/PinyinDecoderService.java create mode 100644 src/com/android/inputmethod/pinyin/PinyinIME.java create mode 100644 src/com/android/inputmethod/pinyin/Settings.java create mode 100644 src/com/android/inputmethod/pinyin/SettingsActivity.java create mode 100644 src/com/android/inputmethod/pinyin/SkbContainer.java create mode 100644 src/com/android/inputmethod/pinyin/SkbPool.java create mode 100644 src/com/android/inputmethod/pinyin/SkbTemplate.java create mode 100644 src/com/android/inputmethod/pinyin/SoftKey.java create mode 100644 src/com/android/inputmethod/pinyin/SoftKeyToggle.java create mode 100644 src/com/android/inputmethod/pinyin/SoftKeyboard.java create mode 100644 src/com/android/inputmethod/pinyin/SoftKeyboardView.java create mode 100644 src/com/android/inputmethod/pinyin/SoundManager.java create mode 100644 src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java diff --git a/Android.mk b/Android.mk index 5053e7d..64224ae 100644 --- a/Android.mk +++ b/Android.mk @@ -1 +1,25 @@ -include $(call all-subdir-makefiles) +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := PinyinIME + +LOCAL_JNI_SHARED_LIBRARIES := libjni_pinyinime + +LOCAL_STATIC_JAVA_LIBRARIES := com.android.inputmethod.pinyin.lib + +LOCAL_CERTIFICATE := shared + +# Make sure our dictionary file is not compressed, so we can read it with +# a raw file descriptor. +LOCAL_AAPT_FLAGS := -0 .dat + +include $(BUILD_PACKAGE) + +MY_PATH := $(LOCAL_PATH) + +include $(MY_PATH)/jni/Android.mk +include $(MY_PATH)/lib/Android.mk diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..e1ca30d --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PinyinIME/Android.mk b/PinyinIME/Android.mk deleted file mode 100644 index 64224ae..0000000 --- a/PinyinIME/Android.mk +++ /dev/null @@ -1,25 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := PinyinIME - -LOCAL_JNI_SHARED_LIBRARIES := libjni_pinyinime - -LOCAL_STATIC_JAVA_LIBRARIES := com.android.inputmethod.pinyin.lib - -LOCAL_CERTIFICATE := shared - -# Make sure our dictionary file is not compressed, so we can read it with -# a raw file descriptor. -LOCAL_AAPT_FLAGS := -0 .dat - -include $(BUILD_PACKAGE) - -MY_PATH := $(LOCAL_PATH) - -include $(MY_PATH)/jni/Android.mk -include $(MY_PATH)/lib/Android.mk diff --git a/PinyinIME/AndroidManifest.xml b/PinyinIME/AndroidManifest.xml deleted file mode 100644 index e1ca30d..0000000 --- a/PinyinIME/AndroidManifest.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/jni/Android.mk b/PinyinIME/jni/Android.mk deleted file mode 100644 index 141a0b7..0000000 --- a/PinyinIME/jni/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -### shared library - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp \ - share/dictbuilder.cpp \ - share/dictlist.cpp \ - share/dicttrie.cpp \ - share/lpicache.cpp \ - share/matrixsearch.cpp \ - share/mystdlib.cpp \ - share/ngram.cpp \ - share/pinyinime.cpp \ - share/searchutility.cpp \ - share/spellingtable.cpp \ - share/spellingtrie.cpp \ - share/splparser.cpp \ - share/userdict.cpp \ - share/utf16char.cpp \ - share/utf16reader.cpp \ - share/sync.cpp - -LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) -LOCAL_LDLIBS += -lpthread -LOCAL_MODULE := libjni_pinyinime -LOCAL_PRELINK_MODULE := false -LOCAL_SHARED_LIBRARIES := libcutils libutils - -include $(BUILD_SHARED_LIBRARY) diff --git a/PinyinIME/jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp b/PinyinIME/jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp deleted file mode 100644 index 4362c30..0000000 --- a/PinyinIME/jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include - -#include "../include/pinyinime.h" -#include "../include/sync.h" -#include "../include/userdict.h" - -#ifdef __cplusplus -extern "C" { -#endif - -using namespace ime_pinyin; - -#define RET_BUF_LEN 256 - -static char16 retbuf[RET_BUF_LEN]; -static char16 (*predict_buf)[kMaxPredictSize + 1] = NULL; -static size_t predict_len; - -static Sync sync_worker; - -static struct file_descriptor_offsets_t -{ - jclass mClass; - jfieldID mDescriptor; -} gFileDescriptorOffsets; - -JNIEXPORT jboolean JNICALL nativeImOpenDecoder(JNIEnv* env, jclass jclazz, - jbyteArray fn_sys_dict, - jbyteArray fn_usr_dict) { - jbyte *fsd = (*env).GetByteArrayElements(fn_sys_dict, 0); - jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0); - - if (im_open_decoder((const char*)fsd, (const char*)fud)) - return JNI_TRUE; - - return JNI_FALSE; -} - -JNIEXPORT jboolean JNICALL nativeImOpenDecoderFd(JNIEnv* env, jclass jclazz, - jobject fd_sys_dict, - jlong startoffset, - jlong length, - jbyteArray fn_usr_dict) { - jint fd = env->GetIntField(fd_sys_dict, gFileDescriptorOffsets.mDescriptor); - jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0); - - int newfd = dup(fd); - if (im_open_decoder_fd(newfd, startoffset, length, (const char*)fud)) { - close(newfd); - return JNI_TRUE; - } - - close(newfd); - return JNI_FALSE; -} - -JNIEXPORT void JNICALL nativeImSetMaxLens(JNIEnv* env, jclass jclazz, - jint max_sps_len, - jint max_hzs_len) { - im_set_max_lens(static_cast(max_sps_len), - static_cast(max_hzs_len)); - return; -} - -JNIEXPORT jboolean JNICALL nativeImCloseDecoder(JNIEnv* env, jclass jclazz) { - im_close_decoder(); - return JNI_TRUE; -} - -JNIEXPORT jint JNICALL nativeImSearch(JNIEnv* env, jclass jclazz, - jbyteArray pybuf, jint pylen) { - jbyte *array_body = (*env).GetByteArrayElements(pybuf, 0); - - if (NULL == array_body) - return 0; - - return im_search((const char*)array_body, pylen); -} - -JNIEXPORT jint JNICALL nativeImDelSearch(JNIEnv* env, jclass jclazz, jint pos, - jboolean is_pos_in_splid, - jboolean clear_fixed_this_step) { - return im_delsearch(pos, is_pos_in_splid, clear_fixed_this_step); -} - -JNIEXPORT void JNICALL nativeImResetSearch(JNIEnv* env, jclass jclazz) { - im_reset_search(); - return; -} - -JNIEXPORT jint JNICALL nativeImAddLetter(JNIEnv *env, jclass clazz, jbyte ch) { - return im_add_letter(ch); -} - -JNIEXPORT jstring JNICALL nativeImGetPyStr(JNIEnv* env, jclass jclazz, - jboolean decoded) { - size_t py_len; - const char *py = im_get_sps_str(&py_len); // py_len gets decoded length - assert(NULL != py); - if (!decoded) - py_len = strlen(py); - - const unsigned short *spl_start; - size_t len; - len = im_get_spl_start_pos(spl_start); - - size_t i; - for (i = 0; i < py_len; i++) - retbuf[i] = py[i]; - retbuf[i] = (char16)'\0'; - - jstring retstr = (*env).NewString((unsigned short*)retbuf, i); - return retstr; -} - -JNIEXPORT jint JNICALL nativeImGetPyStrLen(JNIEnv* env, jclass jclazz, - jboolean decoded) { - size_t py_len; - const char *py = im_get_sps_str(&py_len); // py_len gets decoded length - assert(NULL != py); - if (!decoded) - py_len = strlen(py); - return py_len; -} - -JNIEXPORT jintArray JNICALL nativeImGetSplStart(JNIEnv* env, jclass jclazz) { - const unsigned short *spl_start; - size_t len; - - // There will be len + 1 elements in the buffer when len > 0. - len = im_get_spl_start_pos(spl_start); - - jintArray arr = (*env).NewIntArray(len + 2); - jint *arr_body = (*env).GetIntArrayElements(arr, 0); - assert(NULL != arr_body); - arr_body[0] = len; // element 0 is used to store the length of buffer. - for (size_t i = 0; i <= len; i++) - arr_body[i + 1] = spl_start[i]; - return arr; -} - -JNIEXPORT jstring JNICALL nativeImGetChoice(JNIEnv *env, jclass clazz, - jint candidateId) { - jstring retstr; - if(im_get_candidate(candidateId, retbuf, RET_BUF_LEN)) { - retstr = (*env).NewString(retbuf, utf16_strlen(retbuf)); - return retstr; - } else { - retstr = (*env).NewString((unsigned short*)retbuf, 0); - return retstr; - } -} - -JNIEXPORT jint JNICALL nativeImChoose(JNIEnv *env, jclass clazz, - jint choice_id) { - return im_choose(choice_id); -} - -JNIEXPORT jint JNICALL nativeImCancelLastChoice(JNIEnv *env, jclass clazz) { - return im_cancel_last_choice(); -} - -JNIEXPORT jint JNICALL nativeImGetFixedLen(JNIEnv *env, jclass clazz) { - return im_get_fixed_len(); -} - -JNIEXPORT jboolean JNICALL nativeImCancelInput(JNIEnv *env, jclass clazz) { - if (im_cancel_input()) - return JNI_TRUE; - - return JNI_FALSE; -} - -JNIEXPORT jboolean JNICALL nativeImFlushCache(JNIEnv *env, jclass clazz) { - im_flush_cache(); - return JNI_TRUE; -} - -JNIEXPORT jint JNICALL nativeImGetPredictsNum(JNIEnv *env, jclass clazz, - jstring fixed_str) { - char16 *fixed_ptr = (char16*)(*env).GetStringChars(fixed_str, false); - size_t fixed_len = (size_t)(*env).GetStringLength(fixed_str); - - char16 fixed_buf[kMaxPredictSize + 1]; - - if (fixed_len > kMaxPredictSize) { - fixed_ptr += fixed_len - kMaxPredictSize; - fixed_len = kMaxPredictSize; - } - utf16_strncpy(fixed_buf, fixed_ptr, fixed_len); - fixed_buf[fixed_len] = (char16)'\0'; - - predict_len = im_get_predicts(fixed_buf, predict_buf); - - return predict_len; -} - -JNIEXPORT jstring JNICALL nativeImGetPredictItem(JNIEnv *env, jclass clazz, - jint predict_no) { - jstring retstr; - - if (predict_no < 0 || (size_t)predict_no >= predict_len) { - retstr = (*env).NewString((unsigned short*)predict_buf[0], 0); - } else { - retstr = (*env).NewString((unsigned short*)predict_buf[predict_no], - utf16_strlen(predict_buf[predict_no])); - } - return retstr; -} - -JNIEXPORT jboolean JNICALL nativeSyncBegin(JNIEnv *env, jclass clazz, - jbyteArray dict_file) { - jbyte *file_name = (*env).GetByteArrayElements(dict_file, 0); - if (true == sync_worker.begin((const char *)file_name)) - return JNI_TRUE; - return JNI_FALSE; -} - -JNIEXPORT jboolean JNICALL nativeSyncFinish(JNIEnv *env, jclass clazz) { - sync_worker.finish(); - return JNI_TRUE; -} - -JNIEXPORT jint JNICALL nativeSyncGetCapacity(JNIEnv *env, jclass clazz) { - return sync_worker.get_capacity(); -} - -JNIEXPORT jint JNICALL nativeSyncPutLemmas(JNIEnv *env, jclass clazz, - jstring tomerge) { - - char16 *ptr = (char16*)(*env).GetStringChars(tomerge, NULL); - int len = (size_t)(*env).GetStringLength(tomerge); - - int added = sync_worker.put_lemmas(ptr, len); - - (*env).ReleaseStringChars(tomerge, ptr); - - return added; -} - -JNIEXPORT jstring JNICALL nativeSyncGetLemmas(JNIEnv *env, jclass clazz) { - - int len = sync_worker.get_lemmas(retbuf, RET_BUF_LEN); - if (len == 0) - return NULL; - jstring retstr; - retstr = (*env).NewString((unsigned short*)retbuf, len); - return retstr; -} - -JNIEXPORT jint JNICALL nativeSyncGetLastCount(JNIEnv *env, jclass clazz) { - return sync_worker.get_last_got_count(); -} - -JNIEXPORT jint JNICALL nativeSyncGetTotalCount(JNIEnv *env, jclass clazz) { - return sync_worker.get_total_count(); -} - -JNIEXPORT jboolean JNICALL nativeSyncClearLastGot(JNIEnv *env, jclass clazz) { - sync_worker.clear_last_got(); - return JNI_TRUE; -} - -/** - * Table of methods associated with a single class. - */ -static JNINativeMethod gMethods[] = { - /* name, signature, funcPtr */ - /* ------Functions for Pinyin-to-hanzi decoding begin--------->> */ - { "nativeImOpenDecoder", "([B[B)Z", - (void*) nativeImOpenDecoder }, - { "nativeImOpenDecoderFd", "(Ljava/io/FileDescriptor;JJ[B)Z", - (void*) nativeImOpenDecoderFd }, - { "nativeImSetMaxLens", "(II)V", - (void*) nativeImSetMaxLens }, - { "nativeImCloseDecoder", "()Z", - (void*) nativeImCloseDecoder }, - { "nativeImSearch", "([BI)I", - (void*) nativeImSearch }, - { "nativeImDelSearch", "(IZZ)I", - (void*) nativeImDelSearch }, - { "nativeImResetSearch", "()V", - (void*) nativeImResetSearch }, - { "nativeImAddLetter", "(B)I", - (void*) nativeImAddLetter }, - { "nativeImGetPyStr", "(Z)Ljava/lang/String;", - (void*) nativeImGetPyStr }, - { "nativeImGetPyStrLen", "(Z)I", - (void*) nativeImGetPyStrLen }, - { "nativeImGetSplStart", "()[I", - (void*) nativeImGetSplStart }, - { "nativeImGetChoice", "(I)Ljava/lang/String;", - (void*) nativeImGetChoice }, - { "nativeImChoose", "(I)I", - (void*) nativeImChoose }, - { "nativeImCancelLastChoice", "()I", - (void*) nativeImCancelLastChoice }, - { "nativeImGetFixedLen", "()I", - (void*) nativeImGetFixedLen }, - { "nativeImGetPredictsNum", "(Ljava/lang/String;)I", - (void*) nativeImGetPredictsNum }, - { "nativeImGetPredictItem", "(I)Ljava/lang/String;", - (void*) nativeImGetPredictItem }, - { "nativeImCancelInput", "()Z", - (void*) nativeImCancelInput }, - { "nativeImFlushCache", "()Z", - (void*) nativeImFlushCache }, - /* <<----Functions for Pinyin-to-hanzi decoding end------------- */ - - /* ------Functions for sync begin----------------------------->> */ - { "nativeSyncBegin", "([B)Z", - (void*) nativeSyncBegin }, - { "nativeSyncFinish", "()Z", - (void*) nativeSyncFinish }, - { "nativeSyncPutLemmas", "(Ljava/lang/String;)I", - (void*) nativeSyncPutLemmas }, - { "nativeSyncGetLemmas", "()Ljava/lang/String;", - (void*) nativeSyncGetLemmas }, - { "nativeSyncGetLastCount", "()I", - (void*) nativeSyncGetLastCount }, - { "nativeSyncGetTotalCount", "()I", - (void*) nativeSyncGetTotalCount }, - { "nativeSyncClearLastGot", "()Z", - (void*) nativeSyncClearLastGot }, - { "nativeSyncGetCapacity", "()I", - (void*) nativeSyncGetCapacity }, - /* <<----Functions for sync end--------------------------------- */ -}; - - -/* - * Register several native methods for one class. - */ -static int registerNativeMethods(JNIEnv* env, const char* className, - JNINativeMethod* gMethods, int numMethods) -{ - jclass clazz; - - clazz = (*env).FindClass(className); - if (clazz == NULL) { - return JNI_FALSE; - } - if ((*env).RegisterNatives(clazz, gMethods, numMethods) < 0) { - return JNI_FALSE; - } - - clazz = env->FindClass("java/io/FileDescriptor"); - LOG_FATAL_IF(clazz == NULL, "Unable to find Java class java.io.FileDescriptor"); - gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); - LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, - "Unable to find descriptor field in java.io.FileDescriptor"); - - return JNI_TRUE; -} - -/* - * Register native methods for all classes we know about. - */ -static int registerNatives(JNIEnv* env) -{ - if (!registerNativeMethods(env, - "com/android/inputmethod/pinyin/PinyinDecoderService", - gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) - return JNI_FALSE; - - return JNI_TRUE; -} - -/* - * Set some test stuff up. - * - * Returns the JNI version on success, -1 on failure. - */ -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) -{ - JNIEnv* env = NULL; - jint result = -1; - - if ((*vm).GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { - goto bail; - } - assert(env != NULL); - - if (!registerNatives(env)) { - goto bail; - } - - /* success -- return valid version number */ - result = JNI_VERSION_1_4; - -bail: - return result; -} - -#ifdef __cplusplus -} -#endif diff --git a/PinyinIME/jni/command/Makefile b/PinyinIME/jni/command/Makefile deleted file mode 100644 index 0722932..0000000 --- a/PinyinIME/jni/command/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -CC=gcc -CFLAGS= -g -Wall -std=c99 -CPP=g++ -CPPFLAGS= -g3 -Wall -lpthread - -PINYINIME_DICTBUILDER=pinyinime_dictbuilder - -LIBRARY_SRC= \ - ../share/dictbuilder.cpp \ - ../share/dictlist.cpp \ - ../share/dicttrie.cpp \ - ../share/lpicache.cpp \ - ../share/mystdlib.cpp \ - ../share/ngram.cpp \ - ../share/searchutility.cpp \ - ../share/spellingtable.cpp \ - ../share/spellingtrie.cpp \ - ../share/splparser.cpp \ - ../share/utf16char.cpp \ - ../share/utf16reader.cpp \ - -all: engine - -engine: $(PINYINIME_DICTBUILDER) - -$(PINYINIME_DICTBUILDER): $(LIBRARY_SRC) pinyinime_dictbuilder.cpp - @$(CPP) $(CPPFLAGS) -o $@ $? - - -clean: - -rm -rf $(PINYINIME_DICTBUILDER) - -.PHONY: clean diff --git a/PinyinIME/jni/command/pinyinime_dictbuilder.cpp b/PinyinIME/jni/command/pinyinime_dictbuilder.cpp deleted file mode 100644 index b7f9606..0000000 --- a/PinyinIME/jni/command/pinyinime_dictbuilder.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include "../include/dicttrie.h" - -using namespace ime_pinyin; - -/** - * Build binary dictionary model. Make sure that ___BUILD_MODEL___ is defined - * in dictdef.h. - */ -int main(int argc, char* argv[]) { - DictTrie* dict_trie = new DictTrie(); - bool success; - if (argc >= 3) - success = dict_trie->build_dict(argv[1], argv[2]); - else - success = dict_trie->build_dict("../data/rawdict_utf16_65105_freq.txt", - "../data/valid_utf16.txt"); - - if (success) { - printf("Build dictionary successfully.\n"); - } else { - printf("Build dictionary unsuccessfully.\n"); - return -1; - } - - success = dict_trie->save_dict("../../res/raw/dict_pinyin.dat"); - - if (success) { - printf("Save dictionary successfully.\n"); - } else { - printf("Save dictionary unsuccessfully.\n"); - return -1; - } - - return 0; -} diff --git a/PinyinIME/jni/data/rawdict_utf16_65105_freq.txt b/PinyinIME/jni/data/rawdict_utf16_65105_freq.txt deleted file mode 100644 index 28805ba..0000000 Binary files a/PinyinIME/jni/data/rawdict_utf16_65105_freq.txt and /dev/null differ diff --git a/PinyinIME/jni/data/valid_utf16.txt b/PinyinIME/jni/data/valid_utf16.txt deleted file mode 100644 index fecc67e..0000000 Binary files a/PinyinIME/jni/data/valid_utf16.txt and /dev/null differ diff --git a/PinyinIME/jni/include/atomdictbase.h b/PinyinIME/jni/include/atomdictbase.h deleted file mode 100644 index 0a70a51..0000000 --- a/PinyinIME/jni/include/atomdictbase.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * This class defines AtomDictBase class which is the base class for all atom - * dictionaries. Atom dictionaries are managed by the decoder class - * MatrixSearch. - * - * When the user appends a new character to the Pinyin string, all enabled atom - * dictionaries' extend_dict() will be called at least once to get candidates - * ended in this step (the information of starting step is also given in the - * parameter). Usually, when extend_dict() is called, a MileStoneHandle object - * returned by a previous calling for a earlier step is given to speed up the - * look-up process, and a new MileStoneHandle object will be returned if - * the extension is successful. - * - * A returned MileStoneHandle object should keep alive until Function - * reset_milestones() is called and this object is noticed to be reset. - * - * Usually, the atom dictionary can use step information to manage its - * MileStoneHandle objects, or it can make the objects in ascendant order to - * make the reset easier. - * - * When the decoder loads the dictionary, it will give a starting lemma id for - * this atom dictionary to map a inner id to a global id. Global ids should be - * used when an atom dictionary talks to any component outside. - */ -#ifndef PINYINIME_INCLUDE_ATOMDICTBASE_H__ -#define PINYINIME_INCLUDE_ATOMDICTBASE_H__ - -#include -#include "./dictdef.h" -#include "./searchutility.h" - -namespace ime_pinyin { -class AtomDictBase { - public: - virtual ~AtomDictBase() {} - - /** - * Load an atom dictionary from a file. - * - * @param file_name The file name to load dictionary. - * @param start_id The starting id used for this atom dictionary. - * @param end_id The end id (included) which can be used for this atom - * dictionary. User dictionary will always use the last id space, so it can - * ignore this paramter. All other atom dictionaries should check this - * parameter. - * @return True if succeed. - */ - virtual bool load_dict(const char *file_name, LemmaIdType start_id, - LemmaIdType end_id) = 0; - - /** - * Close this atom dictionary. - * - * @return True if succeed. - */ - virtual bool close_dict() = 0; - - /** - * Get the total number of lemmas in this atom dictionary. - * - * @return The total number of lemmas. - */ - virtual size_t number_of_lemmas() = 0; - - /** - * This function is called by the decoder when user deletes a character from - * the input string, or begins a new input string. - * - * Different atom dictionaries may implement this function in different way. - * an atom dictionary can use one of these two parameters (or both) to reset - * its corresponding MileStoneHandle objects according its detailed - * implementation. - * - * For example, if an atom dictionary uses step information to manage its - * MileStoneHandle objects, parameter from_step can be used to identify which - * objects should be reset; otherwise, if another atom dictionary does not - * use the detailed step information, it only uses ascendant handles - * (according to step. For the same step, earlier call, smaller handle), it - * can easily reset those MileStoneHandle which are larger than from_handle. - * - * The decoder always reset the decoding state by step. So when it begins - * resetting, it will call reset_milestones() of its atom dictionaries with - * the step information, and the MileStoneHandle objects returned by the - * earliest calling of extend_dict() for that step. - * - * If an atom dictionary does not implement incremental search, this function - * can be totally ignored. - * - * @param from_step From which step(included) the MileStoneHandle - * objects should be reset. - * @param from_handle The ealiest MileStoneHandle object for step from_step - */ - virtual void reset_milestones(uint16 from_step, - MileStoneHandle from_handle) = 0; - - /** - * Used to extend in this dictionary. The handle returned should keep valid - * until reset_milestones() is called. - * - * @param from_handle Its previous returned extended handle without the new - * spelling id, it can be used to speed up the extending. - * @param dep The paramter used for extending. - * @param lpi_items Used to fill in the lemmas matched. - * @param lpi_max The length of the buffer - * @param lpi_num Used to return the newly added items. - * @return The new mile stone for this extending. 0 if fail. - */ - virtual MileStoneHandle extend_dict(MileStoneHandle from_handle, - const DictExtPara *dep, - LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num) = 0; - - /** - * Get lemma items with scores according to a spelling id stream. - * This atom dictionary does not need to sort the returned items. - * - * @param splid_str The spelling id stream buffer. - * @param splid_str_len The length of the spelling id stream buffer. - * @param lpi_items Used to return matched lemma items with scores. - * @param lpi_max The maximum size of the buffer to return result. - * @return The number of matched items which have been filled in to lpi_items. - */ - virtual size_t get_lpis(const uint16 *splid_str, uint16 splid_str_len, - LmaPsbItem *lpi_items, size_t lpi_max) = 0; - - /** - * Get a lemma string (The Chinese string) by the given lemma id. - * - * @param id_lemma The lemma id to get the string. - * @param str_buf The buffer to return the Chinese string. - * @param str_max The maximum size of the buffer. - * @return The length of the string, 0 if fail. - */ - virtual uint16 get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, - uint16 str_max) = 0; - - /** - * Get the full spelling ids for the given lemma id. - * If the given buffer is too short, return 0. - * - * @param splids Used to return the spelling ids. - * @param splids_max The maximum buffer length of splids. - * @param arg_valid Used to indicate if the incoming parameters have been - * initialized are valid. If it is true, the splids and splids_max are valid - * and there may be half ids in splids to be updated to full ids. In this - * case, splids_max is the number of valid ids in splids. - * @return The number of ids in the buffer. - */ - virtual uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, - uint16 splids_max, bool arg_valid) = 0; - - /** - * Function used for prediction. - * No need to sort the newly added items. - * - * @param last_hzs The last n Chinese chracters(called Hanzi), its length - * should be less than or equal to kMaxPredictSize. - * @param hzs_len specifies the length(<= kMaxPredictSize) of the history. - * @param npre_items Used used to return the result. - * @param npre_max The length of the buffer to return result - * @param b4_used Number of prediction result (from npre_items[-b4_used]) - * from other atom dictionaries. A atom ditionary can just ignore it. - * @return The number of prediction result from this atom dictionary. - */ - virtual size_t predict(const char16 last_hzs[], uint16 hzs_len, - NPredictItem *npre_items, size_t npre_max, - size_t b4_used) = 0; - - /** - * Add a lemma to the dictionary. If the dictionary allows to add new - * items and this item does not exist, add it. - * - * @param lemma_str The Chinese string of the lemma. - * @param splids The spelling ids of the lemma. - * @param lemma_len The length of the Chinese lemma. - * @param count The frequency count for this lemma. - */ - virtual LemmaIdType put_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count) = 0; - - /** - * Update a lemma's occuring count. - * - * @param lemma_id The lemma id to update. - * @param delta_count The frequnecy count to ajust. - * @param selected Indicate whether this lemma is selected by user and - * submitted to target edit box. - * @return The id if succeed, 0 if fail. - */ - virtual LemmaIdType update_lemma(LemmaIdType lemma_id, int16 delta_count, - bool selected) = 0; - - /** - * Get the lemma id for the given lemma. - * - * @param lemma_str The Chinese string of the lemma. - * @param splids The spelling ids of the lemma. - * @param lemma_len The length of the lemma. - * @return The matched lemma id, or 0 if fail. - */ - virtual LemmaIdType get_lemma_id(char16 lemma_str[], uint16 splids[], - uint16 lemma_len) = 0; - - /** - * Get the lemma score. - * - * @param lemma_id The lemma id to get score. - * @return The score of the lemma, or 0 if fail. - */ - virtual LmaScoreType get_lemma_score(LemmaIdType lemma_id) = 0; - - /** - * Get the lemma score. - * - * @param lemma_str The Chinese string of the lemma. - * @param splids The spelling ids of the lemma. - * @param lemma_len The length of the lemma. - * @return The score of the lamm, or 0 if fail. - */ - virtual LmaScoreType get_lemma_score(char16 lemma_str[], uint16 splids[], - uint16 lemma_len) = 0; - - /** - * If the dictionary allowed, remove a lemma from it. - * - * @param lemma_id The id of the lemma to remove. - * @return True if succeed. - */ - virtual bool remove_lemma(LemmaIdType lemma_id) = 0; - - /** - * Get the total occuring count of this atom dictionary. - * - * @return The total occuring count of this atom dictionary. - */ - virtual size_t get_total_lemma_count() = 0; - - /** - * Set the total occuring count of other atom dictionaries. - * - * @param count The total occuring count of other atom dictionaies. - */ - virtual void set_total_lemma_count_of_others(size_t count) = 0; - - /** - * Notify this atom dictionary to flush the cached data to persistent storage - * if necessary. - */ - virtual void flush_cache() = 0; -}; -} - -#endif // PINYINIME_INCLUDE_ATOMDICTBASE_H__ diff --git a/PinyinIME/jni/include/dictbuilder.h b/PinyinIME/jni/include/dictbuilder.h deleted file mode 100644 index da0d6cd..0000000 --- a/PinyinIME/jni/include/dictbuilder.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_DICTBUILDER_H__ -#define PINYINIME_INCLUDE_DICTBUILDER_H__ - -#include -#include "./utf16char.h" -#include "./dictdef.h" -#include "./dictlist.h" -#include "./spellingtable.h" -#include "./spellingtrie.h" -#include "./splparser.h" - -namespace ime_pinyin { - -#ifdef ___BUILD_MODEL___ - -#define ___DO_STATISTICS___ - -class DictTrie; - -class DictBuilder { - private: - // The raw lemma array buffer. - LemmaEntry *lemma_arr_; - size_t lemma_num_; - - // Used to store all possible single char items. - // Two items may have the same Hanzi while their spelling ids are different. - SingleCharItem *scis_; - size_t scis_num_; - - // In the tree, root's level is -1. - // Lemma nodes for root, and level 0 - LmaNodeLE0 *lma_nodes_le0_; - - // Lemma nodes for layers whose levels are deeper than 0 - LmaNodeGE1 *lma_nodes_ge1_; - - // Number of used lemma nodes - size_t lma_nds_used_num_le0_; - size_t lma_nds_used_num_ge1_; - - // Used to store homophonies' ids. - LemmaIdType *homo_idx_buf_; - // Number of homophonies each of which only contains one Chinese character. - size_t homo_idx_num_eq1_; - // Number of homophonies each of which contains more than one character. - size_t homo_idx_num_gt1_; - - // The items with highest scores. - LemmaEntry *top_lmas_; - size_t top_lmas_num_; - - SpellingTable *spl_table_; - SpellingParser *spl_parser_; - -#ifdef ___DO_STATISTICS___ - size_t max_sonbuf_len_[kMaxLemmaSize]; - size_t max_homobuf_len_[kMaxLemmaSize]; - - size_t total_son_num_[kMaxLemmaSize]; - size_t total_node_hasson_[kMaxLemmaSize]; - size_t total_sonbuf_num_[kMaxLemmaSize]; - size_t total_sonbuf_allnoson_[kMaxLemmaSize]; - size_t total_node_in_sonbuf_allnoson_[kMaxLemmaSize]; - size_t total_homo_num_[kMaxLemmaSize]; - - size_t sonbufs_num1_; // Number of son buffer with only 1 son - size_t sonbufs_numgt1_; // Number of son buffer with more 1 son; - - size_t total_lma_node_num_; - - void stat_init(); - void stat_print(); -#endif - - public: - - DictBuilder(); - ~DictBuilder(); - - // Build dictionary trie from the file fn_raw. File fn_validhzs provides - // valid chars. If fn_validhzs is NULL, only chars in GB2312 will be - // included. - bool build_dict(const char* fn_raw, const char* fn_validhzs, - DictTrie *dict_trie); - - private: - // Fill in the buffer with id. The caller guarantees that the paramters are - // vaild. - void id_to_charbuf(unsigned char *buf, LemmaIdType id); - - // Update the offset of sons for a node. - void set_son_offset(LmaNodeGE1 *node, size_t offset); - - // Update the offset of homophonies' ids for a node. - void set_homo_id_buf_offset(LmaNodeGE1 *node, size_t offset); - - // Format a speling string. - void format_spelling_str(char *spl_str); - - // Sort the lemma_arr by the hanzi string, and give each of unique items - // a id. Why we need to sort the lemma list according to their Hanzi string - // is to find items started by a given prefix string to do prediction. - // Actually, the single char items are be in other order, for example, - // in spelling id order, etc. - // Return value is next un-allocated idx available. - LemmaIdType sort_lemmas_by_hz(); - - // Build the SingleCharItem list, and fill the hanzi_scis_ids in the - // lemma buffer lemma_arr_. - // This function should be called after the lemma array is ready. - // Return the number of unique SingleCharItem elements. - size_t build_scis(); - - // Construct a subtree using a subset of the spelling array (from - // item_star to item_end) - // parent is the parent node to update the necessary information - // parent can be a member of LmaNodeLE0 or LmaNodeGE1 - bool construct_subset(void* parent, LemmaEntry* lemma_arr, - size_t item_start, size_t item_end, size_t level); - - - // Read valid Chinese Hanzis from the given file. - // num is used to return number of chars. - // The return buffer is sorted and caller needs to free the returned buffer. - char16* read_valid_hanzis(const char *fn_validhzs, size_t *num); - - - // Read a raw dictionary. max_item is the maximum number of items. If there - // are more items in the ditionary, only the first max_item will be read. - // Returned value is the number of items successfully read from the file. - size_t read_raw_dict(const char* fn_raw, const char *fn_validhzs, - size_t max_item); - - // Try to find if a character is in hzs buffer. - bool hz_in_hanzis_list(const char16 *hzs, size_t hzs_len, char16 hz); - - // Try to find if all characters in str are in hzs buffer. - bool str_in_hanzis_list(const char16 *hzs, size_t hzs_len, - const char16 *str, size_t str_len); - - // Get these lemmas with toppest scores. - void get_top_lemmas(); - - // Allocate resource to build dictionary. - // lma_num is the number of items to be loaded - bool alloc_resource(size_t lma_num); - - // Free resource. - void free_resource(); -}; -#endif // ___BUILD_MODEL___ -} - -#endif // PINYINIME_INCLUDE_DICTBUILDER_H__ diff --git a/PinyinIME/jni/include/dictdef.h b/PinyinIME/jni/include/dictdef.h deleted file mode 100644 index 3e79d98..0000000 --- a/PinyinIME/jni/include/dictdef.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_DICTDEF_H__ -#define PINYINIME_INCLUDE_DICTDEF_H__ - -#include -#include "./utf16char.h" - -namespace ime_pinyin { - -// Enable the following line when building the binary dictionary model. -// #define ___BUILD_MODEL___ - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; - -typedef signed char int8; -typedef short int16; -typedef int int32; -typedef long long int64; -typedef unsigned long long uint64; - -const bool kPrintDebug0 = false; -const bool kPrintDebug1 = false; -const bool kPrintDebug2 = false; - -// The max length of a lemma. -const size_t kMaxLemmaSize = 8; - -// The max length of a Pinyin (spelling). -const size_t kMaxPinyinSize = 6; - -// The number of half spelling ids. For Chinese Pinyin, there 30 half ids. -// See SpellingTrie.h for details. -const size_t kHalfSpellingIdNum = 29; - -// The maximum number of full spellings. For Chinese Pinyin, there are only -// about 410 spellings. -// If change this value is bigger(needs more bits), please also update -// other structures like SpellingNode, to make sure than a spelling id can be -// stored. -// -1 is because that 0 is never used. -const size_t kMaxSpellingNum = 512 - kHalfSpellingIdNum - 1; -const size_t kMaxSearchSteps = 40; - -// One character predicts its following characters. -const size_t kMaxPredictSize = (kMaxLemmaSize - 1); - -// LemmaIdType must always be size_t. -typedef size_t LemmaIdType; -const size_t kLemmaIdSize = 3; // Actually, a Id occupies 3 bytes in storage. -const size_t kLemmaIdComposing = 0xffffff; - -typedef uint16 LmaScoreType; -typedef uint16 KeyScoreType; - -// Number of items with highest score are kept for prediction purpose. -const size_t kTopScoreLemmaNum = 10; - -const size_t kMaxPredictNumByGt3 = 1; -const size_t kMaxPredictNumBy3 = 2; -const size_t kMaxPredictNumBy2 = 2; - -// The last lemma id (included) for the system dictionary. The system -// dictionary's ids always start from 1. -const LemmaIdType kSysDictIdEnd = 500000; - -// The first lemma id for the user dictionary. -const LemmaIdType kUserDictIdStart = 500001; - -// The last lemma id (included) for the user dictionary. -const LemmaIdType kUserDictIdEnd = 600000; - -typedef struct { - uint16 half_splid:5; - uint16 full_splid:11; -} SpellingId, *PSpellingId; - - -/** - * We use different node types for different layers - * Statistical data of the building result for a testing dictionary: - * root, level 0, level 1, level 2, level 3 - * max son num of one node: 406 280 41 2 - - * max homo num of one node: 0 90 23 2 2 - * total node num of a layer: 1 406 31766 13516 993 - * total homo num of a layer: 9 5674 44609 12667 995 - * - * The node number for root and level 0 won't be larger than 500 - * According to the information above, two kinds of nodes can be used; one for - * root and level 0, the other for these layers deeper than 0. - * - * LE = less and equal, - * A node occupies 16 bytes. so, totallly less than 16 * 500 = 8K - */ -struct LmaNodeLE0 { - size_t son_1st_off; - size_t homo_idx_buf_off; - uint16 spl_idx; - uint16 num_of_son; - uint16 num_of_homo; -}; - -/** - * GE = great and equal - * A node occupies 8 bytes. - */ -struct LmaNodeGE1 { - uint16 son_1st_off_l; // Low bits of the son_1st_off - uint16 homo_idx_buf_off_l; // Low bits of the homo_idx_buf_off_1 - uint16 spl_idx; - unsigned char num_of_son; // number of son nodes - unsigned char num_of_homo; // number of homo words - unsigned char son_1st_off_h; // high bits of the son_1st_off - unsigned char homo_idx_buf_off_h; // high bits of the homo_idx_buf_off -}; - -#ifdef ___BUILD_MODEL___ -struct SingleCharItem { - float freq; - char16 hz; - SpellingId splid; -}; - -struct LemmaEntry { - LemmaIdType idx_by_py; - LemmaIdType idx_by_hz; - char16 hanzi_str[kMaxLemmaSize + 1]; - - // The SingleCharItem id for each Hanzi. - uint16 hanzi_scis_ids[kMaxLemmaSize]; - - uint16 spl_idx_arr[kMaxLemmaSize + 1]; - char pinyin_str[kMaxLemmaSize][kMaxPinyinSize + 1]; - unsigned char hz_str_len; - float freq; -}; -#endif // ___BUILD_MODEL___ - -} // namespace ime_pinyin - -#endif // PINYINIME_INCLUDE_DICTDEF_H__ diff --git a/PinyinIME/jni/include/dictlist.h b/PinyinIME/jni/include/dictlist.h deleted file mode 100644 index 5fcc12f..0000000 --- a/PinyinIME/jni/include/dictlist.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_DICTLIST_H__ -#define PINYINIME_INCLUDE_DICTLIST_H__ - -#include -#include -#include "./dictdef.h" -#include "./searchutility.h" -#include "./spellingtrie.h" -#include "./utf16char.h" - -namespace ime_pinyin { - -class DictList { - private: - bool initialized_; - - const SpellingTrie *spl_trie_; - - // Number of SingCharItem. The first is blank, because id 0 is invalid. - size_t scis_num_; - char16 *scis_hz_; - SpellingId *scis_splid_; - - // The large memory block to store the word list. - char16 *buf_; - - // Starting position of those words whose lengths are i+1, counted in - // char16 - size_t start_pos_[kMaxLemmaSize + 1]; - - size_t start_id_[kMaxLemmaSize + 1]; - - int (*cmp_func_[kMaxLemmaSize])(const void *, const void *); - - bool alloc_resource(size_t buf_size, size_t scim_num); - - void free_resource(); - -#ifdef ___BUILD_MODEL___ - // Calculate the requsted memory, including the start_pos[] buffer. - size_t calculate_size(const LemmaEntry *lemma_arr, size_t lemma_num); - - void fill_scis(const SingleCharItem *scis, size_t scis_num); - - // Copy the related content to the inner buffer - // It should be called after calculate_size() - void fill_list(const LemmaEntry *lemma_arr, size_t lemma_num); - - // Find the starting position for the buffer of those 2-character Chinese word - // whose first character is the given Chinese character. - char16* find_pos2_startedbyhz(char16 hz_char); -#endif - - // Find the starting position for the buffer of those words whose lengths are - // word_len. The given parameter cmp_func decides how many characters from - // beginning will be used to compare. - char16* find_pos_startedbyhzs(const char16 last_hzs[], - size_t word_Len, - int (*cmp_func)(const void *, const void *)); - - public: - - DictList(); - ~DictList(); - - bool save_list(FILE *fp); - bool load_list(FILE *fp); - -#ifdef ___BUILD_MODEL___ - // Init the list from the LemmaEntry array. - // lemma_arr should have been sorted by the hanzi_str, and have been given - // ids from 1 - bool init_list(const SingleCharItem *scis, size_t scis_num, - const LemmaEntry *lemma_arr, size_t lemma_num); -#endif - - // Get the hanzi string for the given id - uint16 get_lemma_str(LemmaIdType id_hz, char16 *str_buf, uint16 str_max); - - void convert_to_hanzis(char16 *str, uint16 str_len); - - void convert_to_scis_ids(char16 *str, uint16 str_len); - - // last_hzs stores the last n Chinese characters history, its length should be - // less or equal than kMaxPredictSize. - // hzs_len specifies the length(<= kMaxPredictSize). - // predict_buf is used to store the result. - // buf_len specifies the buffer length. - // b4_used specifies how many items before predict_buf have been used. - // Returned value is the number of newly added items. - size_t predict(const char16 last_hzs[], uint16 hzs_len, - NPredictItem *npre_items, size_t npre_max, - size_t b4_used); - - // If half_splid is a valid half spelling id, return those full spelling - // ids which share this half id. - uint16 get_splids_for_hanzi(char16 hanzi, uint16 half_splid, - uint16 *splids, uint16 max_splids); - - LemmaIdType get_lemma_id(const char16 *str, uint16 str_len); -}; -} - -#endif // PINYINIME_INCLUDE_DICTLIST_H__ diff --git a/PinyinIME/jni/include/dicttrie.h b/PinyinIME/jni/include/dicttrie.h deleted file mode 100644 index 268624f..0000000 --- a/PinyinIME/jni/include/dicttrie.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_DICTTRIE_H__ -#define PINYINIME_INCLUDE_DICTTRIE_H__ - -#include -#include "./atomdictbase.h" -#include "./dictdef.h" -#include "./dictlist.h" -#include "./searchutility.h" - -namespace ime_pinyin { - -class DictTrie : AtomDictBase { - private: - typedef struct ParsingMark { - size_t node_offset:24; - size_t node_num:8; // Number of nodes with this spelling id given - // by spl_id. If spl_id is a Shengmu, for nodes - // in the first layer of DictTrie, it equals to - // SpellingTrie::shm2full_num(); but for those - // nodes which are not in the first layer, - // node_num < SpellingTrie::shm2full_num(). - // For a full spelling id, node_num = 1; - }; - - // Used to indicate an extended mile stone. - // An extended mile stone is used to mark a partial match in the dictionary - // trie to speed up further potential extending. - // For example, when the user inputs "w", a mile stone is created to mark the - // partial match status, so that when user inputs another char 'm', it will be - // faster to extend search space based on this mile stone. - // - // For partial match status of "wm", there can be more than one sub mile - // stone, for example, "wm" can be matched to "wanm", "wom", ..., etc, so - // there may be more one parsing mark used to mark these partial matchings. - // A mile stone records the starting position in the mark list and number of - // marks. - struct MileStone { - uint16 mark_start; - uint16 mark_num; - }; - - DictList* dict_list_; - - const SpellingTrie *spl_trie_; - - LmaNodeLE0* root_; // Nodes for root and the first layer. - LmaNodeGE1* nodes_ge1_; // Nodes for other layers. - - // An quick index from spelling id to the LmaNodeLE0 node buffer, or - // to the root_ buffer. - // Index length: - // SpellingTrie::get_instance().get_spelling_num() + 1. The last one is used - // to get the end. - // All Shengmu ids are not indexed because they will be converted into - // corresponding full ids. - // So, given an id splid, the son is: - // root_[splid_le0_index_[splid - kFullSplIdStart]] - uint16 *splid_le0_index_; - - size_t lma_node_num_le0_; - size_t lma_node_num_ge1_; - - // The first part is for homophnies, and the last top_lma_num_ items are - // lemmas with highest scores. - unsigned char *lma_idx_buf_; - size_t lma_idx_buf_len_; // The total size of lma_idx_buf_ in byte. - size_t total_lma_num_; // Total number of lemmas in this dictionary. - size_t top_lmas_num_; // Number of lemma with highest scores. - - // Parsing mark list used to mark the detailed extended statuses. - ParsingMark *parsing_marks_; - // The position for next available mark. - uint16 parsing_marks_pos_; - - // Mile stone list used to mark the extended status. - MileStone *mile_stones_; - // The position for the next available mile stone. We use positions (except 0) - // as handles. - MileStoneHandle mile_stones_pos_; - - // Get the offset of sons for a node. - inline size_t get_son_offset(const LmaNodeGE1 *node); - - // Get the offset of homonious ids for a node. - inline size_t get_homo_idx_buf_offset(const LmaNodeGE1 *node); - - // Get the lemma id by the offset. - inline LemmaIdType get_lemma_id(size_t id_offset); - - void free_resource(bool free_dict_list); - - bool load_dict(FILE *fp); - - // Given a LmaNodeLE0 node, extract the lemmas specified by it, and fill - // them into the lpi_items buffer. - // This function is called by the search engine. - size_t fill_lpi_buffer(LmaPsbItem lpi_items[], size_t max_size, - LmaNodeLE0 *node); - - // Given a LmaNodeGE1 node, extract the lemmas specified by it, and fill - // them into the lpi_items buffer. - // This function is called by inner functions extend_dict0(), extend_dict1() - // and extend_dict2(). - size_t fill_lpi_buffer(LmaPsbItem lpi_items[], size_t max_size, - size_t homo_buf_off, LmaNodeGE1 *node, - uint16 lma_len); - - // Extend in the trie from level 0. - MileStoneHandle extend_dict0(MileStoneHandle from_handle, - const DictExtPara *dep, LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num); - - // Extend in the trie from level 1. - MileStoneHandle extend_dict1(MileStoneHandle from_handle, - const DictExtPara *dep, LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num); - - // Extend in the trie from level 2. - MileStoneHandle extend_dict2(MileStoneHandle from_handle, - const DictExtPara *dep, LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num); - - // Try to extend the given spelling id buffer, and if the given id_lemma can - // be successfully gotten, return true; - // The given spelling ids are all valid full ids. - bool try_extend(const uint16 *splids, uint16 splid_num, LemmaIdType id_lemma); - -#ifdef ___BUILD_MODEL___ - bool save_dict(FILE *fp); -#endif // ___BUILD_MODEL___ - - static const int kMaxMileStone = 100; - static const int kMaxParsingMark = 600; - static const MileStoneHandle kFirstValidMileStoneHandle = 1; - - friend class DictParser; - friend class DictBuilder; - - public: - - DictTrie(); - ~DictTrie(); - -#ifdef ___BUILD_MODEL___ - // Construct the tree from the file fn_raw. - // fn_validhzs provide the valid hanzi list. If fn_validhzs is - // NULL, only chars in GB2312 will be included. - bool build_dict(const char *fn_raw, const char *fn_validhzs); - - // Save the binary dictionary - // Actually, the SpellingTrie/DictList instance will be also saved. - bool save_dict(const char *filename); -#endif // ___BUILD_MODEL___ - - void convert_to_hanzis(char16 *str, uint16 str_len); - - void convert_to_scis_ids(char16 *str, uint16 str_len); - - // Load a binary dictionary - // The SpellingTrie instance/DictList will be also loaded - bool load_dict(const char *filename, LemmaIdType start_id, - LemmaIdType end_id); - bool load_dict_fd(int sys_fd, long start_offset, long length, - LemmaIdType start_id, LemmaIdType end_id); - bool close_dict() {return true;} - size_t number_of_lemmas() {return 0;} - - void reset_milestones(uint16 from_step, MileStoneHandle from_handle); - - MileStoneHandle extend_dict(MileStoneHandle from_handle, - const DictExtPara *dep, - LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num); - - size_t get_lpis(const uint16 *splid_str, uint16 splid_str_len, - LmaPsbItem *lpi_items, size_t lpi_max); - - uint16 get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, uint16 str_max); - - uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, - uint16 splids_max, bool arg_valid); - - size_t predict(const char16 *last_hzs, uint16 hzs_len, - NPredictItem *npre_items, size_t npre_max, - size_t b4_used); - - LemmaIdType put_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count) {return 0;} - - LemmaIdType update_lemma(LemmaIdType lemma_id, int16 delta_count, - bool selected) {return 0;} - - LemmaIdType get_lemma_id(char16 lemma_str[], uint16 splids[], - uint16 lemma_len) {return 0;} - - LmaScoreType get_lemma_score(LemmaIdType lemma_id) {return 0;} - - LmaScoreType get_lemma_score(char16 lemma_str[], uint16 splids[], - uint16 lemma_len) {return 0;} - - bool remove_lemma(LemmaIdType lemma_id) {return false;} - - size_t get_total_lemma_count() {return 0;} - void set_total_lemma_count_of_others(size_t count); - - void flush_cache() {} - - LemmaIdType get_lemma_id(const char16 lemma_str[], uint16 lemma_len); - - // Fill the lemmas with highest scores to the prediction buffer. - // his_len is the history length to fill in the prediction buffer. - size_t predict_top_lmas(size_t his_len, NPredictItem *npre_items, - size_t npre_max, size_t b4_used); -}; -} - -#endif // PINYINIME_INCLUDE_DICTTRIE_H__ diff --git a/PinyinIME/jni/include/lpicache.h b/PinyinIME/jni/include/lpicache.h deleted file mode 100644 index 6073597..0000000 --- a/PinyinIME/jni/include/lpicache.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_ANDPY_INCLUDE_LPICACHE_H__ -#define PINYINIME_ANDPY_INCLUDE_LPICACHE_H__ - -#include -#include "./searchutility.h" -#include "./spellingtrie.h" - -namespace ime_pinyin { - -// Used to cache LmaPsbItem list for half spelling ids. -class LpiCache { - private: - static LpiCache *instance_; - static const int kMaxLpiCachePerId = 15; - - LmaPsbItem *lpi_cache_; - uint16 *lpi_cache_len_; - - public: - LpiCache(); - ~LpiCache(); - - static LpiCache& get_instance(); - - // Test if the LPI list of the given splid has been cached. - // If splid is a full spelling id, it returns false, because we only cache - // list for half ids. - bool is_cached(uint16 splid); - - // Put LPI list to cahce. If the length of the list, lpi_num, is longer than - // the cache buffer. the list will be truncated, and function returns the - // maximum length of the cache buffer. - // Note: splid must be a half id, and lpi_items must be not NULL. The - // caller of this function should guarantee this. - size_t put_cache(uint16 splid, LmaPsbItem lpi_items[], size_t lpi_num); - - // Get the cached list for the given half id. - // Return the length of the cached buffer. - // Note: splid must be a half id, and lpi_items must be not NULL. The - // caller of this function should guarantee this. - size_t get_cache(uint16 splid, LmaPsbItem lpi_items[], size_t lpi_max); -}; - -} // namespace - -#endif // PINYINIME_ANDPY_INCLUDE_LPICACHE_H__ diff --git a/PinyinIME/jni/include/matrixsearch.h b/PinyinIME/jni/include/matrixsearch.h deleted file mode 100644 index b9cf061..0000000 --- a/PinyinIME/jni/include/matrixsearch.h +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_ANDPY_INCLUDE_MATRIXSEARCH_H__ -#define PINYINIME_ANDPY_INCLUDE_MATRIXSEARCH_H__ - -#include -#include "./atomdictbase.h" -#include "./dicttrie.h" -#include "./searchutility.h" -#include "./spellingtrie.h" -#include "./splparser.h" - -namespace ime_pinyin { - -static const size_t kMaxRowNum = kMaxSearchSteps; - -typedef struct { - // MileStoneHandle objects for the system and user dictionaries. - MileStoneHandle dict_handles[2]; - // From which DMI node. -1 means it's from root. - PoolPosType dmi_fr; - // The spelling id for the Pinyin string from the previous DMI to this node. - // If it is a half id like Shengmu, the node pointed by dict_node is the first - // node with this Shengmu, - uint16 spl_id; - // What's the level of the dict node. Level of root is 0, but root is never - // recorded by dict_node. - unsigned char dict_level:7; - // If this node is for composing phrase, this bit is 1. - unsigned char c_phrase:1; - // Whether the spl_id is parsed with a split character at the end. - unsigned char splid_end_split:1; - // What's the length of the spelling string for this match, for the whole - // word. - unsigned char splstr_len:7; - // Used to indicate whether all spelling ids from the root are full spelling - // ids. This information is useful for keymapping mode(not finished). Because - // in this mode, there is no clear boundaries, we prefer those results which - // have full spelling ids. - unsigned char all_full_id:1; -} DictMatchInfo, *PDictMatchInfo; - -typedef struct MatrixNode { - LemmaIdType id; - float score; - MatrixNode *from; - // From which DMI node. Used to trace the spelling segmentation. - PoolPosType dmi_fr; - uint16 step; -} MatrixNode, *PMatrixNode; - -typedef struct { - // The MatrixNode position in the matrix pool - PoolPosType mtrx_nd_pos; - // The DictMatchInfo position in the DictMatchInfo pool. - PoolPosType dmi_pos; - uint16 mtrx_nd_num; - uint16 dmi_num:15; - // Used to indicate whether there are dmi nodes in this step with full - // spelling id. This information is used to decide whether a substring of a - // valid Pinyin should be extended. - // - // Example1: shoudao - // When the last char 'o' is added, the parser will find "dao" is a valid - // Pinyin, and because all dmi nodes at location 'd' (including those for - // "shoud", and those for "d") have Shengmu id only, so it is not necessary - // to extend "ao", otherwise the result may be "shoud ao", that is not - // reasonable. - // - // Example2: hengao - // When the last 'o' is added, the parser finds "gao" is a valid Pinyin. - // Because some dmi nodes at 'g' has Shengmu ids (hen'g and g), but some dmi - // nodes at 'g' has full ids ('heng'), so it is necessary to extend "ao", thus - // "heng ao" can also be the result. - // - // Similarly, "ganga" is expanded to "gang a". - // - // For Pinyin string "xian", because "xian" is a valid Pinyin, because all dmi - // nodes at 'x' only have Shengmu ids, the parser will not try "x ian" (and it - // is not valid either). If the parser uses break in the loop, the result - // always be "xian"; but if the parser uses continue in the loop, "xi an" will - // also be tried. This behaviour can be set via the function - // set_xi_an_switch(). - uint16 dmi_has_full_id:1; - // Points to a MatrixNode of the current step to indicate which choice the - // user selects. - MatrixNode *mtrx_nd_fixed; -} MatrixRow, *PMatrixRow; - -// When user inputs and selects candidates, the fixed lemma ids are stored in -// lma_id_ of class MatrixSearch, and fixed_lmas_ is used to indicate how many -// lemmas from the beginning are fixed. If user deletes Pinyin characters one -// by one from the end, these fixed lemmas can be unlocked one by one when -// necessary. Whenever user deletes a Chinese character and its spelling string -// in these fixed lemmas, all fixed lemmas will be merged together into a unit -// named ComposingPhrase with a lemma id kLemmaIdComposing, and this composing -// phrase will be the first lemma in the sentence. Because it contains some -// modified lemmas (by deleting a character), these merged lemmas are called -// sub lemmas (sublma), and each of them are represented individually, so that -// when user deletes Pinyin characters from the end, these sub lemmas can also -// be unlocked one by one. -typedef struct { - uint16 spl_ids[kMaxRowNum]; - uint16 spl_start[kMaxRowNum]; - char16 chn_str[kMaxRowNum]; // Chinese string. - uint16 sublma_start[kMaxRowNum]; // Counted in Chinese characters. - size_t sublma_num; - uint16 length; // Counted in Chinese characters. -} ComposingPhrase, *TComposingPhrase; - -class MatrixSearch { - private: - // If it is true, prediction list by string whose length is greater than 1 - // will be limited to a reasonable number. - static const bool kPredictLimitGt1 = false; - - // If it is true, the engine will prefer long history based prediction, - // for example, when user inputs "BeiJing", we prefer "DaXue", etc., which are - // based on the two-character history. - static const bool kPreferLongHistoryPredict = true; - - // If it is true, prediction will only be based on user dictionary. this flag - // is for debug purpose. - static const bool kOnlyUserDictPredict = false; - - // The maximum buffer to store LmaPsbItems. - static const size_t kMaxLmaPsbItems = 1450; - - // How many rows for each step. - static const size_t kMaxNodeARow = 5; - - // The maximum length of the sentence candidates counted in chinese - // characters - static const size_t kMaxSentenceLength = 16; - - // The size of the matrix node pool. - static const size_t kMtrxNdPoolSize = 200; - - // The size of the DMI node pool. - static const size_t kDmiPoolSize = 800; - - // Used to indicate whether this object has been initialized. - bool inited_; - - // Spelling trie. - const SpellingTrie *spl_trie_; - - // Used to indicate this switcher status: when "xian" is parseed, should - // "xi an" also be extended. Default is false. - // These cases include: xia, xian, xiang, zhuan, jiang..., etc. The string - // should be valid for a FULL spelling, or a combination of two spellings, - // first of which is a FULL id too. So even it is true, "da" will never be - // split into "d a", because "d" is not a full spelling id. - bool xi_an_enabled_; - - // System dictionary. - DictTrie* dict_trie_; - - // User dictionary. - AtomDictBase* user_dict_; - - // Spelling parser. - SpellingParser* spl_parser_; - - // The maximum allowed length of spelling string (such as a Pinyin string). - size_t max_sps_len_; - - // The maximum allowed length of a result Chinese string. - size_t max_hzs_len_; - - // Pinyin string. Max length: kMaxRowNum - 1 - char pys_[kMaxRowNum]; - - // The length of the string that has been decoded successfully. - size_t pys_decoded_len_; - - // Shared buffer for multiple purposes. - size_t *share_buf_; - - MatrixNode *mtrx_nd_pool_; - PoolPosType mtrx_nd_pool_used_; // How many nodes used in the pool - DictMatchInfo *dmi_pool_; - PoolPosType dmi_pool_used_; // How many items used in the pool - - MatrixRow *matrix_; // The first row is for starting - - DictExtPara *dep_; // Parameter used to extend DMI nodes. - - NPredictItem *npre_items_; // Used to do prediction - size_t npre_items_len_; - - // The starting positions and lemma ids for the full sentence candidate. - size_t lma_id_num_; - uint16 lma_start_[kMaxRowNum]; // Counted in spelling ids. - LemmaIdType lma_id_[kMaxRowNum]; - size_t fixed_lmas_; - - // If fixed_lmas_ is bigger than i, Element i is used to indicate whether - // the i'th lemma id in lma_id_ is the first candidate for that step. - // If all candidates are the first one for that step, the whole string can be - // decoded by the engine automatically, so no need to add it to user - // dictionary. (We are considering to add it to user dictionary in the - // future). - uint8 fixed_lmas_no1_[kMaxRowNum]; - - // Composing phrase - ComposingPhrase c_phrase_; - - // If dmi_c_phrase_ is true, the decoder will try to match the - // composing phrase (And definitely it will match successfully). If it - // is false, the decoder will try to match lemmas items in dictionaries. - bool dmi_c_phrase_; - - // The starting positions and spelling ids for the first full sentence - // candidate. - size_t spl_id_num_; // Number of splling ids - uint16 spl_start_[kMaxRowNum]; // Starting positions - uint16 spl_id_[kMaxRowNum]; // Spelling ids - // Used to remember the last fixed position, counted in Hanzi. - size_t fixed_hzs_; - - // Lemma Items with possibility score, two purposes: - // 1. In Viterbi decoding, this buffer is used to get all possible candidates - // for current step; - // 2. When the search is done, this buffer is used to get candiates from the - // first un-fixed step and show them to the user. - LmaPsbItem lpi_items_[kMaxLmaPsbItems]; - size_t lpi_total_; - - // Assign the pointers with NULL. The caller makes sure that all pointers are - // not valid before calling it. This function only will be called in the - // construction function and free_resource(). - void reset_pointers_to_null(); - - bool alloc_resource(); - - void free_resource(); - - // Reset the search space from ch_pos step. For example, if the original - // input Pinyin is "an", reset_search(1) will reset the search space to the - // result of "a". If the given position is out of range, return false. - // if clear_fixed_this_step is true, and the ch_pos step is a fixed step, - // clear its fixed status. if clear_dmi_his_step is true, clear the DMI nodes. - // If clear_mtrx_this_sTep is true, clear the mtrx nodes of this step. - // The DMI nodes will be kept. - // - // Note: this function should not destroy content of pys_. - bool reset_search(size_t ch_pos, bool clear_fixed_this_step, - bool clear_dmi_this_step, bool clear_mtrx_this_step); - - // Delete a part of the content in pys_. - void del_in_pys(size_t start, size_t len); - - // Delete a spelling id and its corresponding Chinese character, and merge - // the fixed lemmas into the composing phrase. - // del_spl_pos indicates which spelling id needs to be delete. - // This function will update the lemma and spelling segmentation information. - // The caller guarantees that fixed_lmas_ > 0 and del_spl_pos is within - // the fixed lemmas. - void merge_fixed_lmas(size_t del_spl_pos); - - // Get spelling start posistions and ids. The result will be stored in - // spl_id_num_, spl_start_[], spl_id_[]. - // fixed_hzs_ will be also assigned. - void get_spl_start_id(); - - // Get all lemma ids with match the given spelling id stream(shorter than the - // maximum length of a word). - // If pfullsent is not NULL, means the full sentence candidate may be the - // same with the coming lemma string, if so, remove that lemma. - // The result is sorted in descendant order by the frequency score. - size_t get_lpis(const uint16* splid_str, size_t splid_str_len, - LmaPsbItem* lma_buf, size_t max_lma_buf, - const char16 *pfullsent, bool sort_by_psb); - - uint16 get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, uint16 str_max); - - uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, - uint16 splids_max, bool arg_valid); - - - // Extend a DMI node with a spelling id. ext_len is the length of the rows - // to extend, actually, it is the size of the spelling string of splid. - // return value can be 1 or 0. - // 1 means a new DMI is filled in (dmi_pool_used_ is the next blank DMI in - // the pool). - // 0 means either the dmi node can not be extended with splid, or the splid - // is a Shengmu id, which is only used to get lpi_items, or the result node - // in DictTrie has no son, it is not nccessary to keep the new DMI. - // - // This function modifies the content of lpi_items_ and lpi_total_. - // lpi_items_ is used to get the LmaPsbItem list, lpi_total_ returns the size. - // The function's returned value has no relation with the value of lpi_num. - // - // If dmi == NULL, this function will extend the root node of DictTrie - // - // This function will not change dmi_nd_pool_used_. Please change it after - // calling this function if necessary. - // - // The caller should guarantees that NULL != dep. - size_t extend_dmi(DictExtPara *dep, DictMatchInfo *dmi_s); - - // Extend dmi for the composing phrase. - size_t extend_dmi_c(DictExtPara *dep, DictMatchInfo *dmi_s); - - // Extend a MatrixNode with the give LmaPsbItem list. - // res_row is the destination row number. - // This function does not change mtrx_nd_pool_used_. Please change it after - // calling this function if necessary. - // return 0 always. - size_t extend_mtrx_nd(MatrixNode *mtrx_nd, LmaPsbItem lpi_items[], - size_t lpi_num, PoolPosType dmi_fr, size_t res_row); - - - // Try to find a dmi node at step_to position, and the found dmi node should - // match the given spelling id strings. - PoolPosType match_dmi(size_t step_to, uint16 spl_ids[], uint16 spl_id_num); - - bool add_char(char ch); - bool prepare_add_char(char ch); - - // Called after prepare_add_char, so the input char has been saved. - bool add_char_qwerty(); - - // Prepare candidates from the last fixed hanzi position. - void prepare_candidates(); - - // Is the character in step pos a splitter character? - // The caller guarantees that the position is valid. - bool is_split_at(uint16 pos); - - void fill_dmi(DictMatchInfo *dmi, MileStoneHandle *handles, - PoolPosType dmi_fr, - uint16 spl_id, uint16 node_num, unsigned char dict_level, - bool splid_end_split, unsigned char splstr_len, - unsigned char all_full_id); - - size_t inner_predict(const char16 fixed_scis_ids[], uint16 scis_num, - char16 predict_buf[][kMaxPredictSize + 1], - size_t buf_len); - - // Add the first candidate to the user dictionary. - bool try_add_cand0_to_userdict(); - - // Add a user lemma to the user dictionary. This lemma is a subset of - // candidate 0. lma_from is from which lemma in lma_ids_, lma_num is the - // number of lemmas to be combined together as a new lemma. The caller - // gurantees that the combined new lemma's length is less or equal to - // kMaxLemmaSize. - bool add_lma_to_userdict(uint16 lma_from, uint16 lma_num, float score); - - // Update dictionary frequencies. - void update_dict_freq(); - - void debug_print_dmi(PoolPosType dmi_pos, uint16 nest_level); - - public: - MatrixSearch(); - ~MatrixSearch(); - - bool init(const char *fn_sys_dict, const char *fn_usr_dict); - - bool init_fd(int sys_fd, long start_offset, long length, - const char *fn_usr_dict); - - void set_max_lens(size_t max_sps_len, size_t max_hzs_len); - - void close(); - - void flush_cache(); - - void set_xi_an_switch(bool xi_an_enabled); - - bool get_xi_an_switch(); - - // Reset the search space. Equivalent to reset_search(0). - // If inited, always return true; - bool reset_search(); - - // Search a Pinyin string. - // Return value is the position successfully parsed. - size_t search(const char *py, size_t py_len); - - // Used to delete something in the Pinyin string kept by the engine, and do - // a re-search. - // Return value is the new length of Pinyin string kept by the engine which - // is parsed successfully. - // If is_pos_in_splid is false, pos is used to indicate that pos-th Pinyin - // character needs to be deleted. If is_pos_in_splid is true, all Pinyin - // characters for pos-th spelling id needs to be deleted. - // If the deleted character(s) is just after a fixed lemma or sub lemma in - // composing phrase, clear_fixed_this_step indicates whether we needs to - // unlock the last fixed lemma or sub lemma. - // If is_pos_in_splid is false, and pos-th character is in the range for the - // fixed lemmas or composing string, this function will do nothing and just - // return the result of the previous search. - size_t delsearch(size_t pos, bool is_pos_in_splid, - bool clear_fixed_this_step); - - // Get the number of candiates, called after search(). - size_t get_candidate_num(); - - // Get the Pinyin string stored by the engine. - // *decoded_len returns the length of the successfully decoded string. - const char* get_pystr(size_t *decoded_len); - - // Get the spelling boundaries for the first sentence candidate. - // Number of spellings will be returned. The number of valid elements in - // spl_start is one more than the return value because the last one is used - // to indicate the beginning of the next un-input speling. - // For a Pinyin "women", the returned value is 2, spl_start is [0, 2, 5] . - size_t get_spl_start(const uint16 *&spl_start); - - // Get one candiate string. If full sentence candidate is available, it will - // be the first one. - char16* get_candidate(size_t cand_id, char16 *cand_str, size_t max_len); - - // Get the first candiate, which is a "full sentence". - // retstr_len is not NULL, it will be used to return the string length. - // If only_unfixed is true, only unfixed part will be fetched. - char16* get_candidate0(char16* cand_str, size_t max_len, - uint16 *retstr_len, bool only_unfixed); - - // Choose a candidate. The decoder will do a search after the fixed position. - size_t choose(size_t cand_id); - - // Cancel the last choosing operation, and return the new number of choices. - size_t cancel_last_choice(); - - // Get the length of fixed Hanzis. - size_t get_fixedlen(); - - size_t get_predicts(const char16 fixed_buf[], - char16 predict_buf[][kMaxPredictSize + 1], - size_t buf_len); -}; -} - -#endif // PINYINIME_ANDPY_INCLUDE_MATRIXSEARCH_H__ diff --git a/PinyinIME/jni/include/mystdlib.h b/PinyinIME/jni/include/mystdlib.h deleted file mode 100644 index dfcf980..0000000 --- a/PinyinIME/jni/include/mystdlib.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_MYSTDLIB_H__ -#define PINYINIME_INCLUDE_MYSTDLIB_H__ - -#include - -namespace ime_pinyin { - -void myqsort(void *p, size_t n, size_t es, - int (*cmp)(const void *, const void *)); - -void *mybsearch(const void *key, const void *base, - size_t nmemb, size_t size, - int (*compar)(const void *, const void *)); -} - -#endif // PINYINIME_INCLUDE_MYSTDLIB_H__ diff --git a/PinyinIME/jni/include/ngram.h b/PinyinIME/jni/include/ngram.h deleted file mode 100644 index ad6c304..0000000 --- a/PinyinIME/jni/include/ngram.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_NGRAM_H__ -#define PINYINIME_INCLUDE_NGRAM_H__ - -#include -#include -#include "./dictdef.h" - -namespace ime_pinyin { - -typedef unsigned char CODEBOOK_TYPE; - -static const size_t kCodeBookSize = 256; - -class NGram { - public: - // The maximum score of a lemma item. - static const LmaScoreType kMaxScore = 0x3fff; - - // In order to reduce the storage size, the original log value is amplified by - // kScoreAmplifier, and we use LmaScoreType to store. - // After this process, an item with a lower score has a higher frequency. - static const int kLogValueAmplifier = -800; - - // System words' total frequency. It is not the real total frequency, instead, - // It is only used to adjust system lemmas' scores when the user dictionary's - // total frequency changes. - // In this version, frequencies of system lemmas are fixed. We are considering - // to make them changable in next version. - static const size_t kSysDictTotalFreq = 100000000; - - private: - - static NGram* instance_; - - bool initialized_; - size_t idx_num_; - - size_t total_freq_none_sys_; - - // Score compensation for system dictionary lemmas. - // Because after user adds some user lemmas, the total frequency changes, and - // we use this value to normalize the score. - float sys_score_compensation_; - -#ifdef ___BUILD_MODEL___ - double *freq_codes_df_; -#endif - LmaScoreType *freq_codes_; - CODEBOOK_TYPE *lma_freq_idx_; - - public: - NGram(); - ~NGram(); - - static NGram& get_instance(); - - bool save_ngram(FILE *fp); - bool load_ngram(FILE *fp); - - // Set the total frequency of all none system dictionaries. - void set_total_freq_none_sys(size_t freq_none_sys); - - float get_uni_psb(LemmaIdType lma_id); - - // Convert a probability to score. Actually, the score will be limited to - // kMaxScore, but at runtime, we also need float expression to get accurate - // value of the score. - // After the conversion, a lower score indicates a higher probability of the - // item. - static float convert_psb_to_score(double psb); - -#ifdef ___BUILD_MODEL___ - // For constructing the unigram mode model. - bool build_unigram(LemmaEntry *lemma_arr, size_t num, - LemmaIdType next_idx_unused); -#endif -}; -} - -#endif // PINYINIME_INCLUDE_NGRAM_H__ diff --git a/PinyinIME/jni/include/pinyinime.h b/PinyinIME/jni/include/pinyinime.h deleted file mode 100644 index 0744ec7..0000000 --- a/PinyinIME/jni/include/pinyinime.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_ANDPYIME_H__ -#define PINYINIME_INCLUDE_ANDPYIME_H__ - -#include -#include "./dictdef.h" - -#ifdef __cplusplus -extern "C" { -#endif - - namespace ime_pinyin { - - /** - * Open the decoder engine via the system and user dictionary file names. - * - * @param fn_sys_dict The file name of the system dictionary. - * @param fn_usr_dict The file name of the user dictionary. - * @return true if open the decoder engine successfully. - */ - bool im_open_decoder(const char *fn_sys_dict, const char *fn_usr_dict); - - /** - * Open the decoder engine via the system dictionary FD and user dictionary - * file name. Because on Android, the system dictionary is embedded in the - * whole application apk file. - * - * @param sys_fd The file in which the system dictionary is embedded. - * @param start_offset The starting position of the system dictionary in the - * file sys_fd. - * @param length The length of the system dictionary in the file sys_fd, - * counted in byte. - * @return true if succeed. - */ - bool im_open_decoder_fd(int sys_fd, long start_offset, long length, - const char *fn_usr_dict); - - /** - * Close the decoder engine. - */ - void im_close_decoder(); - - /** - * Set maximum limitations for decoding. If this function is not called, - * default values will be used. For example, due to screen size limitation, - * the UI engine of the IME can only show a certain number of letters(input) - * to decode, and a certain number of Chinese characters(output). If after - * user adds a new letter, the input or the output string is longer than the - * limitations, the engine will discard the recent letter. - * - * @param max_sps_len Maximum length of the spelling string(Pinyin string). - * @max_hzs_len Maximum length of the decoded Chinese character string. - */ - void im_set_max_lens(size_t max_sps_len, size_t max_hzs_len); - - /** - * Flush cached data to persistent memory. Because at runtime, in order to - * achieve best performance, some data is only store in memory. - */ - void im_flush_cache(); - - /** - * Use a spelling string(Pinyin string) to search. The engine will try to do - * an incremental search based on its previous search result, so if the new - * string has the same prefix with the previous one stored in the decoder, - * the decoder will only continue the search from the end of the prefix. - * If the caller needs to do a brand new search, please call im_reset_search() - * first. Calling im_search() is equivalent to calling im_add_letter() one by - * one. - * - * @param sps_buf The spelling string buffer to decode. - * @param sps_len The length of the spelling string buffer. - * @return The number of candidates. - */ - size_t im_search(const char* sps_buf, size_t sps_len); - - /** - * Make a delete operation in the current search result, and make research if - * necessary. - * - * @param pos The posistion of char in spelling string to delete, or the - * position of spelling id in result string to delete. - * @param is_pos_in_splid Indicate whether the pos parameter is the position - * in the spelling string, or the position in the result spelling id string. - * @return The number of candidates. - */ - size_t im_delsearch(size_t pos, bool is_pos_in_splid, - bool clear_fixed_this_step); - - /** - * Reset the previous search result. - */ - void im_reset_search(); - - /** - * Add a Pinyin letter to the current spelling string kept by decoder. If the - * decoder fails in adding the letter, it will do nothing. im_get_sps_str() - * can be used to get the spelling string kept by decoder currently. - * - * @param ch The letter to add. - * @return The number of candidates. - */ - size_t im_add_letter(char ch); - - /** - * Get the spelling string kept by the decoder. - * - * @param decoded_len Used to return how many characters in the spelling - * string is successfully parsed. - * @return The spelling string kept by the decoder. - */ - const char *im_get_sps_str(size_t *decoded_len); - - /** - * Get a candidate(or choice) string. - * - * @param cand_id The id to get a candidate. Started from 0. Usually, id 0 - * is a sentence-level candidate. - * @param cand_str The buffer to store the candidate. - * @param max_len The maximum length of the buffer. - * @return cand_str if succeeds, otherwise NULL. - */ - char16* im_get_candidate(size_t cand_id, char16* cand_str, - size_t max_len); - - /** - * Get the segmentation information(the starting positions) of the spelling - * string. - * - * @param spl_start Used to return the starting posistions. - * @return The number of spelling ids. If it is L, there will be L+1 valid - * elements in spl_start, and spl_start[L] is the posistion after the end of - * the last spelling id. - */ - size_t im_get_spl_start_pos(const uint16 *&spl_start); - - /** - * Choose a candidate and make it fixed. If the candidate does not match - * the end of all spelling ids, new candidates will be provided from the - * first unfixed position. If the candidate matches the end of the all - * spelling ids, there will be only one new candidates, or the whole fixed - * sentence. - * - * @param cand_id The id of candidate to select and make it fixed. - * @return The number of candidates. If after the selection, the whole result - * string has been fixed, there will be only one candidate. - */ - size_t im_choose(size_t cand_id); - - /** - * Cancel the last selection, or revert the last operation of im_choose(). - * - * @return The number of candidates. - */ - size_t im_cancel_last_choice(); - - /** - * Get the number of fixed spelling ids, or Chinese characters. - * - * @return The number of fixed spelling ids, of Chinese characters. - */ - size_t im_get_fixed_len(); - - /** - * Cancel the input state and reset the search workspace. - */ - bool im_cancel_input(); - - /** - * Get prediction candiates based on the given fixed Chinese string as the - * history. - * - * @param his_buf The history buffer to do the prediction. It should be ended - * with '\0'. - * @param pre_buf Used to return prediction result list. - * @return The number of predicted result string. - */ - size_t im_get_predicts(const char16 *his_buf, - char16 (*&pre_buf)[kMaxPredictSize + 1]); - - /** - * Enable Shengmus in ShouZiMu mode. - */ - void im_enable_shm_as_szm(bool enable); - - /** - * Enable Yunmus in ShouZiMu mode. - */ - void im_enable_ym_as_szm(bool enable); -} - -#ifdef __cplusplus -} -#endif - -#endif // PINYINIME_INCLUDE_ANDPYIME_H__ diff --git a/PinyinIME/jni/include/searchutility.h b/PinyinIME/jni/include/searchutility.h deleted file mode 100644 index f135710..0000000 --- a/PinyinIME/jni/include/searchutility.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_ANDPY_INCLUDE_SEARCHCOMMON_H__ -#define PINYINIME_ANDPY_INCLUDE_SEARCHCOMMON_H__ - -#include -#include "./spellingtrie.h" - -namespace ime_pinyin { - -// Type used to identify the size of a pool, such as id pool, etc. -typedef uint16 PoolPosType; - -// Type used to identify a parsing mile stone in an atom dictionary. -typedef uint16 MileStoneHandle; - -// Type used to express a lemma and its probability score. -typedef struct { - size_t id:(kLemmaIdSize * 8); - size_t lma_len:4; - uint16 psb; // The score, the lower psb, the higher possibility. - // For single character items, we may also need Hanzi. - // For multiple characer items, ignore it. - char16 hanzi; -} LmaPsbItem, *PLmaPsbItem; - -// LmaPsbItem extended with string. -typedef struct { - LmaPsbItem lpi; - char16 str[kMaxLemmaSize + 1]; -} LmaPsbStrItem, *PLmaPsbStrItem; - - -typedef struct { - float psb; - char16 pre_hzs[kMaxPredictSize]; - uint16 his_len; // The length of the history used to do the prediction. -} NPredictItem, *PNPredictItem; - -// Parameter structure used to extend in a dictionary. All dictionaries -// receives the same DictExtPara and a dictionary specific MileStoneHandle for -// extending. -// -// When the user inputs a new character, AtomDictBase::extend_dict() will be -// called at least once for each dictionary. -// -// For example, when the user inputs "wm", extend_dict() will be called twice, -// and the DictExtPara parameter are as follows respectively: -// 1. splids = {w, m}; splids_extended = 1; ext_len = 1; step_no = 1; -// splid_end_split = false; id_start = wa(the first id start with 'w'); -// id_num = number of ids starting with 'w'. -// 2. splids = {m}; splids_extended = 0; ext_len = 1; step_no = 1; -// splid_end_split = false; id_start = wa; id_num = number of ids starting with -// 'w'. -// -// For string "women", one of the cases of the DictExtPara parameter is: -// splids = {wo, men}, splids_extended = 1, ext_len = 3 (length of "men"), -// step_no = 4; splid_end_split = false; id_start = men, id_num = 1. -// -typedef struct { - // Spelling ids for extending, there are splids_extended + 1 ids in the - // buffer. - // For a normal lemma, there can only be kMaxLemmaSize spelling ids in max, - // but for a composing phrase, there can kMaxSearchSteps spelling ids. - uint16 splids[kMaxSearchSteps]; - - // Number of ids that have been used before. splids[splids_extended] is the - // newly added id for the current extension. - uint16 splids_extended; - - // The step span of the extension. It is also the size of the string for - // the newly added spelling id. - uint16 ext_len; - - // The step number for the current extension. It is also the ending position - // in the input Pinyin string for the substring of spelling ids in splids[]. - // For example, when the user inputs "women", step_no = 4. - // This parameter may useful to manage the MileStoneHandle list for each - // step. When the user deletes a character from the string, MileStoneHandle - // objects for the the steps after that character should be reset; when the - // user begins a new string, all MileStoneHandle objects should be reset. - uint16 step_no; - - // Indicate whether the newly added spelling ends with a splitting character - bool splid_end_split; - - // If the newly added id is a half id, id_start is the first id of the - // corresponding full ids; if the newly added id is a full id, id_start is - // that id. - uint16 id_start; - - // If the newly added id is a half id, id_num is the number of corresponding - // ids; if it is a full id, id_num == 1. - uint16 id_num; -}DictExtPara, *PDictExtPara; - -bool is_system_lemma(LemmaIdType lma_id); -bool is_user_lemma(LemmaIdType lma_id); -bool is_composing_lemma(LemmaIdType lma_id); - -int cmp_lpi_with_psb(const void *p1, const void *p2); -int cmp_lpi_with_unified_psb(const void *p1, const void *p2); -int cmp_lpi_with_id(const void *p1, const void *p2); -int cmp_lpi_with_hanzi(const void *p1, const void *p2); - -int cmp_lpsi_with_str(const void *p1, const void *p2); - -int cmp_hanzis_1(const void *p1, const void *p2); -int cmp_hanzis_2(const void *p1, const void *p2); -int cmp_hanzis_3(const void *p1, const void *p2); -int cmp_hanzis_4(const void *p1, const void *p2); -int cmp_hanzis_5(const void *p1, const void *p2); -int cmp_hanzis_6(const void *p1, const void *p2); -int cmp_hanzis_7(const void *p1, const void *p2); -int cmp_hanzis_8(const void *p1, const void *p2); - -int cmp_npre_by_score(const void *p1, const void *p2); -int cmp_npre_by_hislen_score(const void *p1, const void *p2); -int cmp_npre_by_hanzi_score(const void *p1, const void *p2); - - -size_t remove_duplicate_npre(NPredictItem *npre_items, size_t npre_num); - -size_t align_to_size_t(size_t size); - -} // namespace - -#endif // PINYINIME_ANDPY_INCLUDE_SEARCHCOMMON_H__ diff --git a/PinyinIME/jni/include/spellingtable.h b/PinyinIME/jni/include/spellingtable.h deleted file mode 100644 index fd79c6e..0000000 --- a/PinyinIME/jni/include/spellingtable.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_SPELLINGTABLE_H__ -#define PINYINIME_INCLUDE_SPELLINGTABLE_H__ - -#include -#include "./dictdef.h" - -namespace ime_pinyin { - -#ifdef ___BUILD_MODEL___ - -const size_t kMaxSpellingSize = kMaxPinyinSize; - -typedef struct { - char str[kMaxSpellingSize + 1]; - double freq; -} RawSpelling, *PRawSpelling; - -// This class is used to store the spelling strings -// The length of the input spelling string should be less or equal to the -// spelling_size_ (set by init_table). If the input string is too long, -// we only keep its first spelling_size_ chars. -class SpellingTable { - private: - static const size_t kNotSupportNum = 3; - static const char kNotSupportList[kNotSupportNum][kMaxSpellingSize + 1]; - - bool need_score_; - - size_t spelling_max_num_; - - RawSpelling *raw_spellings_; - - // Used to store spelling strings. If the spelling table needs to calculate - // score, an extra char after each spelling string is the score. - // An item with a lower score has a higher probability. - char *spelling_buf_; - size_t spelling_size_; - - double total_freq_; - - size_t spelling_num_; - - double score_amplifier_; - - unsigned char average_score_; - - // If frozen is true, put_spelling() and contain() are not allowed to call. - bool frozen_; - - size_t get_hash_pos(const char* spelling_str); - size_t hash_pos_next(size_t hash_pos); - void free_resource(); - public: - SpellingTable(); - ~SpellingTable(); - - // pure_spl_size is the pure maximum spelling string size. For example, - // "zhuang" is the longgest item in Pinyin, so pure_spl_size should be 6. - // spl_max_num is the maximum number of spelling strings to store. - // need_score is used to indicate whether the caller needs to calculate a - // score for each spelling. - bool init_table(size_t pure_spl_size, size_t spl_max_num, bool need_score); - - // Put a spelling string to the table. - // It always returns false if called after arrange() withtout a new - // init_table() operation. - // freq is the spelling's occuring count. - // If the spelling has been in the table, occuring count will accumulated. - bool put_spelling(const char* spelling_str, double spl_count); - - // Test whether a spelling string is in the table. - // It always returns false, when being called after arrange() withtout a new - // init_table() operation. - bool contain(const char* spelling_str); - - // Sort the spelling strings and put them from the begin of the buffer. - // Return the pointer of the sorted spelling strings. - // item_size and spl_num return the item size and number of spelling. - // Because each spelling uses a '\0' as terminator, the returned item_size is - // at least one char longer than the spl_size parameter specified by - // init_table(). If the table is initialized to calculate score, item_size - // will be increased by 1, and current_spl_str[item_size - 1] stores an - // unsinged char score. - // An item with a lower score has a higher probability. - // Do not call put_spelling() and contains() after arrange(). - const char* arrange(size_t *item_size, size_t *spl_num); - - float get_score_amplifier(); - - unsigned char get_average_score(); -}; -#endif // ___BUILD_MODEL___ -} - -#endif // PINYINIME_INCLUDE_SPELLINGTABLE_H__ diff --git a/PinyinIME/jni/include/spellingtrie.h b/PinyinIME/jni/include/spellingtrie.h deleted file mode 100644 index 4438757..0000000 --- a/PinyinIME/jni/include/spellingtrie.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_SPELLINGTRIE_H__ -#define PINYINIME_INCLUDE_SPELLINGTRIE_H__ - -#include -#include -#include "./dictdef.h" - -namespace ime_pinyin { - -static const unsigned short kFullSplIdStart = kHalfSpellingIdNum + 1; - -// Node used for the trie of spellings -struct SpellingNode { - SpellingNode *first_son; - // The spelling id for each node. If you need more bits to store - // spelling id, please adjust this structure. - uint16 spelling_idx:11; - uint16 num_of_son:5; - char char_this_node; - unsigned char score; -}; - -class SpellingTrie { - private: - static const int kMaxYmNum = 64; - static const size_t kValidSplCharNum = 26; - - static const uint16 kHalfIdShengmuMask = 0x01; - static const uint16 kHalfIdYunmuMask = 0x02; - static const uint16 kHalfIdSzmMask = 0x04; - - // Map from half spelling id to single char. - // For half ids of Zh/Ch/Sh, map to z/c/s (low case) respectively. - // For example, 1 to 'A', 2 to 'B', 3 to 'C', 4 to 'c', 5 to 'D', ..., - // 28 to 'Z', 29 to 'z'. - // [0] is not used to achieve better efficiency. - static const char kHalfId2Sc_[kFullSplIdStart + 1]; - - static unsigned char char_flags_[]; - static SpellingTrie* instance_; - - // The spelling table - char *spelling_buf_; - - // The size of longest spelling string, includes '\0' and an extra char to - // store score. For example, "zhuang" is the longgest item in Pinyin list, - // so spelling_size_ is 8. - // Structure: The string ended with '\0' + score char. - // An item with a lower score has a higher probability. - size_t spelling_size_; - - // Number of full spelling ids. - size_t spelling_num_; - - float score_amplifier_; - unsigned char average_score_; - - // The Yunmu id list for the spelling ids (for half ids of Shengmu, - // the Yunmu id is 0). - // The length of the list is spelling_num_ + kFullSplIdStart, - // so that spl_ym_ids_[splid] is the Yunmu id of the splid. - uint8 *spl_ym_ids_; - - // The Yunmu table. - // Each Yunmu will be assigned with Yunmu id from 1. - char *ym_buf_; - size_t ym_size_; // The size of longest Yunmu string, '\0'included. - size_t ym_num_; - - // The spelling string just queried - char *splstr_queried_; - - // The spelling string just queried - char16 *splstr16_queried_; - - // The root node of the spelling tree - SpellingNode* root_; - - // If a none qwerty key such as a fnction key like ENTER is given, this node - // will be used to indicate that this is not a QWERTY node. - SpellingNode* dumb_node_; - - // If a splitter key is pressed, this node will be used to indicate that this - // is a splitter key. - SpellingNode* splitter_node_; - - // Used to get the first level sons. - SpellingNode* level1_sons_[kValidSplCharNum]; - - // The full spl_id range for specific half id. - // h2f means half to full. - // A half id can be a ShouZiMu id (id to represent the first char of a full - // spelling, including Shengmu and Yunmu), or id of zh/ch/sh. - // [1..kFullSplIdStart-1] is the arrange of half id. - uint16 h2f_start_[kFullSplIdStart]; - uint16 h2f_num_[kFullSplIdStart]; - - // Map from full id to half id. - uint16 *f2h_; - -#ifdef ___BUILD_MODEL___ - // How many node used to build the trie. - size_t node_num_; -#endif - - SpellingTrie(); - - void free_son_trie(SpellingNode* node); - - // Construct a subtree using a subset of the spelling array (from - // item_star to item_end). - // Member spelliing_buf_ and spelling_size_ should be valid. - // parent is used to update its num_of_son and score. - SpellingNode* construct_spellings_subset(size_t item_start, size_t item_end, - size_t level, SpellingNode *parent); - bool build_f2h(); - - // The caller should guarantee ch >= 'A' && ch <= 'Z' - bool is_shengmu_char(char ch) const; - - // The caller should guarantee ch >= 'A' && ch <= 'Z' - bool is_yunmu_char(char ch) const; - -#ifdef ___BUILD_MODEL___ - // Given a spelling string, return its Yunmu string. - // The caller guaratees spl_str is valid. - const char* get_ym_str(const char *spl_str); - - // Build the Yunmu list, and the mapping relation between the full ids and the - // Yunmu ids. This functin is called after the spelling trie is built. - bool build_ym_info(); -#endif - - friend class SpellingParser; - friend class SmartSplParser; - friend class SmartSplParser2; - - public: - ~SpellingTrie(); - - inline static bool is_valid_spl_char(char ch) { - return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); - } - - // The caller guarantees that the two chars are valid spelling chars. - inline static bool is_same_spl_char(char ch1, char ch2) { - return ch1 == ch2 || ch1 - ch2 == 'a' - 'A' || ch2 - ch1 == 'a' - 'A'; - } - - // Construct the tree from the input pinyin array - // The given string list should have been sorted. - // score_amplifier is used to convert a possibility value into score. - // average_score is the average_score of all spellings. The dumb node is - // assigned with this score. - bool construct(const char* spelling_arr, size_t item_size, size_t item_num, - float score_amplifier, unsigned char average_score); - - // Test if the given id is a valid spelling id. - // If function returns true, the given splid may be updated like this: - // When 'A' is not enabled in ShouZiMu mode, the parsing result for 'A' is - // first given as a half id 1, but because 'A' is a one-char Yunmu and - // it is a valid id, it needs to updated to its corresponding full id. - bool if_valid_id_update(uint16 *splid) const; - - // Test if the given id is a half id. - bool is_half_id(uint16 splid) const; - - bool is_full_id(uint16 splid) const; - - // Test if the given id is a one-char Yunmu id (obviously, it is also a half - // id), such as 'A', 'E' and 'O'. - bool is_half_id_yunmu(uint16 splid) const; - - // Test if this char is a ShouZiMu char. This ShouZiMu char may be not enabled. - // For Pinyin, only i/u/v is not a ShouZiMu char. - // The caller should guarantee that ch >= 'A' && ch <= 'Z' - bool is_szm_char(char ch) const; - - // Test If this char is enabled in ShouZiMu mode. - // The caller should guarantee that ch >= 'A' && ch <= 'Z' - bool szm_is_enabled(char ch) const; - - // Enable/disable Shengmus in ShouZiMu mode(using the first char of a spelling - // to input). - void szm_enable_shm(bool enable); - - // Enable/disable Yunmus in ShouZiMu mode. - void szm_enable_ym(bool enable); - - // Test if this char is enabled in ShouZiMu mode. - // The caller should guarantee ch >= 'A' && ch <= 'Z' - bool is_szm_enabled(char ch) const; - - // Return the number of full ids for the given half id. - uint16 half2full_num(uint16 half_id) const; - - // Return the number of full ids for the given half id, and fill spl_id_start - // to return the first full id. - uint16 half_to_full(uint16 half_id, uint16 *spl_id_start) const; - - // Return the corresponding half id for the given full id. - // Not frequently used, low efficient. - // Return 0 if fails. - uint16 full_to_half(uint16 full_id) const; - - // To test whether a half id is compatible with a full id. - // Generally, when half_id == full_to_half(full_id), return true. - // But for "Zh, Ch, Sh", if fussy mode is on, half id for 'Z' is compatible - // with a full id like "Zhe". (Fussy mode is not ready). - bool half_full_compatible(uint16 half_id, uint16 full_id) const; - - static const SpellingTrie* get_cpinstance(); - - static SpellingTrie& get_instance(); - - // Save to the file stream - bool save_spl_trie(FILE *fp); - - // Load from the file stream - bool load_spl_trie(FILE *fp); - - // Get the number of spellings - size_t get_spelling_num(); - - // Return the Yunmu id for the given Yunmu string. - // If the string is not valid, return 0; - uint8 get_ym_id(const char* ym_str); - - // Get the readonly Pinyin string for a given spelling id - const char* get_spelling_str(uint16 splid); - - // Get the readonly Pinyin string for a given spelling id - const char16* get_spelling_str16(uint16 splid); - - // Get Pinyin string for a given spelling id. Return the length of the - // string, and fill-in '\0' at the end. - size_t get_spelling_str16(uint16 splid, char16 *splstr16, - size_t splstr16_len); -}; -} - -#endif // PINYINIME_INCLUDE_SPELLINGTRIE_H__ diff --git a/PinyinIME/jni/include/splparser.h b/PinyinIME/jni/include/splparser.h deleted file mode 100644 index d783bd7..0000000 --- a/PinyinIME/jni/include/splparser.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_SPLPARSER_H__ -#define PINYINIME_INCLUDE_SPLPARSER_H__ - -#include "./dictdef.h" -#include "./spellingtrie.h" - -namespace ime_pinyin { - -class SpellingParser { - protected: - const SpellingTrie *spl_trie_; - - public: - SpellingParser(); - - // Given a string, parse it into a spelling id stream. - // If the whole string are sucessfully parsed, last_is_pre will be true; - // if the whole string is not fullly parsed, last_is_pre will return whether - // the last part of the string is a prefix of a full spelling string. For - // example, given string "zhengzhon", "zhon" is not a valid speling, but it is - // the prefix of "zhong". - // - // If splstr starts with a character not in ['a'-z'] (it is a split char), - // return 0. - // Split char can only appear in the middle of the string or at the end. - uint16 splstr_to_idxs(const char *splstr, uint16 str_len, uint16 splidx[], - uint16 start_pos[], uint16 max_size, bool &last_is_pre); - - // Similar to splstr_to_idxs(), the only difference is that splstr_to_idxs() - // convert single-character Yunmus into half ids, while this function converts - // them into full ids. - uint16 splstr_to_idxs_f(const char *splstr, uint16 str_len, uint16 splidx[], - uint16 start_pos[], uint16 max_size, bool &last_is_pre); - - // Similar to splstr_to_idxs(), the only difference is that this function - // uses char16 instead of char8. - uint16 splstr16_to_idxs(const char16 *splstr, uint16 str_len, uint16 splidx[], - uint16 start_pos[], uint16 max_size, bool &last_is_pre); - - // Similar to splstr_to_idxs_f(), the only difference is that this function - // uses char16 instead of char8. - uint16 splstr16_to_idxs_f(const char16 *splstr16, uint16 str_len, - uint16 splidx[], uint16 start_pos[], - uint16 max_size, bool &last_is_pre); - - // If the given string is a spelling, return the id, others, return 0. - // If the give string is a single char Yunmus like "A", and the char is - // enabled in ShouZiMu mode, the returned spelling id will be a half id. - // When the returned spelling id is a half id, *is_pre returns whether it - // is a prefix of a full spelling string. - uint16 get_splid_by_str(const char *splstr, uint16 str_len, bool *is_pre); - - // If the given string is a spelling, return the id, others, return 0. - // If the give string is a single char Yunmus like "a", no matter the char - // is enabled in ShouZiMu mode or not, the returned spelling id will be - // a full id. - // When the returned spelling id is a half id, *p_is_pre returns whether it - // is a prefix of a full spelling string. - uint16 get_splid_by_str_f(const char *splstr, uint16 str_len, bool *is_pre); - - // Splitter chars are not included. - bool is_valid_to_parse(char ch); - - // When auto-correction is not enabled, get_splid_by_str() will be called to - // return the single result. When auto-correction is enabled, this function - // will be called to get the results. Auto-correction is not ready. - // full_id_num returns number of full spelling ids. - // is_pre returns whether the given string is the prefix of a full spelling - // string. - // If splstr starts with a character not in [a-zA-Z] (it is a split char), - // return 0. - // Split char can only appear in the middle of the string or at the end. - // The caller should guarantee NULL != splstr && str_len > 0 && NULL != splidx - uint16 get_splids_parallel(const char *splstr, uint16 str_len, - uint16 splidx[], uint16 max_size, - uint16 &full_id_num, bool &is_pre); -}; -} - -#endif // PINYINIME_INCLUDE_SPLPARSER_H__ diff --git a/PinyinIME/jni/include/sync.h b/PinyinIME/jni/include/sync.h deleted file mode 100644 index bf42d1f..0000000 --- a/PinyinIME/jni/include/sync.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_SYNC_H__ -#define PINYINIME_INCLUDE_SYNC_H__ - -#define ___SYNC_ENABLED___ - -#ifdef ___SYNC_ENABLED___ - -#include "userdict.h" - -namespace ime_pinyin { - -// Class for user dictionary synchronization -// This class is not thread safe -// Normal invoking flow will be -// begin() -> -// put_lemmas() x N -> -// { -// get_lemmas() -> -// [ get_last_got_count() ] -> -// clear_last_got() -> -// } x N -> -// finish() -class Sync { - public: - Sync(); - ~Sync(); - - static const int kUserDictMaxLemmaCount = 5000; - static const int kUserDictMaxLemmaSize = 200000; - static const int kUserDictRatio = 20; - - bool begin(const char * filename); - - // Merge lemmas downloaded from sync server into local dictionary - // lemmas, lemmas string encoded in UTF16LE - // len, length of lemmas string - // Return how many lemmas merged successfully - int put_lemmas(char16 * lemmas, int len); - - // Get local new user lemmas into UTF16LE string - // str, buffer ptr to store new user lemmas - // size, size of buffer - // Return length of returned buffer in measure of UTF16LE - int get_lemmas(char16 * str, int size); - - // Return lemmas count in last get_lemmas() - int get_last_got_count(); - - // Return total lemmas count need get_lemmas() - int get_total_count(); - - // Clear lemmas got by recent get_lemmas() - void clear_last_got(); - - void finish(); - - int get_capacity(); - - private: - UserDict * userdict_; - char * dictfile_; - int last_count_; -}; - -} - -#endif - -#endif // PINYINIME_INCLUDE_SYNC_H__ diff --git a/PinyinIME/jni/include/userdict.h b/PinyinIME/jni/include/userdict.h deleted file mode 100644 index 02da218..0000000 --- a/PinyinIME/jni/include/userdict.h +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_USERDICT_H__ -#define PINYINIME_INCLUDE_USERDICT_H__ - -#define ___CACHE_ENABLED___ -#define ___SYNC_ENABLED___ -#define ___PREDICT_ENABLED___ - -// Debug performance for operations -// #define ___DEBUG_PERF___ - -#include -#include "atomdictbase.h" - -namespace ime_pinyin { - -class UserDict : public AtomDictBase { - public: - UserDict(); - ~UserDict(); - - bool load_dict(const char *file_name, LemmaIdType start_id, - LemmaIdType end_id); - - bool close_dict(); - - size_t number_of_lemmas(); - - void reset_milestones(uint16 from_step, MileStoneHandle from_handle); - - MileStoneHandle extend_dict(MileStoneHandle from_handle, - const DictExtPara *dep, LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num); - - size_t get_lpis(const uint16 *splid_str, uint16 splid_str_len, - LmaPsbItem *lpi_items, size_t lpi_max); - - uint16 get_lemma_str(LemmaIdType id_lemma, char16* str_buf, - uint16 str_max); - - uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, - uint16 splids_max, bool arg_valid); - - size_t predict(const char16 last_hzs[], uint16 hzs_len, - NPredictItem *npre_items, size_t npre_max, - size_t b4_used); - - // Full spelling ids are required - LemmaIdType put_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count); - - LemmaIdType update_lemma(LemmaIdType lemma_id, int16 delta_count, - bool selected); - - LemmaIdType get_lemma_id(char16 lemma_str[], uint16 splids[], - uint16 lemma_len); - - LmaScoreType get_lemma_score(LemmaIdType lemma_id); - - LmaScoreType get_lemma_score(char16 lemma_str[], uint16 splids[], - uint16 lemma_len); - - bool remove_lemma(LemmaIdType lemma_id); - - size_t get_total_lemma_count(); - void set_total_lemma_count_of_others(size_t count); - - void flush_cache(); - - void set_limit(uint32 max_lemma_count, uint32 max_lemma_size, - uint32 reclaim_ratio); - - void reclaim(); - - void defragment(); - -#ifdef ___SYNC_ENABLED___ - void clear_sync_lemmas(unsigned int start, unsigned int end); - - int get_sync_count(); - - LemmaIdType put_lemma_no_sync(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count, uint64 lmt); - /** - * Add lemmas encoded in UTF-16LE into dictionary without adding sync flag. - * - * @param lemmas in format of 'wo men,WM,0.32;da jia,DJ,0.12' - * @param len length of lemmas string in UTF-16LE - * @return newly added lemma count - */ - int put_lemmas_no_sync_from_utf16le_string(char16 * lemmas, int len); - - /** - * Get lemmas need sync to a UTF-16LE string of above format. - * Note: input buffer (str) must not be too small. If str is too small to - * contain single one lemma, there might be a dead loop. - * - * @param str buffer to write lemmas - * @param size buffer size in UTF-16LE - * @param count output value of lemma returned - * @return UTF-16LE string length - */ - int get_sync_lemmas_in_utf16le_string_from_beginning( - char16 * str, int size, int * count); - -#endif - - struct UserDictStat { - uint32 version; - const char * file_name; - struct timeval load_time; - struct timeval last_update; - uint32 disk_size; - uint32 lemma_count; - uint32 lemma_size; - uint32 delete_count; - uint32 delete_size; -#ifdef ___SYNC_ENABLED___ - uint32 sync_count; -#endif - uint32 reclaim_ratio; - uint32 limit_lemma_count; - uint32 limit_lemma_size; - }; - - bool state(UserDictStat * stat); - - private: - uint32 total_other_nfreq_; - struct timeval load_time_; - LemmaIdType start_id_; - uint32 version_; - uint8 * lemmas_; - - // In-Memory-Only flag for each lemma - static const uint8 kUserDictLemmaFlagRemove = 1; - // Inuse lemmas' offset - uint32 * offsets_; - // Highest bit in offset tells whether corresponding lemma is removed - static const uint32 kUserDictOffsetFlagRemove = (1 << 31); - // Maximum possible for the offset - static const uint32 kUserDictOffsetMask = ~(kUserDictOffsetFlagRemove); - // Bit width for last modified time, from 1 to 16 - static const uint32 kUserDictLMTBitWidth = 16; - // Granularity for last modified time in second - static const uint32 kUserDictLMTGranularity = 60 * 60 * 24 * 7; - // Maximum frequency count - static const uint16 kUserDictMaxFrequency = 0xFFFF; - -#define COARSE_UTC(year, month, day, hour, minute, second) \ - ( \ - (year - 1970) * 365 * 24 * 60 * 60 + \ - (month - 1) * 30 * 24 * 60 * 60 + \ - (day - 1) * 24 * 60 * 60 + \ - (hour - 0) * 60 * 60 + \ - (minute - 0) * 60 + \ - (second - 0) \ - ) - static const uint64 kUserDictLMTSince = COARSE_UTC(2009, 1, 1, 0, 0, 0); - - // Correspond to offsets_ - uint32 * scores_; - // Following two fields are only valid in memory - uint32 * ids_; -#ifdef ___PREDICT_ENABLED___ - uint32 * predicts_; -#endif -#ifdef ___SYNC_ENABLED___ - uint32 * syncs_; - size_t sync_count_size_; -#endif - uint32 * offsets_by_id_; - - size_t lemma_count_left_; - size_t lemma_size_left_; - - const char * dict_file_; - - // Be sure size is 4xN - struct UserDictInfo { - // When limitation reached, how much percentage will be reclaimed (1 ~ 100) - uint32 reclaim_ratio; - // maximum lemma count, 0 means no limitation - uint32 limit_lemma_count; - // Maximum lemma size, it's different from - // whole disk file size or in-mem dict size - // 0 means no limitation - uint32 limit_lemma_size; - // Total lemma count including deleted and inuse - // Also indicate offsets_ size - uint32 lemma_count; - // Total size of lemmas including used and freed - uint32 lemma_size; - // Freed lemma count - uint32 free_count; - // Freed lemma size in byte - uint32 free_size; -#ifdef ___SYNC_ENABLED___ - uint32 sync_count; -#endif - int32 total_nfreq; - } dict_info_; - - static const uint32 kUserDictVersion = 0x0ABCDEF0; - - static const uint32 kUserDictPreAlloc = 32; - static const uint32 kUserDictAverageNchar = 8; - - enum UserDictState { - // Keep in order - USER_DICT_NONE = 0, - USER_DICT_SYNC, -#ifdef ___SYNC_ENABLED___ - USER_DICT_SYNC_DIRTY, -#endif - USER_DICT_SCORE_DIRTY, - USER_DICT_OFFSET_DIRTY, - USER_DICT_LEMMA_DIRTY, - - USER_DICT_DEFRAGMENTED, - } state_; - - struct UserDictSearchable { - uint16 splids_len; - uint16 splid_start[kMaxLemmaSize]; - uint16 splid_count[kMaxLemmaSize]; - // Compact inital letters for both FuzzyCompareSpellId and cache system - uint32 signature[kMaxLemmaSize / 4]; - }; - -#ifdef ___CACHE_ENABLED___ - enum UserDictCacheType { - USER_DICT_CACHE, - USER_DICT_MISS_CACHE, - }; - - static const int kUserDictCacheSize = 4; - static const int kUserDictMissCacheSize = kMaxLemmaSize - 1; - - struct UserDictMissCache { - uint32 signatures[kUserDictMissCacheSize][kMaxLemmaSize / 4]; - uint16 head, tail; - } miss_caches_[kMaxLemmaSize]; - - struct UserDictCache { - uint32 signatures[kUserDictCacheSize][kMaxLemmaSize / 4]; - uint32 offsets[kUserDictCacheSize]; - uint32 lengths[kUserDictCacheSize]; - // Ring buffer - uint16 head, tail; - } caches_[kMaxLemmaSize]; - - void cache_init(); - - void cache_push(UserDictCacheType type, - UserDictSearchable *searchable, - uint32 offset, uint32 length); - - bool cache_hit(UserDictSearchable *searchable, - uint32 *offset, uint32 *length); - - bool load_cache(UserDictSearchable *searchable, - uint32 *offset, uint32 *length); - - void save_cache(UserDictSearchable *searchable, - uint32 offset, uint32 length); - - void reset_cache(); - - bool load_miss_cache(UserDictSearchable *searchable); - - void save_miss_cache(UserDictSearchable *searchable); - - void reset_miss_cache(); -#endif - - LmaScoreType translate_score(int f); - - int extract_score_freq(int raw_score); - - uint64 extract_score_lmt(int raw_score); - - inline int build_score(uint64 lmt, int freq); - - inline int64 utf16le_atoll(uint16 *s, int len); - - inline int utf16le_lltoa(int64 v, uint16 *s, int size); - - LemmaIdType _put_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count, uint64 lmt); - - size_t _get_lpis(const uint16 *splid_str, uint16 splid_str_len, - LmaPsbItem *lpi_items, size_t lpi_max, bool * need_extend); - - int _get_lemma_score(char16 lemma_str[], uint16 splids[], uint16 lemma_len); - - int _get_lemma_score(LemmaIdType lemma_id); - - int is_fuzzy_prefix_spell_id(const uint16 * id1, uint16 len1, - const UserDictSearchable *searchable); - - bool is_prefix_spell_id(const uint16 * fullids, - uint16 fulllen, const UserDictSearchable *searchable); - - uint32 get_dict_file_size(UserDictInfo * info); - - bool reset(const char *file); - - bool validate(const char *file); - - bool load(const char *file, LemmaIdType start_id); - - bool is_valid_state(); - - bool is_valid_lemma_id(LemmaIdType id); - - LemmaIdType get_max_lemma_id(); - - void set_lemma_flag(uint32 offset, uint8 flag); - - char get_lemma_flag(uint32 offset); - - char get_lemma_nchar(uint32 offset); - - uint16 * get_lemma_spell_ids(uint32 offset); - - uint16 * get_lemma_word(uint32 offset); - - // Prepare searchable to fasten locate process - void prepare_locate(UserDictSearchable *searchable, - const uint16 * splids, uint16 len); - - // Compare initial letters only - int32 fuzzy_compare_spell_id(const uint16 * id1, uint16 len1, - const UserDictSearchable *searchable); - - // Compare exactly two spell ids - // First argument must be a full id spell id - bool equal_spell_id(const uint16 * fullids, - uint16 fulllen, const UserDictSearchable *searchable); - - // Find first item by initial letters - int32 locate_first_in_offsets(const UserDictSearchable *searchable); - - LemmaIdType append_a_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count, uint64 lmt); - - // Check if a lemma is in dictionary - int32 locate_in_offsets(char16 lemma_str[], - uint16 splid_str[], uint16 lemma_len); - - bool remove_lemma_by_offset_index(int offset_index); -#ifdef ___PREDICT_ENABLED___ - uint32 locate_where_to_insert_in_predicts(const uint16 * words, - int lemma_len); - - int32 locate_first_in_predicts(const uint16 * words, int lemma_len); - - void remove_lemma_from_predict_list(uint32 offset); -#endif -#ifdef ___SYNC_ENABLED___ - void queue_lemma_for_sync(LemmaIdType id); - - void remove_lemma_from_sync_list(uint32 offset); - - void write_back_sync(int fd); -#endif - void write_back_score(int fd); - void write_back_offset(int fd); - void write_back_lemma(int fd); - void write_back_all(int fd); - void write_back(); - - struct UserDictScoreOffsetPair { - int score; - uint32 offset_index; - }; - - inline void swap(UserDictScoreOffsetPair * sop, int i, int j); - - void shift_down(UserDictScoreOffsetPair * sop, int i, int n); - - // On-disk format for each lemma - // +-------------+ - // | Version (4) | - // +-------------+ - // +-----------+-----------+--------------------+-------------------+ - // | Spare (1) | Nchar (1) | Splids (2 x Nchar) | Lemma (2 x Nchar) | - // +-----------+-----------+--------------------+-------------------+ - // ... - // +-----------------------+ +-------------+ <---Offset of offset - // | Offset1 by_splids (4) | ... | OffsetN (4) | - // +-----------------------+ +-------------+ -#ifdef ___PREDICT_ENABLED___ - // +----------------------+ +-------------+ - // | Offset1 by_lemma (4) | ... | OffsetN (4) | - // +----------------------+ +-------------+ -#endif - // +------------+ +------------+ - // | Score1 (4) | ... | ScoreN (4) | - // +------------+ +------------+ -#ifdef ___SYNC_ENABLED___ - // +-------------+ +-------------+ - // | NewAdd1 (4) | ... | NewAddN (4) | - // +-------------+ +-------------+ -#endif - // +----------------+ - // | Dict Info (4x) | - // +----------------+ -}; -} - -#endif diff --git a/PinyinIME/jni/include/utf16char.h b/PinyinIME/jni/include/utf16char.h deleted file mode 100644 index 7e957db..0000000 --- a/PinyinIME/jni/include/utf16char.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_UTF16CHAR_H__ -#define PINYINIME_INCLUDE_UTF16CHAR_H__ - -#include - -namespace ime_pinyin { - -#ifdef __cplusplus -extern "C" { -#endif - - typedef unsigned short char16; - - // Get a token from utf16_str, - // Returned pointer is a '\0'-terminated utf16 string, or NULL - // *utf16_str_next returns the next part of the string for further tokenizing - char16* utf16_strtok(char16 *utf16_str, size_t *token_size, - char16 **utf16_str_next); - - int utf16_atoi(const char16 *utf16_str); - - float utf16_atof(const char16 *utf16_str); - - size_t utf16_strlen(const char16 *utf16_str); - - int utf16_strcmp(const char16 *str1, const char16 *str2); - int utf16_strncmp(const char16 *str1, const char16 *str2, size_t size); - - char16* utf16_strcpy(char16 *dst, const char16 *src); - char16* utf16_strncpy(char16 *dst, const char16 *src, size_t size); - - - char* utf16_strcpy_tochar(char *dst, const char16 *src); - -#ifdef __cplusplus -} -#endif -} - -#endif // PINYINIME_INCLUDE_UTF16CHAR_H__ diff --git a/PinyinIME/jni/include/utf16reader.h b/PinyinIME/jni/include/utf16reader.h deleted file mode 100644 index b6d6719..0000000 --- a/PinyinIME/jni/include/utf16reader.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PINYINIME_INCLUDE_UTF16READER_H__ -#define PINYINIME_INCLUDE_UTF16READER_H__ - -#include -#include "./utf16char.h" - -namespace ime_pinyin { - -class Utf16Reader { - private: - FILE *fp_; - char16 *buffer_; - size_t buffer_total_len_; - size_t buffer_next_pos_; - - // Always less than buffer_total_len_ - buffer_next_pos_ - size_t buffer_valid_len_; - - public: - Utf16Reader(); - ~Utf16Reader(); - - // filename is the name of the file to open. - // buffer_len specifies how long buffer should be allocated to speed up the - // future reading - bool open(const char* filename, size_t buffer_len); - char16* readline(char16* read_buf, size_t max_len); - bool close(); -}; -} - -#endif // PINYINIME_INCLUDE_UTF16READER_H__ diff --git a/PinyinIME/jni/share/dictbuilder.cpp b/PinyinIME/jni/share/dictbuilder.cpp deleted file mode 100644 index b2e989c..0000000 --- a/PinyinIME/jni/share/dictbuilder.cpp +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "../include/dictbuilder.h" -#include "../include/dicttrie.h" -#include "../include/mystdlib.h" -#include "../include/ngram.h" -#include "../include/searchutility.h" -#include "../include/spellingtable.h" -#include "../include/spellingtrie.h" -#include "../include/splparser.h" -#include "../include/utf16reader.h" - -namespace ime_pinyin { - -#ifdef ___BUILD_MODEL___ - -static const size_t kReadBufLen = 512; -static const size_t kSplTableHashLen = 2000; - -// Compare a SingleCharItem, first by Hanzis, then by spelling ids, then by -// frequencies. -int cmp_scis_hz_splid_freq(const void* p1, const void* p2) { - const SingleCharItem *s1, *s2; - s1 = static_cast(p1); - s2 = static_cast(p2); - - if (s1->hz < s2->hz) - return -1; - if (s1->hz > s2->hz) - return 1; - - if (s1->splid.half_splid < s2->splid.half_splid) - return -1; - if (s1->splid.half_splid > s2->splid.half_splid) - return 1; - - if (s1->splid.full_splid < s2->splid.full_splid) - return -1; - if (s1->splid.full_splid > s2->splid.full_splid) - return 1; - - if (s1->freq > s2->freq) - return -1; - if (s1->freq < s2->freq) - return 1; - return 0; -} - -int cmp_scis_hz_splid(const void* p1, const void* p2) { - const SingleCharItem *s1, *s2; - s1 = static_cast(p1); - s2 = static_cast(p2); - - if (s1->hz < s2->hz) - return -1; - if (s1->hz > s2->hz) - return 1; - - if (s1->splid.half_splid < s2->splid.half_splid) - return -1; - if (s1->splid.half_splid > s2->splid.half_splid) - return 1; - - if (s1->splid.full_splid < s2->splid.full_splid) - return -1; - if (s1->splid.full_splid > s2->splid.full_splid) - return 1; - - return 0; -} - -int cmp_lemma_entry_hzs(const void* p1, const void* p2) { - size_t size1 = utf16_strlen(((const LemmaEntry*)p1)->hanzi_str); - size_t size2 = utf16_strlen(((const LemmaEntry*)p2)->hanzi_str); - if (size1 < size2) - return -1; - else if (size1 > size2) - return 1; - - return utf16_strcmp(((const LemmaEntry*)p1)->hanzi_str, - ((const LemmaEntry*)p2)->hanzi_str); -} - -int compare_char16(const void* p1, const void* p2) { - if (*((const char16*)p1) < *((const char16*)p2)) - return -1; - if (*((const char16*)p1) > *((const char16*)p2)) - return 1; - return 0; -} - -int compare_py(const void* p1, const void* p2) { - int ret = utf16_strcmp(((const LemmaEntry*)p1)->spl_idx_arr, - ((const LemmaEntry*)p2)->spl_idx_arr); - - if (0 != ret) - return ret; - - return static_cast(((const LemmaEntry*)p2)->freq) - - static_cast(((const LemmaEntry*)p1)->freq); -} - -// First hanzi, if the same, then Pinyin -int cmp_lemma_entry_hzspys(const void* p1, const void* p2) { - size_t size1 = utf16_strlen(((const LemmaEntry*)p1)->hanzi_str); - size_t size2 = utf16_strlen(((const LemmaEntry*)p2)->hanzi_str); - if (size1 < size2) - return -1; - else if (size1 > size2) - return 1; - int ret = utf16_strcmp(((const LemmaEntry*)p1)->hanzi_str, - ((const LemmaEntry*)p2)->hanzi_str); - - if (0 != ret) - return ret; - - ret = utf16_strcmp(((const LemmaEntry*)p1)->spl_idx_arr, - ((const LemmaEntry*)p2)->spl_idx_arr); - return ret; -} - -int compare_splid2(const void* p1, const void* p2) { - int ret = utf16_strcmp(((const LemmaEntry*)p1)->spl_idx_arr, - ((const LemmaEntry*)p2)->spl_idx_arr); - return ret; -} - -DictBuilder::DictBuilder() { - lemma_arr_ = NULL; - lemma_num_ = 0; - - scis_ = NULL; - scis_num_ = 0; - - lma_nodes_le0_ = NULL; - lma_nodes_ge1_ = NULL; - - lma_nds_used_num_le0_ = 0; - lma_nds_used_num_ge1_ = 0; - - homo_idx_buf_ = NULL; - homo_idx_num_eq1_ = 0; - homo_idx_num_gt1_ = 0; - - top_lmas_ = NULL; - top_lmas_num_ = 0; - - spl_table_ = NULL; - spl_parser_ = NULL; -} - -DictBuilder::~DictBuilder() { - free_resource(); -} - -bool DictBuilder::alloc_resource(size_t lma_num) { - if (0 == lma_num) - return false; - - free_resource(); - - lemma_num_ = lma_num; - lemma_arr_ = new LemmaEntry[lemma_num_]; - - top_lmas_num_ = 0; - top_lmas_ = new LemmaEntry[kTopScoreLemmaNum]; - - // New the scis_ buffer to the possible maximum size. - scis_num_ = lemma_num_ * kMaxLemmaSize; - scis_ = new SingleCharItem[scis_num_]; - - // The root and first level nodes is less than kMaxSpellingNum + 1 - lma_nds_used_num_le0_ = 0; - lma_nodes_le0_ = new LmaNodeLE0[kMaxSpellingNum + 1]; - - // Other nodes is less than lemma_num - lma_nds_used_num_ge1_ = 0; - lma_nodes_ge1_ = new LmaNodeGE1[lemma_num_]; - - homo_idx_buf_ = new LemmaIdType[lemma_num_]; - spl_table_ = new SpellingTable(); - spl_parser_ = new SpellingParser(); - - if (NULL == lemma_arr_ || NULL == top_lmas_ || - NULL == scis_ || NULL == spl_table_ || - NULL == spl_parser_ || NULL == lma_nodes_le0_ || - NULL == lma_nodes_ge1_ || NULL == homo_idx_buf_) { - free_resource(); - return false; - } - - memset(lemma_arr_, 0, sizeof(LemmaEntry) * lemma_num_); - memset(scis_, 0, sizeof(SingleCharItem) * scis_num_); - memset(lma_nodes_le0_, 0, sizeof(LmaNodeLE0) * (kMaxSpellingNum + 1)); - memset(lma_nodes_ge1_, 0, sizeof(LmaNodeGE1) * lemma_num_); - memset(homo_idx_buf_, 0, sizeof(LemmaIdType) * lemma_num_); - spl_table_->init_table(kMaxPinyinSize, kSplTableHashLen, true); - - return true; -} - -char16* DictBuilder::read_valid_hanzis(const char *fn_validhzs, size_t *num) { - if (NULL == fn_validhzs || NULL == num) - return NULL; - - *num = 0; - FILE *fp = fopen(fn_validhzs, "rb"); - if (NULL == fp) - return NULL; - - char16 utf16header; - if (fread(&utf16header, sizeof(char16), 1, fp) != 1 || - 0xfeff != utf16header) { - fclose(fp); - return NULL; - } - - fseek(fp, 0, SEEK_END); - *num = ftell(fp) / sizeof(char16); - assert(*num >= 1); - *num -= 1; - - char16 *hzs = new char16[*num]; - if (NULL == hzs) { - fclose(fp); - return NULL; - } - - fseek(fp, 2, SEEK_SET); - - if (fread(hzs, sizeof(char16), *num, fp) != *num) { - fclose(fp); - delete [] hzs; - return NULL; - } - fclose(fp); - - myqsort(hzs, *num, sizeof(char16), compare_char16); - return hzs; -} - -bool DictBuilder::hz_in_hanzis_list(const char16 *hzs, size_t hzs_len, - char16 hz) { - if (NULL == hzs) - return false; - - char16 *found; - found = static_cast( - mybsearch(&hz, hzs, hzs_len, sizeof(char16), compare_char16)); - if (NULL == found) - return false; - - assert(*found == hz); - return true; -} - -// The caller makes sure that the parameters are valid. -bool DictBuilder::str_in_hanzis_list(const char16 *hzs, size_t hzs_len, - const char16 *str, size_t str_len) { - if (NULL == hzs || NULL == str) - return false; - - for (size_t pos = 0; pos < str_len; pos++) { - if (!hz_in_hanzis_list(hzs, hzs_len, str[pos])) - return false; - } - return true; -} - -void DictBuilder::get_top_lemmas() { - top_lmas_num_ = 0; - if (NULL == lemma_arr_) - return; - - for (size_t pos = 0; pos < lemma_num_; pos++) { - if (0 == top_lmas_num_) { - top_lmas_[0] = lemma_arr_[pos]; - top_lmas_num_ = 1; - continue; - } - - if (lemma_arr_[pos].freq > top_lmas_[top_lmas_num_ - 1].freq) { - if (kTopScoreLemmaNum > top_lmas_num_) - top_lmas_num_ += 1; - - size_t move_pos; - for (move_pos = top_lmas_num_ - 1; move_pos > 0; move_pos--) { - top_lmas_[move_pos] = top_lmas_[move_pos - 1]; - if (0 == move_pos - 1 || - (move_pos - 1 > 0 && - top_lmas_[move_pos - 2].freq > lemma_arr_[pos].freq)) { - break; - } - } - assert(move_pos > 0); - top_lmas_[move_pos - 1] = lemma_arr_[pos]; - } else if (kTopScoreLemmaNum > top_lmas_num_) { - top_lmas_[top_lmas_num_] = lemma_arr_[pos]; - top_lmas_num_ += 1; - } - } - - if (kPrintDebug0) { - printf("\n------Top Lemmas------------------\n"); - for (size_t pos = 0; pos < top_lmas_num_; pos++) { - printf("--%d, idx:%06d, score:%.5f\n", pos, top_lmas_[pos].idx_by_hz, - top_lmas_[pos].freq); - } - } -} - -void DictBuilder::free_resource() { - if (NULL != lemma_arr_) - delete [] lemma_arr_; - - if (NULL != scis_) - delete [] scis_; - - if (NULL != lma_nodes_le0_) - delete [] lma_nodes_le0_; - - if (NULL != lma_nodes_ge1_) - delete [] lma_nodes_ge1_; - - if (NULL != homo_idx_buf_) - delete [] homo_idx_buf_; - - if (NULL != spl_table_) - delete spl_table_; - - if (NULL != spl_parser_) - delete spl_parser_; - - lemma_arr_ = NULL; - scis_ = NULL; - lma_nodes_le0_ = NULL; - lma_nodes_ge1_ = NULL; - homo_idx_buf_ = NULL; - spl_table_ = NULL; - spl_parser_ = NULL; - - lemma_num_ = 0; - lma_nds_used_num_le0_ = 0; - lma_nds_used_num_ge1_ = 0; - homo_idx_num_eq1_ = 0; - homo_idx_num_gt1_ = 0; -} - -size_t DictBuilder::read_raw_dict(const char* fn_raw, - const char *fn_validhzs, - size_t max_item) { - if (NULL == fn_raw) return 0; - - Utf16Reader utf16_reader; - if (!utf16_reader.open(fn_raw, kReadBufLen * 10)) - return false; - - char16 read_buf[kReadBufLen]; - - // Read the number of lemmas in the file - size_t lemma_num = 240000; - - // allocate resource required - if (!alloc_resource(lemma_num)) { - utf16_reader.close(); - } - - // Read the valid Hanzi list. - char16 *valid_hzs = NULL; - size_t valid_hzs_num = 0; - valid_hzs = read_valid_hanzis(fn_validhzs, &valid_hzs_num); - - // Begin reading the lemma entries - for (size_t i = 0; i < max_item; i++) { - // read next entry - if (!utf16_reader.readline(read_buf, kReadBufLen)) { - lemma_num = i; - break; - } - - size_t token_size; - char16 *token; - char16 *to_tokenize = read_buf; - - // Get the Hanzi string - token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); - if (NULL == token) { - free_resource(); - utf16_reader.close(); - return false; - } - - size_t lemma_size = utf16_strlen(token); - - if (lemma_size > kMaxLemmaSize) { - i--; - continue; - } - - if (lemma_size > 4) { - i--; - continue; - } - - // Copy to the lemma entry - utf16_strcpy(lemma_arr_[i].hanzi_str, token); - - lemma_arr_[i].hz_str_len = token_size; - - // Get the freq string - token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); - if (NULL == token) { - free_resource(); - utf16_reader.close(); - return false; - } - lemma_arr_[i].freq = utf16_atof(token); - - if (lemma_size > 1 && lemma_arr_[i].freq < 60) { - i--; - continue; - } - - // Get GBK mark, if no valid Hanzi list available, all items which contains - // GBK characters will be discarded. Otherwise, all items which contains - // characters outside of the valid Hanzi list will be discarded. - token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); - assert(NULL != token); - int gbk_flag = utf16_atoi(token); - if (NULL == valid_hzs || 0 == valid_hzs_num) { - if (0 != gbk_flag) { - i--; - continue; - } - } else { - if (!str_in_hanzis_list(valid_hzs, valid_hzs_num, - lemma_arr_[i].hanzi_str, lemma_arr_[i].hz_str_len)) { - i--; - continue; - } - } - - // Get spelling String - bool spelling_not_support = false; - for (size_t hz_pos = 0; hz_pos < (size_t)lemma_arr_[i].hz_str_len; - hz_pos++) { - // Get a Pinyin - token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); - if (NULL == token) { - free_resource(); - utf16_reader.close(); - return false; - } - - assert(utf16_strlen(token) <= kMaxPinyinSize); - - utf16_strcpy_tochar(lemma_arr_[i].pinyin_str[hz_pos], token); - - format_spelling_str(lemma_arr_[i].pinyin_str[hz_pos]); - - // Put the pinyin to the spelling table - if (!spl_table_->put_spelling(lemma_arr_[i].pinyin_str[hz_pos], - lemma_arr_[i].freq)) { - spelling_not_support = true; - break; - } - } - - // The whole line must have been parsed fully, otherwise discard this one. - token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); - if (spelling_not_support || NULL != token) { - i--; - continue; - } - // printf("i: %d\n", i); - } - - delete [] valid_hzs; - utf16_reader.close(); - - printf("read succesfully, lemma num: %d\n", lemma_num); - - return lemma_num; -} - -bool DictBuilder::build_dict(const char *fn_raw, - const char *fn_validhzs, - DictTrie *dict_trie) { - if (NULL == fn_raw || NULL == dict_trie) - return false; - - lemma_num_ = read_raw_dict(fn_raw, fn_validhzs, 240000); - if (0 == lemma_num_) - return false; - - // Arrange the spelling table, and build a spelling tree - // The size of an spelling. '\0' is included. If the spelling table is - // initialized to calculate the spelling scores, the last char in the - // spelling string will be score, and it is also included in spl_item_size. - size_t spl_item_size; - size_t spl_num; - const char* spl_buf; - spl_buf = spl_table_->arrange(&spl_item_size, &spl_num); - if (NULL == spl_buf) { - free_resource(); - return false; - } - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - - if (!spl_trie.construct(spl_buf, spl_item_size, spl_num, - spl_table_->get_score_amplifier(), - spl_table_->get_average_score())) { - free_resource(); - return false; - } - - printf("spelling tree construct successfully.\n"); - - // Convert the spelling string to idxs - for (size_t i = 0; i < lemma_num_; i++) { - for (size_t hz_pos = 0; hz_pos < (size_t)lemma_arr_[i].hz_str_len; - hz_pos++) { - uint16 spl_idxs[2]; - uint16 spl_start_pos[3]; - bool is_pre = true; - int spl_idx_num = - spl_parser_->splstr_to_idxs(lemma_arr_[i].pinyin_str[hz_pos], - strlen(lemma_arr_[i].pinyin_str[hz_pos]), - spl_idxs, spl_start_pos, 2, is_pre); - assert(1 == spl_idx_num); - - if (spl_trie.is_half_id(spl_idxs[0])) { - uint16 num = spl_trie.half_to_full(spl_idxs[0], spl_idxs); - assert(0 != num); - } - lemma_arr_[i].spl_idx_arr[hz_pos] = spl_idxs[0]; - } - } - - // Sort the lemma items according to the hanzi, and give each unique item a - // id - sort_lemmas_by_hz(); - - scis_num_ = build_scis(); - - // Construct the dict list - dict_trie->dict_list_ = new DictList(); - bool dl_success = dict_trie->dict_list_->init_list(scis_, scis_num_, - lemma_arr_, lemma_num_); - assert(dl_success); - - // Construct the NGram information - NGram& ngram = NGram::get_instance(); - ngram.build_unigram(lemma_arr_, lemma_num_, - lemma_arr_[lemma_num_ - 1].idx_by_hz + 1); - - // sort the lemma items according to the spelling idx string - myqsort(lemma_arr_, lemma_num_, sizeof(LemmaEntry), compare_py); - - get_top_lemmas(); - - // printf("Now begin construct tree\n"); -#ifdef ___DO_STATISTICS___ - stat_init(); -#endif - - lma_nds_used_num_le0_ = 1; // The root node - bool dt_success = construct_subset(static_cast(lma_nodes_le0_), - lemma_arr_, 0, lemma_num_, 0); - if (!dt_success) { - free_resource(); - return false; - } - -#ifdef ___DO_STATISTICS___ - stat_print(); -#endif - - // Move the node data and homo data to the DictTrie - dict_trie->root_ = new LmaNodeLE0[lma_nds_used_num_le0_]; - dict_trie->nodes_ge1_ = new LmaNodeGE1[lma_nds_used_num_ge1_]; - size_t lma_idx_num = homo_idx_num_eq1_ + homo_idx_num_gt1_ + top_lmas_num_; - dict_trie->lma_idx_buf_ = new unsigned char[lma_idx_num * kLemmaIdSize]; - assert(NULL != dict_trie->root_); - assert(NULL != dict_trie->lma_idx_buf_); - dict_trie->lma_node_num_le0_ = lma_nds_used_num_le0_; - dict_trie->lma_node_num_ge1_ = lma_nds_used_num_ge1_; - dict_trie->lma_idx_buf_len_ = lma_idx_num * kLemmaIdSize; - dict_trie->top_lmas_num_ = top_lmas_num_; - - memcpy(dict_trie->root_, lma_nodes_le0_, - sizeof(LmaNodeLE0) * lma_nds_used_num_le0_); - memcpy(dict_trie->nodes_ge1_, lma_nodes_ge1_, - sizeof(LmaNodeGE1) * lma_nds_used_num_ge1_); - - for (size_t pos = 0; pos < homo_idx_num_eq1_ + homo_idx_num_gt1_; pos++) { - id_to_charbuf(dict_trie->lma_idx_buf_ + pos * kLemmaIdSize, - homo_idx_buf_[pos]); - } - - for (size_t pos = homo_idx_num_eq1_ + homo_idx_num_gt1_; - pos < lma_idx_num; pos++) { - LemmaIdType idx = - top_lmas_[pos - homo_idx_num_eq1_ - homo_idx_num_gt1_].idx_by_hz; - id_to_charbuf(dict_trie->lma_idx_buf_ + pos * kLemmaIdSize, idx); - } - - if (kPrintDebug0) { - printf("homo_idx_num_eq1_: %d\n", homo_idx_num_eq1_); - printf("homo_idx_num_gt1_: %d\n", homo_idx_num_gt1_); - printf("top_lmas_num_: %d\n", top_lmas_num_); - } - - free_resource(); - - if (kPrintDebug0) { - printf("Building dict succeds\n"); - } - return dt_success; -} - -void DictBuilder::id_to_charbuf(unsigned char *buf, LemmaIdType id) { - if (NULL == buf) return; - for (size_t pos = 0; pos < kLemmaIdSize; pos++) { - (buf)[pos] = (unsigned char)(id >> (pos * 8)); - } -} - -void DictBuilder::set_son_offset(LmaNodeGE1 *node, size_t offset) { - node->son_1st_off_l = static_cast(offset); - node->son_1st_off_h = static_cast(offset >> 16); -} - -void DictBuilder:: set_homo_id_buf_offset(LmaNodeGE1 *node, size_t offset) { - node->homo_idx_buf_off_l = static_cast(offset); - node->homo_idx_buf_off_h = static_cast(offset >> 16); - -} - -// All spelling strings will be converted to upper case, except that -// spellings started with "ZH"/"CH"/"SH" will be converted to -// "Zh"/"Ch"/"Sh" -void DictBuilder::format_spelling_str(char *spl_str) { - if (NULL == spl_str) - return; - - uint16 pos = 0; - while ('\0' != spl_str[pos]) { - if (spl_str[pos] >= 'a' && spl_str[pos] <= 'z') - spl_str[pos] = spl_str[pos] - 'a' + 'A'; - - if (1 == pos && 'H' == spl_str[pos]) { - if ('C' == spl_str[0] || 'S' == spl_str[0] || 'Z' == spl_str[0]) { - spl_str[pos] = 'h'; - } - } - pos++; - } -} - -LemmaIdType DictBuilder::sort_lemmas_by_hz() { - if (NULL == lemma_arr_ || 0 == lemma_num_) - return 0; - - myqsort(lemma_arr_, lemma_num_, sizeof(LemmaEntry), cmp_lemma_entry_hzs); - - lemma_arr_[0].idx_by_hz = 1; - LemmaIdType idx_max = 1; - for (size_t i = 1; i < lemma_num_; i++) { - if (utf16_strcmp(lemma_arr_[i].hanzi_str, lemma_arr_[i-1].hanzi_str)) { - idx_max++; - lemma_arr_[i].idx_by_hz = idx_max; - } else { - idx_max++; - lemma_arr_[i].idx_by_hz = idx_max; - } - } - return idx_max + 1; -} - -size_t DictBuilder::build_scis() { - if (NULL == scis_ || lemma_num_ * kMaxLemmaSize > scis_num_) - return 0; - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - - // This first one is blank, because id 0 is invalid. - scis_[0].freq = 0; - scis_[0].hz = 0; - scis_[0].splid.full_splid = 0; - scis_[0].splid.half_splid = 0; - scis_num_ = 1; - - // Copy the hanzis to the buffer - for (size_t pos = 0; pos < lemma_num_; pos++) { - size_t hz_num = lemma_arr_[pos].hz_str_len; - for (size_t hzpos = 0; hzpos < hz_num; hzpos++) { - scis_[scis_num_].hz = lemma_arr_[pos].hanzi_str[hzpos]; - scis_[scis_num_].splid.full_splid = lemma_arr_[pos].spl_idx_arr[hzpos]; - scis_[scis_num_].splid.half_splid = - spl_trie.full_to_half(scis_[scis_num_].splid.full_splid); - if (1 == hz_num) - scis_[scis_num_].freq = lemma_arr_[pos].freq; - else - scis_[scis_num_].freq = 0.000001; - scis_num_++; - } - } - - myqsort(scis_, scis_num_, sizeof(SingleCharItem), cmp_scis_hz_splid_freq); - - // Remove repeated items - size_t unique_scis_num = 1; - for (size_t pos = 1; pos < scis_num_; pos++) { - if (scis_[pos].hz == scis_[pos - 1].hz && - scis_[pos].splid.full_splid == scis_[pos - 1].splid.full_splid) - continue; - scis_[unique_scis_num] = scis_[pos]; - scis_[unique_scis_num].splid.half_splid = - spl_trie.full_to_half(scis_[pos].splid.full_splid); - unique_scis_num++; - } - - scis_num_ = unique_scis_num; - - // Update the lemma list. - for (size_t pos = 0; pos < lemma_num_; pos++) { - size_t hz_num = lemma_arr_[pos].hz_str_len; - for (size_t hzpos = 0; hzpos < hz_num; hzpos++) { - SingleCharItem key; - key.hz = lemma_arr_[pos].hanzi_str[hzpos]; - key.splid.full_splid = lemma_arr_[pos].spl_idx_arr[hzpos]; - key.splid.half_splid = spl_trie.full_to_half(key.splid.full_splid); - - SingleCharItem *found; - found = static_cast(mybsearch(&key, scis_, - unique_scis_num, - sizeof(SingleCharItem), - cmp_scis_hz_splid)); - - assert(found); - - lemma_arr_[pos].hanzi_scis_ids[hzpos] = - static_cast(found - scis_); - lemma_arr_[pos].spl_idx_arr[hzpos] = found->splid.full_splid; - } - } - - return scis_num_; -} - -bool DictBuilder::construct_subset(void* parent, LemmaEntry* lemma_arr, - size_t item_start, size_t item_end, - size_t level) { - if (level >= kMaxLemmaSize || item_end <= item_start) - return false; - - // printf("++ enter recursive\n"); - - // 1. Scan for how many sons - size_t parent_son_num = 0; - // LemmaNode *son_1st = NULL; - // parent.num_of_son = 0; - - LemmaEntry *lma_last_start = lemma_arr_ + item_start; - uint16 spl_idx_node = lma_last_start->spl_idx_arr[level]; - - // Scan for how many sons to be allocaed - for (size_t i = item_start + 1; i< item_end; i++) { - LemmaEntry *lma_current = lemma_arr + i; - uint16 spl_idx_current = lma_current->spl_idx_arr[level]; - if (spl_idx_current != spl_idx_node) { - parent_son_num++; - spl_idx_node = spl_idx_current; - } - } - parent_son_num++; - -#ifdef ___DO_STATISTICS___ - // Use to indicate whether all nodes of this layer have no son. - bool allson_noson = true; - - assert(level < kMaxLemmaSize); - if (parent_son_num > max_sonbuf_len_[level]) - max_sonbuf_len_[level] = parent_son_num; - - total_son_num_[level] += parent_son_num; - total_sonbuf_num_[level] += 1; - - if (parent_son_num == 1) - sonbufs_num1_++; - else - sonbufs_numgt1_++; - total_lma_node_num_ += parent_son_num; -#endif - - // 2. Update the parent's information - // Update the parent's son list; - LmaNodeLE0 *son_1st_le0 = NULL; // only one of le0 or ge1 is used - LmaNodeGE1 *son_1st_ge1 = NULL; // only one of le0 or ge1 is used. - if (0 == level) { // the parent is root - (static_cast(parent))->son_1st_off = - lma_nds_used_num_le0_; - son_1st_le0 = lma_nodes_le0_ + lma_nds_used_num_le0_; - lma_nds_used_num_le0_ += parent_son_num; - - assert(parent_son_num <= 65535); - (static_cast(parent))->num_of_son = - static_cast(parent_son_num); - } else if (1 == level) { // the parent is a son of root - (static_cast(parent))->son_1st_off = - lma_nds_used_num_ge1_; - son_1st_ge1 = lma_nodes_ge1_ + lma_nds_used_num_ge1_; - lma_nds_used_num_ge1_ += parent_son_num; - - assert(parent_son_num <= 65535); - (static_cast(parent))->num_of_son = - static_cast(parent_son_num); - } else { - set_son_offset((static_cast(parent)), - lma_nds_used_num_ge1_); - son_1st_ge1 = lma_nodes_ge1_ + lma_nds_used_num_ge1_; - lma_nds_used_num_ge1_ += parent_son_num; - - assert(parent_son_num <= 255); - (static_cast(parent))->num_of_son = - (unsigned char)parent_son_num; - } - - // 3. Now begin to construct the son one by one - size_t son_pos = 0; - - lma_last_start = lemma_arr_ + item_start; - spl_idx_node = lma_last_start->spl_idx_arr[level]; - - // printf("++ spl_idx_node: %d\n", spl_idx_node); - - size_t homo_num = 0; - if (lma_last_start->spl_idx_arr[level + 1] == 0) - homo_num = 1; - - size_t item_start_next = item_start; - - for (size_t i = item_start + 1; i < item_end; i++) { - LemmaEntry* lma_current = lemma_arr_ + i; - uint16 spl_idx_current = lma_current->spl_idx_arr[level]; - - if (spl_idx_current == spl_idx_node) { - if (lma_current->spl_idx_arr[level + 1] == 0) - homo_num++; - } else { - // Construct a node - LmaNodeLE0 *node_cur_le0 = NULL; // only one of them is valid - LmaNodeGE1 *node_cur_ge1 = NULL; - if (0 == level) { - node_cur_le0 = son_1st_le0 + son_pos; - node_cur_le0->spl_idx = spl_idx_node; - node_cur_le0->homo_idx_buf_off = homo_idx_num_eq1_ + homo_idx_num_gt1_; - node_cur_le0->son_1st_off = 0; - homo_idx_num_eq1_ += homo_num; - } else { - node_cur_ge1 = son_1st_ge1 + son_pos; - node_cur_ge1->spl_idx = spl_idx_node; - - set_homo_id_buf_offset(node_cur_ge1, - (homo_idx_num_eq1_ + homo_idx_num_gt1_)); - set_son_offset(node_cur_ge1, 0); - homo_idx_num_gt1_ += homo_num; - } - - // printf("++ homo_num: %d\n++ lmaids: ", homo_num); - - if (homo_num > 0) { - LemmaIdType* idx_buf = homo_idx_buf_ + homo_idx_num_eq1_ + - homo_idx_num_gt1_ - homo_num; - if (0 == level) { - assert(homo_num <= 65535); - node_cur_le0->num_of_homo = static_cast(homo_num); - } else { - assert(homo_num <= 255); - node_cur_ge1->num_of_homo = (unsigned char)homo_num; - } - - for (size_t homo_pos = 0; homo_pos < homo_num; homo_pos++) { - idx_buf[homo_pos] = lemma_arr_[item_start_next + homo_pos].idx_by_hz; - // printf("[%d] %d ", item_start_next + homo_pos, idx_buf[homo_pos]); - } - - // printf("\n"); -#ifdef ___DO_STATISTICS___ - if (homo_num > max_homobuf_len_[level]) - max_homobuf_len_[level] = homo_num; - - total_homo_num_[level] += homo_num; -#endif - } - - if (i - item_start_next > homo_num) { - void *next_parent; - if (0 == level) - next_parent = static_cast(node_cur_le0); - else - next_parent = static_cast(node_cur_ge1); - construct_subset(next_parent, lemma_arr, - item_start_next + homo_num, i, level + 1); -#ifdef ___DO_STATISTICS___ - - total_node_hasson_[level] += 1; - allson_noson = false; -#endif - } - - // for the next son - lma_last_start = lma_current; - spl_idx_node = spl_idx_current; - item_start_next = i; - homo_num = 0; - if (lma_current->spl_idx_arr[level + 1] == 0) - homo_num = 1; - - son_pos++; - } - } - - // 4. The last one to construct - LmaNodeLE0 *node_cur_le0 = NULL; // only one of them is valid - LmaNodeGE1 *node_cur_ge1 = NULL; - if (0 == level) { - node_cur_le0 = son_1st_le0 + son_pos; - node_cur_le0->spl_idx = spl_idx_node; - node_cur_le0->homo_idx_buf_off = homo_idx_num_eq1_ + homo_idx_num_gt1_; - node_cur_le0->son_1st_off = 0; - homo_idx_num_eq1_ += homo_num; - } else { - node_cur_ge1 = son_1st_ge1 + son_pos; - node_cur_ge1->spl_idx = spl_idx_node; - - set_homo_id_buf_offset(node_cur_ge1, - (homo_idx_num_eq1_ + homo_idx_num_gt1_)); - set_son_offset(node_cur_ge1, 0); - homo_idx_num_gt1_ += homo_num; - } - - if (homo_num > 0) { - LemmaIdType* idx_buf = homo_idx_buf_ + homo_idx_num_eq1_ + - homo_idx_num_gt1_ - homo_num; - if (0 == level) { - assert(homo_num <= 65535); - node_cur_le0->num_of_homo = static_cast(homo_num); - } else { - assert(homo_num <= 255); - node_cur_ge1->num_of_homo = (unsigned char)homo_num; - } - - for (size_t homo_pos = 0; homo_pos < homo_num; homo_pos++) { - idx_buf[homo_pos] = lemma_arr[item_start_next + homo_pos].idx_by_hz; - } - -#ifdef ___DO_STATISTICS___ - if (homo_num > max_homobuf_len_[level]) - max_homobuf_len_[level] = homo_num; - - total_homo_num_[level] += homo_num; -#endif - } - - if (item_end - item_start_next > homo_num) { - void *next_parent; - if (0 == level) - next_parent = static_cast(node_cur_le0); - else - next_parent = static_cast(node_cur_ge1); - construct_subset(next_parent, lemma_arr, - item_start_next + homo_num, item_end, level + 1); -#ifdef ___DO_STATISTICS___ - - total_node_hasson_[level] += 1; - allson_noson = false; -#endif - } - -#ifdef ___DO_STATISTICS___ - if (allson_noson) { - total_sonbuf_allnoson_[level] += 1; - total_node_in_sonbuf_allnoson_[level] += parent_son_num; - } -#endif - - assert(son_pos + 1 == parent_son_num); - return true; -} - -#ifdef ___DO_STATISTICS___ -void DictBuilder::stat_init() { - memset(max_sonbuf_len_, 0, sizeof(size_t) * kMaxLemmaSize); - memset(max_homobuf_len_, 0, sizeof(size_t) * kMaxLemmaSize); - memset(total_son_num_, 0, sizeof(size_t) * kMaxLemmaSize); - memset(total_node_hasson_, 0, sizeof(size_t) * kMaxLemmaSize); - memset(total_sonbuf_num_, 0, sizeof(size_t) * kMaxLemmaSize); - memset(total_sonbuf_allnoson_, 0, sizeof(size_t) * kMaxLemmaSize); - memset(total_node_in_sonbuf_allnoson_, 0, sizeof(size_t) * kMaxLemmaSize); - memset(total_homo_num_, 0, sizeof(size_t) * kMaxLemmaSize); - - sonbufs_num1_ = 0; - sonbufs_numgt1_ = 0; - total_lma_node_num_ = 0; -} - -void DictBuilder::stat_print() { - printf("\n------------STAT INFO-------------\n"); - printf("[root is layer -1]\n"); - printf(".. max_sonbuf_len per layer(from layer 0):\n "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", max_sonbuf_len_[i]); - printf("-, \n"); - - printf(".. max_homobuf_len per layer:\n -, "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", max_homobuf_len_[i]); - printf("\n"); - - printf(".. total_son_num per layer:\n "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", total_son_num_[i]); - printf("-, \n"); - - printf(".. total_node_hasson per layer:\n 1, "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", total_node_hasson_[i]); - printf("\n"); - - printf(".. total_sonbuf_num per layer:\n "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", total_sonbuf_num_[i]); - printf("-, \n"); - - printf(".. total_sonbuf_allnoson per layer:\n "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", total_sonbuf_allnoson_[i]); - printf("-, \n"); - - printf(".. total_node_in_sonbuf_allnoson per layer:\n "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", total_node_in_sonbuf_allnoson_[i]); - printf("-, \n"); - - printf(".. total_homo_num per layer:\n 0, "); - for (size_t i = 0; i < kMaxLemmaSize; i++) - printf("%d, ", total_homo_num_[i]); - printf("\n"); - - printf(".. son buf allocation number with only 1 son: %d\n", sonbufs_num1_); - printf(".. son buf allocation number with more than 1 son: %d\n", - sonbufs_numgt1_); - printf(".. total lemma node number: %d\n", total_lma_node_num_ + 1); -} -#endif // ___DO_STATISTICS___ - -#endif // ___BUILD_MODEL___ -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/dictlist.cpp b/PinyinIME/jni/share/dictlist.cpp deleted file mode 100644 index aa7905c..0000000 --- a/PinyinIME/jni/share/dictlist.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "../include/dictlist.h" -#include "../include/mystdlib.h" -#include "../include/ngram.h" -#include "../include/searchutility.h" - -namespace ime_pinyin { - -DictList::DictList() { - initialized_ = false; - scis_num_ = 0; - scis_hz_ = NULL; - scis_splid_ = NULL; - buf_ = NULL; - spl_trie_ = SpellingTrie::get_cpinstance(); - - assert(kMaxLemmaSize == 8); - cmp_func_[0] = cmp_hanzis_1; - cmp_func_[1] = cmp_hanzis_2; - cmp_func_[2] = cmp_hanzis_3; - cmp_func_[3] = cmp_hanzis_4; - cmp_func_[4] = cmp_hanzis_5; - cmp_func_[5] = cmp_hanzis_6; - cmp_func_[6] = cmp_hanzis_7; - cmp_func_[7] = cmp_hanzis_8; -} - -DictList::~DictList() { - free_resource(); -} - -bool DictList::alloc_resource(size_t buf_size, size_t scis_num) { - // Allocate memory - buf_ = static_cast(malloc(buf_size * sizeof(char16))); - if (NULL == buf_) - return false; - - scis_num_ = scis_num; - - scis_hz_ = static_cast(malloc(scis_num_ * sizeof(char16))); - if (NULL == scis_hz_) - return false; - - scis_splid_ = static_cast - (malloc(scis_num_ * sizeof(SpellingId))); - - if (NULL == scis_splid_) - return false; - - return true; -} - -void DictList::free_resource() { - if (NULL != buf_) - free(buf_); - buf_ = NULL; - - if (NULL != scis_hz_) - free(scis_hz_); - scis_hz_ = NULL; - - if (NULL != scis_splid_) - free(scis_splid_); - scis_splid_ = NULL; -} - -#ifdef ___BUILD_MODEL___ -bool DictList::init_list(const SingleCharItem *scis, size_t scis_num, - const LemmaEntry *lemma_arr, size_t lemma_num) { - if (NULL == scis || 0 == scis_num || NULL == lemma_arr || 0 == lemma_num) - return false; - - initialized_ = false; - - if (NULL != buf_) - free(buf_); - - // calculate the size - size_t buf_size = calculate_size(lemma_arr, lemma_num); - if (0 == buf_size) - return false; - - if (!alloc_resource(buf_size, scis_num)) - return false; - - fill_scis(scis, scis_num); - - // Copy the related content from the array to inner buffer - fill_list(lemma_arr, lemma_num); - - initialized_ = true; - return true; -} - -size_t DictList::calculate_size(const LemmaEntry* lemma_arr, size_t lemma_num) { - size_t last_hz_len = 0; - size_t list_size = 0; - size_t id_num = 0; - - for (size_t i = 0; i < lemma_num; i++) { - if (0 == i) { - last_hz_len = lemma_arr[i].hz_str_len; - - assert(last_hz_len > 0); - assert(lemma_arr[0].idx_by_hz == 1); - - id_num++; - start_pos_[0] = 0; - start_id_[0] = id_num; - - last_hz_len = 1; - list_size += last_hz_len; - } else { - size_t current_hz_len = lemma_arr[i].hz_str_len; - - assert(current_hz_len >= last_hz_len); - - if (current_hz_len == last_hz_len) { - list_size += current_hz_len; - id_num++; - } else { - for (size_t len = last_hz_len; len < current_hz_len - 1; len++) { - start_pos_[len] = start_pos_[len - 1]; - start_id_[len] = start_id_[len - 1]; - } - - start_pos_[current_hz_len - 1] = list_size; - - id_num++; - start_id_[current_hz_len - 1] = id_num; - - last_hz_len = current_hz_len; - list_size += current_hz_len; - } - } - } - - for (size_t i = last_hz_len; i <= kMaxLemmaSize; i++) { - if (0 == i) { - start_pos_[0] = 0; - start_id_[0] = 1; - } else { - start_pos_[i] = list_size; - start_id_[i] = id_num; - } - } - - return start_pos_[kMaxLemmaSize]; -} - -void DictList::fill_scis(const SingleCharItem *scis, size_t scis_num) { - assert(scis_num_ == scis_num); - - for (size_t pos = 0; pos < scis_num_; pos++) { - scis_hz_[pos] = scis[pos].hz; - scis_splid_[pos] = scis[pos].splid; - } -} - -void DictList::fill_list(const LemmaEntry* lemma_arr, size_t lemma_num) { - size_t current_pos = 0; - - utf16_strncpy(buf_, lemma_arr[0].hanzi_str, - lemma_arr[0].hz_str_len); - - current_pos = lemma_arr[0].hz_str_len; - - size_t id_num = 1; - - for (size_t i = 1; i < lemma_num; i++) { - utf16_strncpy(buf_ + current_pos, lemma_arr[i].hanzi_str, - lemma_arr[i].hz_str_len); - - id_num++; - current_pos += lemma_arr[i].hz_str_len; - } - - assert(current_pos == start_pos_[kMaxLemmaSize]); - assert(id_num == start_id_[kMaxLemmaSize]); -} - -char16* DictList::find_pos2_startedbyhz(char16 hz_char) { - char16 *found_2w = static_cast - (mybsearch(&hz_char, buf_ + start_pos_[1], - (start_pos_[2] - start_pos_[1]) / 2, - sizeof(char16) * 2, cmp_hanzis_1)); - if (NULL == found_2w) - return NULL; - - while (found_2w > buf_ + start_pos_[1] && *found_2w == *(found_2w - 1)) - found_2w -= 2; - - return found_2w; -} -#endif // ___BUILD_MODEL___ - -char16* DictList::find_pos_startedbyhzs(const char16 last_hzs[], - size_t word_len, int (*cmp_func)(const void *, const void *)) { - char16 *found_w = static_cast - (mybsearch(last_hzs, buf_ + start_pos_[word_len - 1], - (start_pos_[word_len] - start_pos_[word_len - 1]) - / word_len, - sizeof(char16) * word_len, cmp_func)); - - if (NULL == found_w) - return NULL; - - while (found_w > buf_ + start_pos_[word_len -1] && - cmp_func(found_w, found_w - word_len) == 0) - found_w -= word_len; - - return found_w; -} - -size_t DictList::predict(const char16 last_hzs[], uint16 hzs_len, - NPredictItem *npre_items, size_t npre_max, - size_t b4_used) { - assert(hzs_len <= kMaxPredictSize && hzs_len > 0); - - // 1. Prepare work - int (*cmp_func)(const void *, const void *) = cmp_func_[hzs_len - 1]; - - NGram& ngram = NGram::get_instance(); - - size_t item_num = 0; - - // 2. Do prediction - for (uint16 pre_len = 1; pre_len <= kMaxPredictSize + 1 - hzs_len; - pre_len++) { - uint16 word_len = hzs_len + pre_len; - char16 *w_buf = find_pos_startedbyhzs(last_hzs, word_len, cmp_func); - if (NULL == w_buf) - continue; - while (w_buf < buf_ + start_pos_[word_len] && - cmp_func(w_buf, last_hzs) == 0 && - item_num < npre_max) { - memset(npre_items + item_num, 0, sizeof(NPredictItem)); - utf16_strncpy(npre_items[item_num].pre_hzs, w_buf + hzs_len, pre_len); - npre_items[item_num].psb = - ngram.get_uni_psb((size_t)(w_buf - buf_ - start_pos_[word_len - 1]) - / word_len + start_id_[word_len - 1]); - npre_items[item_num].his_len = hzs_len; - item_num++; - w_buf += word_len; - } - } - - size_t new_num = 0; - for (size_t i = 0; i < item_num; i++) { - // Try to find it in the existing items - size_t e_pos; - for (e_pos = 1; e_pos <= b4_used; e_pos++) { - if (utf16_strncmp((*(npre_items - e_pos)).pre_hzs, npre_items[i].pre_hzs, - kMaxPredictSize) == 0) - break; - } - if (e_pos <= b4_used) - continue; - - // If not found, append it to the buffer - npre_items[new_num] = npre_items[i]; - new_num++; - } - - return new_num; -} - -uint16 DictList::get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, - uint16 str_max) { - if (!initialized_ || id_lemma >= start_id_[kMaxLemmaSize] || NULL == str_buf - || str_max <= 1) - return 0; - - // Find the range - for (uint16 i = 0; i < kMaxLemmaSize; i++) { - if (i + 1 > str_max - 1) - return 0; - if (start_id_[i] <= id_lemma && start_id_[i + 1] > id_lemma) { - size_t id_span = id_lemma - start_id_[i]; - - uint16 *buf = buf_ + start_pos_[i] + id_span * (i + 1); - for (uint16 len = 0; len <= i; len++) { - str_buf[len] = buf[len]; - } - str_buf[i+1] = (char16)'\0'; - return i + 1; - } - } - return 0; -} - -uint16 DictList::get_splids_for_hanzi(char16 hanzi, uint16 half_splid, - uint16 *splids, uint16 max_splids) { - char16 *hz_found = static_cast - (mybsearch(&hanzi, scis_hz_, scis_num_, sizeof(char16), cmp_hanzis_1)); - assert(NULL != hz_found && hanzi == *hz_found); - - // Move to the first one. - while (hz_found > scis_hz_ && hanzi == *(hz_found - 1)) - hz_found--; - - // First try to found if strict comparison result is not zero. - char16 *hz_f = hz_found; - bool strict = false; - while (hz_f < scis_hz_ + scis_num_ && hanzi == *hz_f) { - uint16 pos = hz_f - scis_hz_; - if (0 == half_splid || scis_splid_[pos].half_splid == half_splid) { - strict = true; - } - hz_f++; - } - - uint16 found_num = 0; - while (hz_found < scis_hz_ + scis_num_ && hanzi == *hz_found) { - uint16 pos = hz_found - scis_hz_; - if (0 == half_splid || - (strict && scis_splid_[pos].half_splid == half_splid) || - (!strict && spl_trie_->half_full_compatible(half_splid, - scis_splid_[pos].full_splid))) { - assert(found_num + 1 < max_splids); - splids[found_num] = scis_splid_[pos].full_splid; - found_num++; - } - hz_found++; - } - - return found_num; -} - -LemmaIdType DictList::get_lemma_id(const char16 *str, uint16 str_len) { - if (NULL == str || str_len > kMaxLemmaSize) - return 0; - - char16 *found = find_pos_startedbyhzs(str, str_len, cmp_func_[str_len - 1]); - if (NULL == found) - return 0; - - assert(found > buf_); - assert(static_cast(found - buf_) >= start_pos_[str_len - 1]); - return static_cast - (start_id_[str_len - 1] + - (found - buf_ - start_pos_[str_len - 1]) / str_len); -} - -void DictList::convert_to_hanzis(char16 *str, uint16 str_len) { - assert(NULL != str); - - for (uint16 str_pos = 0; str_pos < str_len; str_pos++) { - str[str_pos] = scis_hz_[str[str_pos]]; - } -} - -void DictList::convert_to_scis_ids(char16 *str, uint16 str_len) { - assert(NULL != str); - - for (uint16 str_pos = 0; str_pos < str_len; str_pos++) { - str[str_pos] = 0x100; - } -} - -bool DictList::save_list(FILE *fp) { - if (!initialized_ || NULL == fp) - return false; - - if (NULL == buf_ || 0 == start_pos_[kMaxLemmaSize] || - NULL == scis_hz_ || NULL == scis_splid_ || 0 == scis_num_) - return false; - - if (fwrite(&scis_num_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(start_pos_, sizeof(size_t), kMaxLemmaSize + 1, fp) != - kMaxLemmaSize + 1) - return false; - - if (fwrite(start_id_, sizeof(size_t), kMaxLemmaSize + 1, fp) != - kMaxLemmaSize + 1) - return false; - - if (fwrite(scis_hz_, sizeof(char16), scis_num_, fp) != scis_num_) - return false; - - if (fwrite(scis_splid_, sizeof(SpellingId), scis_num_, fp) != scis_num_) - return false; - - if (fwrite(buf_, sizeof(char16), start_pos_[kMaxLemmaSize], fp) != - start_pos_[kMaxLemmaSize]) - return false; - - return true; -} - -bool DictList::load_list(FILE *fp) { - if (NULL == fp) - return false; - - initialized_ = false; - - if (fread(&scis_num_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fread(start_pos_, sizeof(size_t), kMaxLemmaSize + 1, fp) != - kMaxLemmaSize + 1) - return false; - - if (fread(start_id_, sizeof(size_t), kMaxLemmaSize + 1, fp) != - kMaxLemmaSize + 1) - return false; - - free_resource(); - - if (!alloc_resource(start_pos_[kMaxLemmaSize], scis_num_)) - return false; - - if (fread(scis_hz_, sizeof(char16), scis_num_, fp) != scis_num_) - return false; - - if (fread(scis_splid_, sizeof(SpellingId), scis_num_, fp) != scis_num_) - return false; - - if (fread(buf_, sizeof(char16), start_pos_[kMaxLemmaSize], fp) != - start_pos_[kMaxLemmaSize]) - return false; - - initialized_ = true; - return true; -} -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/dicttrie.cpp b/PinyinIME/jni/share/dicttrie.cpp deleted file mode 100644 index 88b819d..0000000 --- a/PinyinIME/jni/share/dicttrie.cpp +++ /dev/null @@ -1,942 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "../include/dicttrie.h" -#include "../include/dictbuilder.h" -#include "../include/lpicache.h" -#include "../include/mystdlib.h" -#include "../include/ngram.h" - -namespace ime_pinyin { - -DictTrie::DictTrie() { - spl_trie_ = SpellingTrie::get_cpinstance(); - - root_ = NULL; - splid_le0_index_ = NULL; - lma_node_num_le0_ = 0; - nodes_ge1_ = NULL; - lma_node_num_ge1_ = 0; - lma_idx_buf_ = NULL; - lma_idx_buf_len_ = 0; - total_lma_num_ = 0; - top_lmas_num_ = 0; - dict_list_ = NULL; - - parsing_marks_ = NULL; - mile_stones_ = NULL; - reset_milestones(0, kFirstValidMileStoneHandle); -} - -DictTrie::~DictTrie() { - free_resource(true); -} - -void DictTrie::free_resource(bool free_dict_list) { - if (NULL != root_) - free(root_); - root_ = NULL; - - if (NULL != splid_le0_index_) - free(splid_le0_index_); - splid_le0_index_ = NULL; - - if (NULL != nodes_ge1_) - free(nodes_ge1_); - nodes_ge1_ = NULL; - - if (NULL != nodes_ge1_) - free(nodes_ge1_); - nodes_ge1_ = NULL; - - if (free_dict_list) { - if (NULL != dict_list_) { - delete dict_list_; - } - dict_list_ = NULL; - } - - if (parsing_marks_) - delete [] parsing_marks_; - parsing_marks_ = NULL; - - if (mile_stones_) - delete [] mile_stones_; - mile_stones_ = NULL; - - reset_milestones(0, kFirstValidMileStoneHandle); -} - -inline size_t DictTrie::get_son_offset(const LmaNodeGE1 *node) { - return ((size_t)node->son_1st_off_l + ((size_t)node->son_1st_off_h << 16)); -} - -inline size_t DictTrie::get_homo_idx_buf_offset(const LmaNodeGE1 *node) { - return ((size_t)node->homo_idx_buf_off_l + - ((size_t)node->homo_idx_buf_off_h << 16)); -} - -inline LemmaIdType DictTrie::get_lemma_id(size_t id_offset) { - LemmaIdType id = 0; - for (uint16 pos = kLemmaIdSize - 1; pos > 0; pos--) - id = (id << 8) + lma_idx_buf_[id_offset * kLemmaIdSize + pos]; - id = (id << 8) + lma_idx_buf_[id_offset * kLemmaIdSize]; - return id; -} - -#ifdef ___BUILD_MODEL___ -bool DictTrie::build_dict(const char* fn_raw, const char* fn_validhzs) { - DictBuilder* dict_builder = new DictBuilder(); - - free_resource(true); - - return dict_builder->build_dict(fn_raw, fn_validhzs, this); -} - -bool DictTrie::save_dict(FILE *fp) { - if (NULL == fp) - return false; - - if (fwrite(&lma_node_num_le0_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(&lma_node_num_ge1_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(&lma_idx_buf_len_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(&top_lmas_num_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(root_, sizeof(LmaNodeLE0), lma_node_num_le0_, fp) - != lma_node_num_le0_) - return false; - - if (fwrite(nodes_ge1_, sizeof(LmaNodeGE1), lma_node_num_ge1_, fp) - != lma_node_num_ge1_) - return false; - - if (fwrite(lma_idx_buf_, sizeof(unsigned char), lma_idx_buf_len_, fp) != - lma_idx_buf_len_) - return false; - - return true; -} - -bool DictTrie::save_dict(const char *filename) { - if (NULL == filename) - return false; - - if (NULL == root_ || NULL == dict_list_) - return false; - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - NGram &ngram = NGram::get_instance(); - - FILE *fp = fopen(filename, "wb"); - if (NULL == fp) - return false; - - if (!spl_trie.save_spl_trie(fp) || !dict_list_->save_list(fp) || - !save_dict(fp) || !ngram.save_ngram(fp)) { - fclose(fp); - return false; - } - - fclose(fp); - return true; -} -#endif // ___BUILD_MODEL___ - -bool DictTrie::load_dict(FILE *fp) { - if (NULL == fp) - return false; - - if (fread(&lma_node_num_le0_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fread(&lma_node_num_ge1_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fread(&lma_idx_buf_len_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fread(&top_lmas_num_, sizeof(size_t), 1, fp) != 1 || - top_lmas_num_ >= lma_idx_buf_len_) - return false; - - free_resource(false); - - root_ = static_cast - (malloc(lma_node_num_le0_ * sizeof(LmaNodeLE0))); - nodes_ge1_ = static_cast - (malloc(lma_node_num_ge1_ * sizeof(LmaNodeGE1))); - lma_idx_buf_ = (unsigned char*)malloc(lma_idx_buf_len_); - total_lma_num_ = lma_idx_buf_len_ / kLemmaIdSize; - - size_t buf_size = SpellingTrie::get_instance().get_spelling_num() + 1; - assert(lma_node_num_le0_ <= buf_size); - splid_le0_index_ = static_cast(malloc(buf_size * sizeof(uint16))); - - // Init the space for parsing. - parsing_marks_ = new ParsingMark[kMaxParsingMark]; - mile_stones_ = new MileStone[kMaxMileStone]; - reset_milestones(0, kFirstValidMileStoneHandle); - - if (NULL == root_ || NULL == nodes_ge1_ || NULL == lma_idx_buf_ || - NULL == splid_le0_index_ || NULL == parsing_marks_ || - NULL == mile_stones_) { - free_resource(false); - return false; - } - - if (fread(root_, sizeof(LmaNodeLE0), lma_node_num_le0_, fp) - != lma_node_num_le0_) - return false; - - if (fread(nodes_ge1_, sizeof(LmaNodeGE1), lma_node_num_ge1_, fp) - != lma_node_num_ge1_) - return false; - - if (fread(lma_idx_buf_, sizeof(unsigned char), lma_idx_buf_len_, fp) != - lma_idx_buf_len_) - return false; - - // The quick index for the first level sons - uint16 last_splid = kFullSplIdStart; - size_t last_pos = 0; - for (size_t i = 1; i < lma_node_num_le0_; i++) { - for (uint16 splid = last_splid; splid < root_[i].spl_idx; splid++) - splid_le0_index_[splid - kFullSplIdStart] = last_pos; - - splid_le0_index_[root_[i].spl_idx - kFullSplIdStart] = - static_cast(i); - last_splid = root_[i].spl_idx; - last_pos = i; - } - - for (uint16 splid = last_splid + 1; - splid < buf_size + kFullSplIdStart; splid++) { - assert(static_cast(splid - kFullSplIdStart) < buf_size); - splid_le0_index_[splid - kFullSplIdStart] = last_pos + 1; - } - - return true; -} - -bool DictTrie::load_dict(const char *filename, LemmaIdType start_id, - LemmaIdType end_id) { - if (NULL == filename || end_id <= start_id) - return false; - - FILE *fp = fopen(filename, "rb"); - if (NULL == fp) - return false; - - free_resource(true); - - dict_list_ = new DictList(); - if (NULL == dict_list_) { - fclose(fp); - return false; - } - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - NGram &ngram = NGram::get_instance(); - - if (!spl_trie.load_spl_trie(fp) || !dict_list_->load_list(fp) || - !load_dict(fp) || !ngram.load_ngram(fp) || - total_lma_num_ > end_id - start_id + 1) { - free_resource(true); - fclose(fp); - return false; - } - - fclose(fp); - return true; -} - -bool DictTrie::load_dict_fd(int sys_fd, long start_offset, - long length, LemmaIdType start_id, - LemmaIdType end_id) { - if (start_offset < 0 || length <= 0 || end_id <= start_id) - return false; - - FILE *fp = fdopen(sys_fd, "rb"); - if (NULL == fp) - return false; - - if (-1 == fseek(fp, start_offset, SEEK_SET)) { - fclose(fp); - return false; - } - - free_resource(true); - - dict_list_ = new DictList(); - if (NULL == dict_list_) { - fclose(fp); - return false; - } - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - NGram &ngram = NGram::get_instance(); - - if (!spl_trie.load_spl_trie(fp) || !dict_list_->load_list(fp) || - !load_dict(fp) || !ngram.load_ngram(fp) || - ftell(fp) < start_offset + length || - total_lma_num_ > end_id - start_id + 1) { - free_resource(true); - fclose(fp); - return false; - } - - fclose(fp); - return true; -} - -size_t DictTrie::fill_lpi_buffer(LmaPsbItem lpi_items[], size_t lpi_max, - LmaNodeLE0 *node) { - size_t lpi_num = 0; - NGram& ngram = NGram::get_instance(); - for (size_t homo = 0; homo < (size_t)node->num_of_homo; homo++) { - lpi_items[lpi_num].id = get_lemma_id(node->homo_idx_buf_off + - homo); - lpi_items[lpi_num].lma_len = 1; - lpi_items[lpi_num].psb = - static_cast(ngram.get_uni_psb(lpi_items[lpi_num].id)); - lpi_num++; - if (lpi_num >= lpi_max) - break; - } - - return lpi_num; -} - -size_t DictTrie::fill_lpi_buffer(LmaPsbItem lpi_items[], size_t lpi_max, - size_t homo_buf_off, LmaNodeGE1 *node, - uint16 lma_len) { - size_t lpi_num = 0; - NGram& ngram = NGram::get_instance(); - for (size_t homo = 0; homo < (size_t)node->num_of_homo; homo++) { - lpi_items[lpi_num].id = get_lemma_id(homo_buf_off + homo); - lpi_items[lpi_num].lma_len = lma_len; - lpi_items[lpi_num].psb = - static_cast(ngram.get_uni_psb(lpi_items[lpi_num].id)); - lpi_num++; - if (lpi_num >= lpi_max) - break; - } - - return lpi_num; -} - -void DictTrie::reset_milestones(uint16 from_step, MileStoneHandle from_handle) { - if (0 == from_step) { - parsing_marks_pos_ = 0; - mile_stones_pos_ = kFirstValidMileStoneHandle; - } else { - if (from_handle > 0 && from_handle < mile_stones_pos_) { - mile_stones_pos_ = from_handle; - - MileStone *mile_stone = mile_stones_ + from_handle; - parsing_marks_pos_ = mile_stone->mark_start; - } - } -} - -MileStoneHandle DictTrie::extend_dict(MileStoneHandle from_handle, - const DictExtPara *dep, - LmaPsbItem *lpi_items, size_t lpi_max, - size_t *lpi_num) { - if (NULL == dep) - return 0; - - // from LmaNodeLE0 (root) to LmaNodeLE0 - if (0 == from_handle) { - assert(0 == dep->splids_extended); - return extend_dict0(from_handle, dep, lpi_items, lpi_max, lpi_num); - } - - // from LmaNodeLE0 to LmaNodeGE1 - if (1 == dep->splids_extended) - return extend_dict1(from_handle, dep, lpi_items, lpi_max, lpi_num); - - // From LmaNodeGE1 to LmaNodeGE1 - return extend_dict2(from_handle, dep, lpi_items, lpi_max, lpi_num); -} - -MileStoneHandle DictTrie::extend_dict0(MileStoneHandle from_handle, - const DictExtPara *dep, - LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num) { - assert(NULL != dep && 0 == from_handle); - *lpi_num = 0; - MileStoneHandle ret_handle = 0; - - uint16 splid = dep->splids[dep->splids_extended]; - uint16 id_start = dep->id_start; - uint16 id_num = dep->id_num; - - LpiCache& lpi_cache = LpiCache::get_instance(); - bool cached = lpi_cache.is_cached(splid); - - // 2. Begin exgtending - // 2.1 Get the LmaPsbItem list - LmaNodeLE0 *node = root_; - size_t son_start = splid_le0_index_[id_start - kFullSplIdStart]; - size_t son_end = splid_le0_index_[id_start + id_num - kFullSplIdStart]; - for (size_t son_pos = son_start; son_pos < son_end; son_pos++) { - assert(1 == node->son_1st_off); - LmaNodeLE0 *son = root_ + son_pos; - assert(son->spl_idx >= id_start && son->spl_idx < id_start + id_num); - - if (!cached && *lpi_num < lpi_max) { - bool need_lpi = true; - if (spl_trie_->is_half_id_yunmu(splid) && son_pos != son_start) - need_lpi = false; - - if (need_lpi) - *lpi_num += fill_lpi_buffer(lpi_items + (*lpi_num), - lpi_max - *lpi_num, son); - } - - // If necessary, fill in a new mile stone. - if (son->spl_idx == id_start) { - if (mile_stones_pos_ < kMaxMileStone && - parsing_marks_pos_ < kMaxParsingMark) { - parsing_marks_[parsing_marks_pos_].node_offset = son_pos; - parsing_marks_[parsing_marks_pos_].node_num = id_num; - mile_stones_[mile_stones_pos_].mark_start = parsing_marks_pos_; - mile_stones_[mile_stones_pos_].mark_num = 1; - ret_handle = mile_stones_pos_; - parsing_marks_pos_++; - mile_stones_pos_++; - } - } - - if (son->spl_idx >= id_start + id_num -1) - break; - } - - // printf("----- parsing marks: %d, mile stone: %d \n", parsing_marks_pos_, - // mile_stones_pos_); - return ret_handle; -} - -MileStoneHandle DictTrie::extend_dict1(MileStoneHandle from_handle, - const DictExtPara *dep, - LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num) { - assert(NULL != dep && from_handle > 0 && from_handle < mile_stones_pos_); - - MileStoneHandle ret_handle = 0; - - // 1. If this is a half Id, get its corresponding full starting Id and - // number of full Id. - size_t ret_val = 0; - - uint16 id_start = dep->id_start; - uint16 id_num = dep->id_num; - - // 2. Begin extending. - MileStone *mile_stone = mile_stones_ + from_handle; - - for (uint16 h_pos = 0; h_pos < mile_stone->mark_num; h_pos++) { - ParsingMark p_mark = parsing_marks_[mile_stone->mark_start + h_pos]; - uint16 ext_num = p_mark.node_num; - for (uint16 ext_pos = 0; ext_pos < ext_num; ext_pos++) { - LmaNodeLE0 *node = root_ + p_mark.node_offset + ext_pos; - size_t found_start = 0; - size_t found_num = 0; - for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; son_pos++) { - assert(node->son_1st_off <= lma_node_num_ge1_); - LmaNodeGE1 *son = nodes_ge1_ + node->son_1st_off + son_pos; - if (son->spl_idx >= id_start - && son->spl_idx < id_start + id_num) { - if (*lpi_num < lpi_max) { - size_t homo_buf_off = get_homo_idx_buf_offset(son); - *lpi_num += fill_lpi_buffer(lpi_items + (*lpi_num), - lpi_max - *lpi_num, homo_buf_off, son, - 2); - } - - // If necessary, fill in the new DTMI - if (0 == found_num) { - found_start = son_pos; - } - found_num++; - } - if (son->spl_idx >= id_start + id_num - 1 || son_pos == - (size_t)node->num_of_son - 1) { - if (found_num > 0) { - if (mile_stones_pos_ < kMaxMileStone && - parsing_marks_pos_ < kMaxParsingMark) { - parsing_marks_[parsing_marks_pos_].node_offset = - node->son_1st_off + found_start; - parsing_marks_[parsing_marks_pos_].node_num = found_num; - if (0 == ret_val) - mile_stones_[mile_stones_pos_].mark_start = - parsing_marks_pos_; - parsing_marks_pos_++; - } - - ret_val++; - } - break; - } // for son_pos - } // for ext_pos - } // for h_pos - } - - if (ret_val > 0) { - mile_stones_[mile_stones_pos_].mark_num = ret_val; - ret_handle = mile_stones_pos_; - mile_stones_pos_++; - ret_val = 1; - } - - // printf("----- parsing marks: %d, mile stone: %d \n", parsing_marks_pos_, - // mile_stones_pos_); - return ret_handle; -} - -MileStoneHandle DictTrie::extend_dict2(MileStoneHandle from_handle, - const DictExtPara *dep, - LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num) { - assert(NULL != dep && from_handle > 0 && from_handle < mile_stones_pos_); - - MileStoneHandle ret_handle = 0; - - // 1. If this is a half Id, get its corresponding full starting Id and - // number of full Id. - size_t ret_val = 0; - - uint16 id_start = dep->id_start; - uint16 id_num = dep->id_num; - - // 2. Begin extending. - MileStone *mile_stone = mile_stones_ + from_handle; - - for (uint16 h_pos = 0; h_pos < mile_stone->mark_num; h_pos++) { - ParsingMark p_mark = parsing_marks_[mile_stone->mark_start + h_pos]; - uint16 ext_num = p_mark.node_num; - for (uint16 ext_pos = 0; ext_pos < ext_num; ext_pos++) { - LmaNodeGE1 *node = nodes_ge1_ + p_mark.node_offset + ext_pos; - size_t found_start = 0; - size_t found_num = 0; - - for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; son_pos++) { - assert(node->son_1st_off_l > 0 || node->son_1st_off_h > 0); - LmaNodeGE1 *son = nodes_ge1_ + get_son_offset(node) + son_pos; - if (son->spl_idx >= id_start - && son->spl_idx < id_start + id_num) { - if (*lpi_num < lpi_max) { - size_t homo_buf_off = get_homo_idx_buf_offset(son); - *lpi_num += fill_lpi_buffer(lpi_items + (*lpi_num), - lpi_max - *lpi_num, homo_buf_off, son, - dep->splids_extended + 1); - } - - // If necessary, fill in the new DTMI - if (0 == found_num) { - found_start = son_pos; - } - found_num++; - } - if (son->spl_idx >= id_start + id_num - 1 || son_pos == - (size_t)node->num_of_son - 1) { - if (found_num > 0) { - if (mile_stones_pos_ < kMaxMileStone && - parsing_marks_pos_ < kMaxParsingMark) { - parsing_marks_[parsing_marks_pos_].node_offset = - get_son_offset(node) + found_start; - parsing_marks_[parsing_marks_pos_].node_num = found_num; - if (0 == ret_val) - mile_stones_[mile_stones_pos_].mark_start = - parsing_marks_pos_; - parsing_marks_pos_++; - } - - ret_val++; - } - break; - } - } // for son_pos - } // for ext_pos - } // for h_pos - - if (ret_val > 0) { - mile_stones_[mile_stones_pos_].mark_num = ret_val; - ret_handle = mile_stones_pos_; - mile_stones_pos_++; - } - - // printf("----- parsing marks: %d, mile stone: %d \n", parsing_marks_pos_, - // mile_stones_pos_); - return ret_handle; -} - -bool DictTrie::try_extend(const uint16 *splids, uint16 splid_num, - LemmaIdType id_lemma) { - if (0 == splid_num || NULL == splids) - return false; - - void *node = root_ + splid_le0_index_[splids[0] - kFullSplIdStart]; - - for (uint16 pos = 1; pos < splid_num; pos++) { - if (1 == pos) { - LmaNodeLE0 *node_le0 = reinterpret_cast(node); - LmaNodeGE1 *node_son; - uint16 son_pos; - for (son_pos = 0; son_pos < static_cast(node_le0->num_of_son); - son_pos++) { - assert(node_le0->son_1st_off <= lma_node_num_ge1_); - node_son = nodes_ge1_ + node_le0->son_1st_off - + son_pos; - if (node_son->spl_idx == splids[pos]) - break; - } - if (son_pos < node_le0->num_of_son) - node = reinterpret_cast(node_son); - else - return false; - } else { - LmaNodeGE1 *node_ge1 = reinterpret_cast(node); - LmaNodeGE1 *node_son; - uint16 son_pos; - for (son_pos = 0; son_pos < static_cast(node_ge1->num_of_son); - son_pos++) { - assert(node_ge1->son_1st_off_l > 0 || node_ge1->son_1st_off_h > 0); - node_son = nodes_ge1_ + get_son_offset(node_ge1) + son_pos; - if (node_son->spl_idx == splids[pos]) - break; - } - if (son_pos < node_ge1->num_of_son) - node = reinterpret_cast(node_son); - else - return false; - } - } - - if (1 == splid_num) { - LmaNodeLE0* node_le0 = reinterpret_cast(node); - size_t num_of_homo = (size_t)node_le0->num_of_homo; - for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { - LemmaIdType id_this = get_lemma_id(node_le0->homo_idx_buf_off + homo_pos); - char16 str[2]; - get_lemma_str(id_this, str, 2); - if (id_this == id_lemma) - return true; - } - } else { - LmaNodeGE1* node_ge1 = reinterpret_cast(node); - size_t num_of_homo = (size_t)node_ge1->num_of_homo; - for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { - size_t node_homo_off = get_homo_idx_buf_offset(node_ge1); - if (get_lemma_id(node_homo_off + homo_pos) == id_lemma) - return true; - } - } - - return false; -} - -size_t DictTrie::get_lpis(const uint16* splid_str, uint16 splid_str_len, - LmaPsbItem* lma_buf, size_t max_lma_buf) { - if (splid_str_len > kMaxLemmaSize) - return 0; - -#define MAX_EXTENDBUF_LEN 200 - - size_t* node_buf1[MAX_EXTENDBUF_LEN]; // use size_t for data alignment - size_t* node_buf2[MAX_EXTENDBUF_LEN]; - LmaNodeLE0** node_fr_le0 = - reinterpret_cast(node_buf1); // Nodes from. - LmaNodeLE0** node_to_le0 = - reinterpret_cast(node_buf2); // Nodes to. - LmaNodeGE1** node_fr_ge1 = NULL; - LmaNodeGE1** node_to_ge1 = NULL; - size_t node_fr_num = 1; - size_t node_to_num = 0; - node_fr_le0[0] = root_; - if (NULL == node_fr_le0[0]) - return 0; - - size_t spl_pos = 0; - - while (spl_pos < splid_str_len) { - uint16 id_num = 1; - uint16 id_start = splid_str[spl_pos]; - // If it is a half id - if (spl_trie_->is_half_id(splid_str[spl_pos])) { - id_num = spl_trie_->half_to_full(splid_str[spl_pos], &id_start); - assert(id_num > 0); - } - - // Extend the nodes - if (0 == spl_pos) { // From LmaNodeLE0 (root) to LmaNodeLE0 nodes - for (size_t node_fr_pos = 0; node_fr_pos < node_fr_num; node_fr_pos++) { - LmaNodeLE0 *node = node_fr_le0[node_fr_pos]; - assert(node == root_ && 1 == node_fr_num); - size_t son_start = splid_le0_index_[id_start - kFullSplIdStart]; - size_t son_end = - splid_le0_index_[id_start + id_num - kFullSplIdStart]; - for (size_t son_pos = son_start; son_pos < son_end; son_pos++) { - assert(1 == node->son_1st_off); - LmaNodeLE0 *node_son = root_ + son_pos; - assert(node_son->spl_idx >= id_start - && node_son->spl_idx < id_start + id_num); - if (node_to_num < MAX_EXTENDBUF_LEN) { - node_to_le0[node_to_num] = node_son; - node_to_num++; - } - // id_start + id_num - 1 is the last one, which has just been - // recorded. - if (node_son->spl_idx >= id_start + id_num - 1) - break; - } - } - - spl_pos++; - if (spl_pos >= splid_str_len || node_to_num == 0) - break; - // Prepare the nodes for next extending - // next time, from LmaNodeLE0 to LmaNodeGE1 - LmaNodeLE0** node_tmp = node_fr_le0; - node_fr_le0 = node_to_le0; - node_to_le0 = NULL; - node_to_ge1 = reinterpret_cast(node_tmp); - } else if (1 == spl_pos) { // From LmaNodeLE0 to LmaNodeGE1 nodes - for (size_t node_fr_pos = 0; node_fr_pos < node_fr_num; node_fr_pos++) { - LmaNodeLE0 *node = node_fr_le0[node_fr_pos]; - for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; - son_pos++) { - assert(node->son_1st_off <= lma_node_num_ge1_); - LmaNodeGE1 *node_son = nodes_ge1_ + node->son_1st_off - + son_pos; - if (node_son->spl_idx >= id_start - && node_son->spl_idx < id_start + id_num) { - if (node_to_num < MAX_EXTENDBUF_LEN) { - node_to_ge1[node_to_num] = node_son; - node_to_num++; - } - } - // id_start + id_num - 1 is the last one, which has just been - // recorded. - if (node_son->spl_idx >= id_start + id_num - 1) - break; - } - } - - spl_pos++; - if (spl_pos >= splid_str_len || node_to_num == 0) - break; - // Prepare the nodes for next extending - // next time, from LmaNodeGE1 to LmaNodeGE1 - node_fr_ge1 = node_to_ge1; - node_to_ge1 = reinterpret_cast(node_fr_le0); - node_fr_le0 = NULL; - node_to_le0 = NULL; - } else { // From LmaNodeGE1 to LmaNodeGE1 nodes - for (size_t node_fr_pos = 0; node_fr_pos < node_fr_num; node_fr_pos++) { - LmaNodeGE1 *node = node_fr_ge1[node_fr_pos]; - for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; - son_pos++) { - assert(node->son_1st_off_l > 0 || node->son_1st_off_h > 0); - LmaNodeGE1 *node_son = nodes_ge1_ - + get_son_offset(node) + son_pos; - if (node_son->spl_idx >= id_start - && node_son->spl_idx < id_start + id_num) { - if (node_to_num < MAX_EXTENDBUF_LEN) { - node_to_ge1[node_to_num] = node_son; - node_to_num++; - } - } - // id_start + id_num - 1 is the last one, which has just been - // recorded. - if (node_son->spl_idx >= id_start + id_num - 1) - break; - } - } - - spl_pos++; - if (spl_pos >= splid_str_len || node_to_num == 0) - break; - // Prepare the nodes for next extending - // next time, from LmaNodeGE1 to LmaNodeGE1 - LmaNodeGE1 **node_tmp = node_fr_ge1; - node_fr_ge1 = node_to_ge1; - node_to_ge1 = node_tmp; - } - - // The number of node for next extending - node_fr_num = node_to_num; - node_to_num = 0; - } // while - - if (0 == node_to_num) - return 0; - - NGram &ngram = NGram::get_instance(); - size_t lma_num = 0; - - // If the length is 1, and the splid is a one-char Yunmu like 'a', 'o', 'e', - // only those candidates for the full matched one-char id will be returned. - if (1 == splid_str_len && spl_trie_->is_half_id_yunmu(splid_str[0])) - node_to_num = node_to_num > 0 ? 1 : 0; - - for (size_t node_pos = 0; node_pos < node_to_num; node_pos++) { - size_t num_of_homo = 0; - if (spl_pos <= 1) { // Get from LmaNodeLE0 nodes - LmaNodeLE0* node_le0 = node_to_le0[node_pos]; - num_of_homo = (size_t)node_le0->num_of_homo; - for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { - size_t ch_pos = lma_num + homo_pos; - lma_buf[ch_pos].id = - get_lemma_id(node_le0->homo_idx_buf_off + homo_pos); - lma_buf[ch_pos].lma_len = 1; - lma_buf[ch_pos].psb = - static_cast(ngram.get_uni_psb(lma_buf[ch_pos].id)); - - if (lma_num + homo_pos >= max_lma_buf - 1) - break; - } - } else { // Get from LmaNodeGE1 nodes - LmaNodeGE1* node_ge1 = node_to_ge1[node_pos]; - num_of_homo = (size_t)node_ge1->num_of_homo; - for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { - size_t ch_pos = lma_num + homo_pos; - size_t node_homo_off = get_homo_idx_buf_offset(node_ge1); - lma_buf[ch_pos].id = get_lemma_id(node_homo_off + homo_pos); - lma_buf[ch_pos].lma_len = splid_str_len; - lma_buf[ch_pos].psb = - static_cast(ngram.get_uni_psb(lma_buf[ch_pos].id)); - - if (lma_num + homo_pos >= max_lma_buf - 1) - break; - } - } - - lma_num += num_of_homo; - if (lma_num >= max_lma_buf) { - lma_num = max_lma_buf; - break; - } - } - return lma_num; -} - -uint16 DictTrie::get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, - uint16 str_max) { - return dict_list_->get_lemma_str(id_lemma, str_buf, str_max); -} - -uint16 DictTrie::get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, - uint16 splids_max, bool arg_valid) { - char16 lma_str[kMaxLemmaSize + 1]; - uint16 lma_len = get_lemma_str(id_lemma, lma_str, kMaxLemmaSize + 1); - assert((!arg_valid && splids_max >= lma_len) || lma_len == splids_max); - - uint16 spl_mtrx[kMaxLemmaSize * 5]; - uint16 spl_start[kMaxLemmaSize + 1]; - spl_start[0] = 0; - uint16 try_num = 1; - - for (uint16 pos = 0; pos < lma_len; pos++) { - uint16 cand_splids_this = 0; - if (arg_valid && spl_trie_->is_full_id(splids[pos])) { - spl_mtrx[spl_start[pos]] = splids[pos]; - cand_splids_this = 1; - } else { - cand_splids_this = dict_list_->get_splids_for_hanzi(lma_str[pos], - arg_valid ? splids[pos] : 0, spl_mtrx + spl_start[pos], - kMaxLemmaSize * 5 - spl_start[pos]); - assert(cand_splids_this > 0); - } - spl_start[pos + 1] = spl_start[pos] + cand_splids_this; - try_num *= cand_splids_this; - } - - for (uint16 try_pos = 0; try_pos < try_num; try_pos++) { - uint16 mod = 1; - for (uint16 pos = 0; pos < lma_len; pos++) { - uint16 radix = spl_start[pos + 1] - spl_start[pos]; - splids[pos] = spl_mtrx[ spl_start[pos] + try_pos / mod % radix]; - mod *= radix; - } - - if (try_extend(splids, lma_len, id_lemma)) - return lma_len; - } - - return 0; -} - -void DictTrie::set_total_lemma_count_of_others(size_t count) { - NGram& ngram = NGram::get_instance(); - ngram.set_total_freq_none_sys(count); -} - -void DictTrie::convert_to_hanzis(char16 *str, uint16 str_len) { - return dict_list_->convert_to_hanzis(str, str_len); -} - -void DictTrie::convert_to_scis_ids(char16 *str, uint16 str_len) { - return dict_list_->convert_to_scis_ids(str, str_len); -} - -LemmaIdType DictTrie::get_lemma_id(const char16 lemma_str[], uint16 lemma_len) { - if (NULL == lemma_str || lemma_len > kMaxLemmaSize) - return 0; - - return dict_list_->get_lemma_id(lemma_str, lemma_len); -} - -size_t DictTrie::predict_top_lmas(size_t his_len, NPredictItem *npre_items, - size_t npre_max, size_t b4_used) { - NGram &ngram = NGram::get_instance(); - - size_t item_num = 0; - size_t top_lmas_id_offset = lma_idx_buf_len_ / kLemmaIdSize - top_lmas_num_; - size_t top_lmas_pos = 0; - while (item_num < npre_max && top_lmas_pos < top_lmas_num_) { - memset(npre_items + item_num, 0, sizeof(NPredictItem)); - LemmaIdType top_lma_id = get_lemma_id(top_lmas_id_offset + top_lmas_pos); - top_lmas_pos += 1; - if (dict_list_->get_lemma_str(top_lma_id, - npre_items[item_num].pre_hzs, - kMaxLemmaSize - 1) == 0) { - continue; - } - npre_items[item_num].psb = ngram.get_uni_psb(top_lma_id); - npre_items[item_num].his_len = his_len; - item_num++; - } - return item_num; -} - -size_t DictTrie::predict(const char16 *last_hzs, uint16 hzs_len, - NPredictItem *npre_items, size_t npre_max, - size_t b4_used) { - return dict_list_->predict(last_hzs, hzs_len, npre_items, npre_max, b4_used); -} -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/lpicache.cpp b/PinyinIME/jni/share/lpicache.cpp deleted file mode 100644 index 4bb4ca2..0000000 --- a/PinyinIME/jni/share/lpicache.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "../include/lpicache.h" - -namespace ime_pinyin { - -LpiCache* LpiCache::instance_ = NULL; - -LpiCache::LpiCache() { - lpi_cache_ = new LmaPsbItem[kFullSplIdStart * kMaxLpiCachePerId]; - lpi_cache_len_ = new uint16[kFullSplIdStart]; - assert(NULL != lpi_cache_); - assert(NULL != lpi_cache_len_); - for (uint16 id = 0; id < kFullSplIdStart; id++) - lpi_cache_len_[id] = 0; -} - -LpiCache::~LpiCache() { - if (NULL != lpi_cache_) - delete [] lpi_cache_; - - if (NULL != lpi_cache_len_) - delete [] lpi_cache_len_; -} - -LpiCache& LpiCache::get_instance() { - if (NULL == instance_) { - instance_ = new LpiCache(); - assert(NULL != instance_); - } - return *instance_; -} - -bool LpiCache::is_cached(uint16 splid) { - if (splid >= kFullSplIdStart) - return false; - return lpi_cache_len_[splid] != 0; -} - -size_t LpiCache::put_cache(uint16 splid, LmaPsbItem lpi_items[], - size_t lpi_num) { - uint16 num = kMaxLpiCachePerId; - if (num > lpi_num) - num = static_cast(lpi_num); - - LmaPsbItem *lpi_cache_this = lpi_cache_ + splid * kMaxLpiCachePerId; - for (uint16 pos = 0; pos < num; pos++) - lpi_cache_this[pos] = lpi_items[pos]; - - lpi_cache_len_[splid] = num; - return num; -} - -size_t LpiCache::get_cache(uint16 splid, LmaPsbItem lpi_items[], - size_t lpi_max) { - if (lpi_max > lpi_cache_len_[splid]) - lpi_max = lpi_cache_len_[splid]; - - LmaPsbItem *lpi_cache_this = lpi_cache_ + splid * kMaxLpiCachePerId; - for (uint16 pos = 0; pos < lpi_max; pos++) { - lpi_items[pos] = lpi_cache_this[pos]; - } - return lpi_max; -} - -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/matrixsearch.cpp b/PinyinIME/jni/share/matrixsearch.cpp deleted file mode 100644 index d13a4c4..0000000 --- a/PinyinIME/jni/share/matrixsearch.cpp +++ /dev/null @@ -1,1895 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include "../include/lpicache.h" -#include "../include/matrixsearch.h" -#include "../include/mystdlib.h" -#include "../include/ngram.h" -#include "../include/userdict.h" - -namespace ime_pinyin { - -#define PRUMING_SCORE 8000.0 - -MatrixSearch::MatrixSearch() { - inited_ = false; - spl_trie_ = SpellingTrie::get_cpinstance(); - - reset_pointers_to_null(); - - pys_decoded_len_ = 0; - mtrx_nd_pool_used_ = 0; - dmi_pool_used_ = 0; - xi_an_enabled_ = false; - dmi_c_phrase_ = false; - - assert(kMaxSearchSteps > 0); - max_sps_len_ = kMaxSearchSteps - 1; - max_hzs_len_ = kMaxSearchSteps; -} - -MatrixSearch::~MatrixSearch() { - free_resource(); -} - -void MatrixSearch::reset_pointers_to_null() { - dict_trie_ = NULL; - user_dict_ = NULL; - spl_parser_ = NULL; - - share_buf_ = NULL; - - // The following four buffers are used for decoding, and they are based on - // share_buf_, no need to delete them. - mtrx_nd_pool_ = NULL; - dmi_pool_ = NULL; - matrix_ = NULL; - dep_ = NULL; - - // Based on share_buf_, no need to delete them. - npre_items_ = NULL; -} - -bool MatrixSearch::alloc_resource() { - free_resource(); - - dict_trie_ = new DictTrie(); - user_dict_ = static_cast(new UserDict()); - spl_parser_ = new SpellingParser(); - - size_t mtrx_nd_size = sizeof(MatrixNode) * kMtrxNdPoolSize; - mtrx_nd_size = align_to_size_t(mtrx_nd_size) / sizeof(size_t); - size_t dmi_size = sizeof(DictMatchInfo) * kDmiPoolSize; - dmi_size = align_to_size_t(dmi_size) / sizeof(size_t); - size_t matrix_size = sizeof(MatrixRow) * kMaxRowNum; - matrix_size = align_to_size_t(matrix_size) / sizeof(size_t); - size_t dep_size = sizeof(DictExtPara); - dep_size = align_to_size_t(dep_size) / sizeof(size_t); - - // share_buf's size is determined by the buffers for search. - share_buf_ = new size_t[mtrx_nd_size + dmi_size + matrix_size + dep_size]; - - if (NULL == dict_trie_ || NULL == user_dict_ || NULL == spl_parser_ || - NULL == share_buf_) - return false; - - // The buffers for search are based on the share buffer - mtrx_nd_pool_ = reinterpret_cast(share_buf_); - dmi_pool_ = reinterpret_cast(share_buf_ + mtrx_nd_size); - matrix_ = reinterpret_cast(share_buf_ + mtrx_nd_size + dmi_size); - dep_ = reinterpret_cast - (share_buf_ + mtrx_nd_size + dmi_size + matrix_size); - - // The prediction buffer is also based on the share buffer. - npre_items_ = reinterpret_cast(share_buf_); - npre_items_len_ = (mtrx_nd_size + dmi_size + matrix_size + dep_size) * - sizeof(size_t) / sizeof(NPredictItem); - return true; -} - -void MatrixSearch::free_resource() { - if (NULL != dict_trie_) - delete dict_trie_; - - if (NULL != user_dict_) - delete user_dict_; - - if (NULL != spl_parser_) - delete spl_parser_; - - if (NULL != share_buf_) - delete [] share_buf_; - - reset_pointers_to_null(); -} - -bool MatrixSearch::init(const char *fn_sys_dict, const char *fn_usr_dict) { - if (NULL == fn_sys_dict || NULL == fn_usr_dict) - return false; - - if (!alloc_resource()) - return false; - - if (!dict_trie_->load_dict(fn_sys_dict, 1, kSysDictIdEnd)) - return false; - - // If engine fails to load the user dictionary, reset the user dictionary - // to NULL. - if (!user_dict_->load_dict(fn_usr_dict, kUserDictIdStart, kUserDictIdEnd)) { - delete user_dict_; - user_dict_ = NULL; - } else{ - user_dict_->set_total_lemma_count_of_others(NGram::kSysDictTotalFreq); - } - - reset_search(0, true, true, true); - - inited_ = true; - return true; -} - -bool MatrixSearch::init_fd(int sys_fd, long start_offset, long length, - const char *fn_usr_dict) { - if (NULL == fn_usr_dict) - return false; - - if (!alloc_resource()) - return false; - - if (!dict_trie_->load_dict_fd(sys_fd, start_offset, length, 1, kSysDictIdEnd)) - return false; - - if (!user_dict_->load_dict(fn_usr_dict, kUserDictIdStart, kUserDictIdEnd)) { - delete user_dict_; - user_dict_ = NULL; - } else { - user_dict_->set_total_lemma_count_of_others(NGram::kSysDictTotalFreq); - } - - reset_search(0, true, true, true); - - inited_ = true; - return true; -} - -void MatrixSearch::set_max_lens(size_t max_sps_len, size_t max_hzs_len) { - if (0 != max_sps_len) - max_sps_len_ = max_sps_len; - if (0 != max_hzs_len) - max_hzs_len_ = max_hzs_len; -} - -void MatrixSearch::close() { - flush_cache(); - free_resource(); - inited_ = false; -} - -void MatrixSearch::flush_cache() { - if (NULL != user_dict_) - user_dict_->flush_cache(); -} - -void MatrixSearch::set_xi_an_switch(bool xi_an_enabled) { - xi_an_enabled_ = xi_an_enabled; -} - -bool MatrixSearch::get_xi_an_switch() { - return xi_an_enabled_; -} - -bool MatrixSearch::reset_search() { - if (!inited_) - return false; - return reset_search(0, true, true, true); -} - -bool MatrixSearch::reset_search(size_t ch_pos, bool clear_fixed_this_step, - bool clear_dmi_this_step, - bool clear_mtrx_this_step) { - if (!inited_ || ch_pos > pys_decoded_len_ || ch_pos >= kMaxRowNum) - return false; - - if (0 == ch_pos) { - pys_decoded_len_ = 0; - mtrx_nd_pool_used_ = 0; - dmi_pool_used_ = 0; - - // Get a MatrixNode from the pool - matrix_[0].mtrx_nd_pos = mtrx_nd_pool_used_; - matrix_[0].mtrx_nd_num = 1; - mtrx_nd_pool_used_ += 1; - - // Update the node, and make it to be a starting node - MatrixNode *node = mtrx_nd_pool_ + matrix_[0].mtrx_nd_pos; - node->id = 0; - node->score = 0; - node->from = NULL; - node->step = 0; - node->dmi_fr = (PoolPosType)-1; - - matrix_[0].dmi_pos = 0; - matrix_[0].dmi_num = 0; - matrix_[0].dmi_has_full_id = 1; - matrix_[0].mtrx_nd_fixed = node; - - lma_start_[0] = 0; - fixed_lmas_ = 0; - spl_start_[0] = 0; - fixed_hzs_ = 0; - - dict_trie_->reset_milestones(0, 0); - if (NULL != user_dict_) - user_dict_->reset_milestones(0, 0); - } else { - // Prepare mile stones of this step to clear. - MileStoneHandle *dict_handles_to_clear = NULL; - if (clear_dmi_this_step && matrix_[ch_pos].dmi_num > 0) { - dict_handles_to_clear = dmi_pool_[matrix_[ch_pos].dmi_pos].dict_handles; - } - - // If there are more steps, and this step is not allowed to clear, find - // milestones of next step. - if (pys_decoded_len_ > ch_pos && !clear_dmi_this_step) { - dict_handles_to_clear = NULL; - if (matrix_[ch_pos + 1].dmi_num > 0) { - dict_handles_to_clear = - dmi_pool_[matrix_[ch_pos + 1].dmi_pos].dict_handles; - } - } - - if (NULL != dict_handles_to_clear) { - dict_trie_->reset_milestones(ch_pos, dict_handles_to_clear[0]); - if (NULL != user_dict_) - user_dict_->reset_milestones(ch_pos, dict_handles_to_clear[1]); - } - - pys_decoded_len_ = ch_pos; - - if (clear_dmi_this_step) { - dmi_pool_used_ = matrix_[ch_pos - 1].dmi_pos - + matrix_[ch_pos - 1].dmi_num; - matrix_[ch_pos].dmi_num = 0; - } else { - dmi_pool_used_ = matrix_[ch_pos].dmi_pos + matrix_[ch_pos].dmi_num; - } - - if (clear_mtrx_this_step) { - mtrx_nd_pool_used_ = matrix_[ch_pos - 1].mtrx_nd_pos - + matrix_[ch_pos - 1].mtrx_nd_num; - matrix_[ch_pos].mtrx_nd_num = 0; - } else { - mtrx_nd_pool_used_ = matrix_[ch_pos].mtrx_nd_pos - + matrix_[ch_pos].mtrx_nd_num; - } - - // Modify fixed_hzs_ - if (fixed_hzs_ > 0 && kLemmaIdComposing != lma_id_[0]) { - size_t fixed_ch_pos = ch_pos; - if (clear_fixed_this_step) - fixed_ch_pos = fixed_ch_pos > 0 ? fixed_ch_pos - 1 : 0; - while (NULL == matrix_[fixed_ch_pos].mtrx_nd_fixed && fixed_ch_pos > 0) - fixed_ch_pos--; - - fixed_lmas_ = 0; - fixed_hzs_ = 0; - if (fixed_ch_pos > 0) { - while (spl_start_[fixed_hzs_] < fixed_ch_pos) - fixed_hzs_++; - assert(spl_start_[fixed_hzs_] == fixed_ch_pos); - - while (lma_start_[fixed_lmas_] < fixed_hzs_) - fixed_lmas_++; - assert(lma_start_[fixed_lmas_] == fixed_hzs_); - } - - // Re-search the Pinyin string for the unlocked lemma - // which was previously fixed. - // - // Prepare mile stones of this step to clear. - MileStoneHandle *dict_handles_to_clear = NULL; - if (clear_dmi_this_step && ch_pos == fixed_ch_pos && - matrix_[fixed_ch_pos].dmi_num > 0) { - dict_handles_to_clear = dmi_pool_[matrix_[fixed_ch_pos].dmi_pos].dict_handles; - } - - // If there are more steps, and this step is not allowed to clear, find - // milestones of next step. - if (pys_decoded_len_ > fixed_ch_pos && !clear_dmi_this_step) { - dict_handles_to_clear = NULL; - if (matrix_[fixed_ch_pos + 1].dmi_num > 0) { - dict_handles_to_clear = - dmi_pool_[matrix_[fixed_ch_pos + 1].dmi_pos].dict_handles; - } - } - - if (NULL != dict_handles_to_clear) { - dict_trie_->reset_milestones(fixed_ch_pos, dict_handles_to_clear[0]); - if (NULL != user_dict_) - user_dict_->reset_milestones(fixed_ch_pos, dict_handles_to_clear[1]); - } - - - pys_decoded_len_ = fixed_ch_pos; - - if (clear_dmi_this_step && ch_pos == fixed_ch_pos) { - dmi_pool_used_ = matrix_[fixed_ch_pos - 1].dmi_pos - + matrix_[fixed_ch_pos - 1].dmi_num; - matrix_[fixed_ch_pos].dmi_num = 0; - } else { - dmi_pool_used_ = matrix_[fixed_ch_pos].dmi_pos + - matrix_[fixed_ch_pos].dmi_num; - } - - if (clear_mtrx_this_step && ch_pos == fixed_ch_pos) { - mtrx_nd_pool_used_ = matrix_[fixed_ch_pos - 1].mtrx_nd_pos - + matrix_[fixed_ch_pos - 1].mtrx_nd_num; - matrix_[fixed_ch_pos].mtrx_nd_num = 0; - } else { - mtrx_nd_pool_used_ = matrix_[fixed_ch_pos].mtrx_nd_pos - + matrix_[fixed_ch_pos].mtrx_nd_num; - } - - for (uint16 re_pos = fixed_ch_pos; re_pos < ch_pos; re_pos++) { - add_char(pys_[re_pos]); - } - } else if (kLemmaIdComposing == lma_id_[0]) { - // Do nothing for the composing phrase. - } - } - - return true; -} - -void MatrixSearch::del_in_pys(size_t start, size_t len) { - while (start < kMaxRowNum - len && '\0' != pys_[start]) { - pys_[start] = pys_[start + len]; - start++; - } -} - -size_t MatrixSearch::search(const char *py, size_t py_len) { - if (!inited_ || NULL == py) - return 0; - - // If the search Pinyin string is too long, it will be truncated. - if (py_len > kMaxRowNum - 1) - py_len = kMaxRowNum - 1; - - // Compare the new string with the previous one. Find their prefix to - // increase search efficiency. - size_t ch_pos = 0; - for (ch_pos = 0; ch_pos < pys_decoded_len_; ch_pos++) { - if ('\0' == py[ch_pos] || py[ch_pos] != pys_[ch_pos]) - break; - } - - bool clear_fix = true; - if (ch_pos == pys_decoded_len_) - clear_fix = false; - - reset_search(ch_pos, clear_fix, false, false); - - memcpy(pys_ + ch_pos, py + ch_pos, py_len - ch_pos); - pys_[py_len] = '\0'; - - while ('\0' != pys_[ch_pos]) { - if (!add_char(py[ch_pos])) { - pys_decoded_len_ = ch_pos; - break; - } - ch_pos++; - } - - // Get spelling ids and starting positions. - get_spl_start_id(); - - // If there are too many spellings, remove the last letter until the spelling - // number is acceptable. - while (spl_id_num_ > 9) { - py_len--; - reset_search(py_len, false, false, false); - pys_[py_len] = '\0'; - get_spl_start_id(); - } - - prepare_candidates(); - - if (kPrintDebug0) { - printf("--Matrix Node Pool Used: %d\n", mtrx_nd_pool_used_); - printf("--DMI Pool Used: %d\n", dmi_pool_used_); - - if (kPrintDebug1) { - for (PoolPosType pos = 0; pos < dmi_pool_used_; pos++) { - debug_print_dmi(pos, 1); - } - } - } - - return ch_pos; -} - -size_t MatrixSearch::delsearch(size_t pos, bool is_pos_in_splid, - bool clear_fixed_this_step) { - if (!inited_) - return 0; - - // Out of range for both Pinyin mode and Spelling id mode. - if (pys_decoded_len_ <= pos) { - del_in_pys(pos, 1); - return pys_decoded_len_; - } - - // Spelling id mode, but out of range. - if (is_pos_in_splid && spl_id_num_ <= pos) - return pys_decoded_len_; - - // Begin to handle two modes respectively. - // Pinyin mode by default - size_t c_py_len = 0; // The length of composing phrase's Pinyin - size_t reset_pos = pos; - size_t del_py_len = 1; - size_t stop_pos = pys_decoded_len_; - if (!is_pos_in_splid) { - // Pinyin mode is only allowed to delete beyond the fixed lemmas. - if (fixed_lmas_ > 0 && pos < spl_start_[lma_start_[fixed_lmas_]]) - return pys_decoded_len_; - - del_in_pys(pos, 1); - - // If the deleted character is just the one after the last fixed lemma - if (pos == spl_start_[lma_start_[fixed_lmas_]]) { - // If all fixed lemmas have been merged, and the caller of the function - // request to unlock the last fixed lemma. - if (kLemmaIdComposing == lma_id_[0] && clear_fixed_this_step) { - // Unlock the last sub lemma in the composing phrase. Because it is not - // easy to unlock it directly. Instead, we re-decode the modified - // composing phrase. - c_phrase_.sublma_num--; - c_phrase_.length = c_phrase_.sublma_start[c_phrase_.sublma_num]; - reset_pos = spl_start_[c_phrase_.length]; - c_py_len = reset_pos; - } - } - } else { - del_py_len = spl_start_[pos + 1] - spl_start_[pos]; - - del_in_pys(spl_start_[pos], del_py_len); - - if (pos >= lma_start_[fixed_lmas_]) { - c_py_len = 0; - reset_pos = spl_start_[pos + 1] - del_py_len; - } else { - c_py_len = spl_start_[lma_start_[fixed_lmas_]] - del_py_len; - reset_pos = c_py_len; - if (c_py_len > 0) - merge_fixed_lmas(pos); - } - } - - if (c_py_len > 0) { - assert(c_phrase_.length > 0 && c_py_len == - c_phrase_.spl_start[c_phrase_.sublma_start[c_phrase_.sublma_num]]); - // The composing phrase is valid, reset all search space, - // and begin a new search which will only extend the composing - // phrase. - reset_search(0, true, true, true); - - dmi_c_phrase_ = true; - // Extend the composing phrase. - uint16 c_py_pos = 0; - while (c_py_pos < c_py_len) { - bool b_ac_tmp = add_char(pys_[c_py_pos]); - assert(b_ac_tmp); - c_py_pos++; - } - dmi_c_phrase_ = false; - - // Fixd the composing phrase as the first choice. - lma_id_num_ = 1; - fixed_lmas_ = 1; - fixed_lmas_no1_[0] = 0; // A composing string is always modified. - fixed_hzs_ = c_phrase_.length; - lma_start_[1] = fixed_hzs_; - lma_id_[0] = kLemmaIdComposing; - matrix_[spl_start_[fixed_hzs_]].mtrx_nd_fixed = mtrx_nd_pool_ + - matrix_[spl_start_[fixed_hzs_]].mtrx_nd_pos; - } else { - // Reseting search only clear pys_decoded_len_, but the string is kept. - reset_search(reset_pos, clear_fixed_this_step, false, false); - } - - // Decode the string after the delete position. - stop_pos -= del_py_len; - while (reset_pos < stop_pos) { - if (!add_char(pys_[reset_pos])) { - pys_decoded_len_ = reset_pos; - break; - } - reset_pos++; - } - - get_spl_start_id(); - prepare_candidates(); - return c_py_len; -} - -size_t MatrixSearch::get_candidate_num() { - if (!inited_ || 0 == pys_decoded_len_ || - 0 == matrix_[pys_decoded_len_].mtrx_nd_num) - return 0; - - return 1 + lpi_total_; -} - -char16* MatrixSearch::get_candidate(size_t cand_id, char16 *cand_str, - size_t max_len) { - if (!inited_ || 0 == pys_decoded_len_ || NULL == cand_str) - return NULL; - - if (0 == cand_id) { - return get_candidate0(cand_str, max_len, NULL, false); - } else { - cand_id--; - } - - // For this case: the current sentence is a word only, and the user fixed it, - // so the result will be fixed to the sentence space, and - // lpi_total_ will be set to 0. - if (0 == lpi_total_) { - return get_candidate0(cand_str, max_len, NULL, false); - } - - LemmaIdType id = lpi_items_[cand_id].id; - char16 s[kMaxLemmaSize + 1]; - - uint16 s_len = lpi_items_[cand_id].lma_len; - if (s_len > 1) { - s_len = get_lemma_str(id, s, kMaxLemmaSize + 1); - } else { - // For a single character, Hanzi is ready. - s[0] = lpi_items_[cand_id].hanzi; - s[1] = static_cast(0); - } - - if (s_len > 0 && max_len > s_len) { - utf16_strncpy(cand_str, s, s_len); - cand_str[s_len] = (char16)'\0'; - return cand_str; - } - - return NULL; -} - -void MatrixSearch::update_dict_freq() { - if (NULL != user_dict_) { - // Update the total frequency of all lemmas, including system lemmas and - // user dictionary lemmas. - size_t total_freq = user_dict_->get_total_lemma_count(); - dict_trie_->set_total_lemma_count_of_others(total_freq); - } -} - -bool MatrixSearch::add_lma_to_userdict(uint16 lma_fr, uint16 lma_to, - float score) { - if (lma_to - lma_fr <= 1 || NULL == user_dict_) - return false; - - char16 word_str[kMaxLemmaSize + 1]; - uint16 spl_ids[kMaxLemmaSize]; - - uint16 spl_id_fr = 0; - - for (uint16 pos = lma_fr; pos < lma_to; pos++) { - LemmaIdType lma_id = lma_id_[pos]; - if (is_user_lemma(lma_id)) { - user_dict_->update_lemma(lma_id, 1, true); - } - uint16 lma_len = lma_start_[pos + 1] - lma_start_[pos]; - utf16_strncpy(spl_ids + spl_id_fr, spl_id_ + lma_start_[pos], lma_len); - - uint16 tmp = get_lemma_str(lma_id, word_str + spl_id_fr, - kMaxLemmaSize + 1 - spl_id_fr); - assert(tmp == lma_len); - - tmp = get_lemma_splids(lma_id, spl_ids + spl_id_fr, lma_len, true); - if (tmp != lma_len) { - return false; - } - - spl_id_fr += lma_len; - } - - assert(spl_id_fr <= kMaxLemmaSize); - - return user_dict_->put_lemma(static_cast(word_str), spl_ids, - spl_id_fr, 1); -} - -void MatrixSearch::debug_print_dmi(PoolPosType dmi_pos, uint16 nest_level) { - if (dmi_pos >= dmi_pool_used_) return; - - DictMatchInfo *dmi = dmi_pool_ + dmi_pos; - - if (1 == nest_level) { - printf("-----------------%d\'th DMI node begin----------->\n", dmi_pos); - } - if (dmi->dict_level > 1) { - debug_print_dmi(dmi->dmi_fr, nest_level + 1); - } - printf("---%d\n", dmi->dict_level); - printf(" MileStone: %x, %x\n", dmi->dict_handles[0], dmi->dict_handles[1]); - printf(" Spelling : %s, %d\n", SpellingTrie::get_instance(). - get_spelling_str(dmi->spl_id), dmi->spl_id); - printf(" Total Pinyin Len: %d\n", dmi->splstr_len); - if (1 == nest_level) { - printf("<----------------%d\'th DMI node end--------------\n\n", dmi_pos); - } -} - -bool MatrixSearch::try_add_cand0_to_userdict() { - size_t new_cand_num = get_candidate_num(); - if (fixed_hzs_ > 0 && 1 == new_cand_num) { - float score_from = 0; - uint16 lma_id_from = 0; - uint16 pos = 0; - bool modified = false; - while (pos < fixed_lmas_) { - if (lma_start_[pos + 1] - lma_start_[lma_id_from] > - static_cast(kMaxLemmaSize)) { - float score_to_add = - mtrx_nd_pool_[matrix_[spl_start_[lma_start_[pos]]] - .mtrx_nd_pos].score - score_from; - if (modified) { - score_to_add += 1.0; - if (score_to_add > NGram::kMaxScore) { - score_to_add = NGram::kMaxScore; - } - add_lma_to_userdict(lma_id_from, pos, score_to_add); - } - lma_id_from = pos; - score_from += score_to_add; - - // Clear the flag for next user lemma. - modified = false; - } - - if (0 == fixed_lmas_no1_[pos]) { - modified = true; - } - pos++; - } - - // Single-char word is not allowed to add to userdict. - if (lma_start_[pos] - lma_start_[lma_id_from] > 1) { - float score_to_add = - mtrx_nd_pool_[matrix_[spl_start_[lma_start_[pos]]] - .mtrx_nd_pos].score - score_from; - if (modified) { - score_to_add += 1.0; - if (score_to_add > NGram::kMaxScore) { - score_to_add = NGram::kMaxScore; - } - add_lma_to_userdict(lma_id_from, pos, score_to_add); - } - } - } - return true; -} - -// Choose a candidate, and give new candidates for next step. -// If user finishes selection, we will try to communicate with user dictionary -// to add new items or update score of some existing items. -// -// Basic rule: -// 1. If user selects the first choice: -// 1.1. If the first choice is not a sentence, instead, it is a lemma: -// 1.1.1. If the first choice is a user lemma, notify the user -// dictionary that a user lemma is hit, and add occuring count -// by 1. -// 1.1.2. If the first choice is a system lemma, do nothing. -// 1.2. If the first choice is a sentence containing more than one lemma: -// 1.2.1. The whole sentence will be added as a user lemma. If the -// sentence contains user lemmas, -> hit, and add occuring count -// by 1. -size_t MatrixSearch::choose(size_t cand_id) { - if (!inited_ || 0 == pys_decoded_len_) - return 0; - - if (0 == cand_id) { - fixed_hzs_ = spl_id_num_; - matrix_[spl_start_[fixed_hzs_]].mtrx_nd_fixed = mtrx_nd_pool_ + - matrix_[spl_start_[fixed_hzs_]].mtrx_nd_pos; - for (size_t pos = fixed_lmas_; pos < lma_id_num_; pos++) { - fixed_lmas_no1_[pos] = 1; - } - fixed_lmas_ = lma_id_num_; - lpi_total_ = 0; // Clean all other candidates. - - // 1. It is the first choice - if (1 == lma_id_num_) { - // 1.1. The first choice is not a sentence but a lemma - if (is_user_lemma(lma_id_[0])) { - // 1.1.1. The first choice is a user lemma, notify the user dictionary - // that it is hit. - if (NULL != user_dict_) - user_dict_->update_lemma(lma_id_[0], 1, true); - } else { - // 1.1.2. do thing for a system lemma. - } - } else { - // 1.2. The first choice is a sentence. - // 1.2.1 Try to add the whole sentence to user dictionary, the whole - // sentence may be splitted into many items. - if (NULL != user_dict_) { - try_add_cand0_to_userdict(); - } - } - update_dict_freq(); - return 1; - } else { - cand_id--; - } - - // 2. It is not the full sentence candidate. - // Find the length of the candidate. - LemmaIdType id_chosen = lpi_items_[cand_id].id; - LmaScoreType score_chosen = lpi_items_[cand_id].psb; - size_t cand_len = lpi_items_[cand_id].lma_len; - - assert(cand_len > 0); - - // Notify the atom dictionary that this item is hit. - if (is_user_lemma(id_chosen)) { - if (NULL != user_dict_) { - user_dict_->update_lemma(id_chosen, 1, true); - } - update_dict_freq(); - } - - // 3. Fixed the chosen item. - // 3.1 Get the steps number. - size_t step_fr = spl_start_[fixed_hzs_]; - size_t step_to = spl_start_[fixed_hzs_ + cand_len]; - - // 3.2 Save the length of the original string. - size_t pys_decoded_len = pys_decoded_len_; - - // 3.2 Reset the space of the fixed part. - reset_search(step_to, false, false, true); - - // 3.3 For the last character of the fixed part, the previous DMI - // information will be kept, while the MTRX information will be re-extended, - // and only one node will be extended. - matrix_[step_to].mtrx_nd_num = 0; - - LmaPsbItem lpi_item; - lpi_item.psb = score_chosen; - lpi_item.id = id_chosen; - - PoolPosType step_to_dmi_fr = match_dmi(step_to, - spl_id_ + fixed_hzs_, cand_len); - assert(step_to_dmi_fr != static_cast(-1)); - - extend_mtrx_nd(matrix_[step_fr].mtrx_nd_fixed, &lpi_item, 1, - step_to_dmi_fr, step_to); - - matrix_[step_to].mtrx_nd_fixed = mtrx_nd_pool_ + matrix_[step_to].mtrx_nd_pos; - mtrx_nd_pool_used_ = matrix_[step_to].mtrx_nd_pos + - matrix_[step_to].mtrx_nd_num; - - if (id_chosen == lma_id_[fixed_lmas_]) - fixed_lmas_no1_[fixed_lmas_] = 1; - else - fixed_lmas_no1_[fixed_lmas_] = 0; - lma_id_[fixed_lmas_] = id_chosen; - lma_start_[fixed_lmas_ + 1] = lma_start_[fixed_lmas_] + cand_len; - fixed_lmas_++; - fixed_hzs_ = fixed_hzs_ + cand_len; - - while (step_to != pys_decoded_len) { - bool b = add_char(pys_[step_to]); - assert(b); - step_to++; - } - - if (fixed_hzs_ < spl_id_num_) { - prepare_candidates(); - } else { - lpi_total_ = 0; - if (NULL != user_dict_) { - try_add_cand0_to_userdict(); - } - } - - return get_candidate_num(); -} - -size_t MatrixSearch::cancel_last_choice() { - if (!inited_ || 0 == pys_decoded_len_) - return 0; - - size_t step_start = 0; - if (fixed_hzs_ > 0) { - size_t step_end = spl_start_[fixed_hzs_]; - MatrixNode *end_node = matrix_[step_end].mtrx_nd_fixed; - assert(NULL != end_node); - - step_start = end_node->from->step; - - if (step_start > 0) { - DictMatchInfo *dmi = dmi_pool_ + end_node->dmi_fr; - fixed_hzs_ -= dmi->dict_level; - } else { - fixed_hzs_ = 0; - } - - reset_search(step_start, false, false, false); - - while (pys_[step_start] != '\0') { - bool b = add_char(pys_[step_start]); - assert(b); - step_start++; - } - - prepare_candidates(); - } - return get_candidate_num(); -} - -size_t MatrixSearch::get_fixedlen() { - if (!inited_ || 0 == pys_decoded_len_) - return 0; - return fixed_hzs_; -} - -bool MatrixSearch::prepare_add_char(char ch) { - if (pys_decoded_len_ >= kMaxRowNum - 1 || - (!spl_parser_->is_valid_to_parse(ch) && ch != '\'')) - return false; - - if (dmi_pool_used_ >= kDmiPoolSize) return false; - - pys_[pys_decoded_len_] = ch; - pys_decoded_len_++; - - MatrixRow *mtrx_this_row = matrix_ + pys_decoded_len_; - mtrx_this_row->mtrx_nd_pos = mtrx_nd_pool_used_; - mtrx_this_row->mtrx_nd_num = 0; - mtrx_this_row->dmi_pos = dmi_pool_used_; - mtrx_this_row->dmi_num = 0; - mtrx_this_row->dmi_has_full_id = 0; - - return true; -} - -bool MatrixSearch::is_split_at(uint16 pos) { - return !spl_parser_->is_valid_to_parse(pys_[pos - 1]); -} - -void MatrixSearch::fill_dmi(DictMatchInfo *dmi, MileStoneHandle *handles, - PoolPosType dmi_fr, uint16 spl_id, - uint16 node_num, unsigned char dict_level, - bool splid_end_split, unsigned char splstr_len, - unsigned char all_full_id) { - dmi->dict_handles[0] = handles[0]; - dmi->dict_handles[1] = handles[1]; - dmi->dmi_fr = dmi_fr; - dmi->spl_id = spl_id; - dmi->dict_level = dict_level; - dmi->splid_end_split = splid_end_split ? 1 : 0; - dmi->splstr_len = splstr_len; - dmi->all_full_id = all_full_id; - dmi->c_phrase = 0; -} - -bool MatrixSearch::add_char(char ch) { - if (!prepare_add_char(ch)) - return false; - return add_char_qwerty(); -} - -bool MatrixSearch::add_char_qwerty() { - matrix_[pys_decoded_len_].mtrx_nd_num = 0; - - bool spl_matched = false; - uint16 longest_ext = 0; - // Extend the search matrix, from the oldest unfixed row. ext_len means - // extending length. - for (uint16 ext_len = kMaxPinyinSize + 1; ext_len > 0; ext_len--) { - if (ext_len > pys_decoded_len_ - spl_start_[fixed_hzs_]) - continue; - - // Refer to the declaration of the variable dmi_has_full_id for the - // explanation of this piece of code. In one word, it is used to prevent - // from the unwise extending of "shoud ou" but allow the reasonable - // extending of "heng ao", "lang a", etc. - if (ext_len > 1 && 0 != longest_ext && - 0 == matrix_[pys_decoded_len_ - ext_len].dmi_has_full_id) { - if (xi_an_enabled_) - continue; - else - break; - } - - uint16 oldrow = pys_decoded_len_ - ext_len; - - // 0. If that row is before the last fixed step, ignore. - if (spl_start_[fixed_hzs_] > oldrow) - continue; - - // 1. Check if that old row has valid MatrixNode. If no, means that row is - // not a boundary, either a word boundary or a spelling boundary. - // If it is for extending composing phrase, it's OK to ignore the 0. - if (0 == matrix_[oldrow].mtrx_nd_num && !dmi_c_phrase_) - continue; - - // 2. Get spelling id(s) for the last ext_len chars. - uint16 spl_idx; - bool is_pre = false; - spl_idx = spl_parser_->get_splid_by_str(pys_ + oldrow, - ext_len, &is_pre); - if (is_pre) - spl_matched = true; - - if (0 == spl_idx) - continue; - - bool splid_end_split = is_split_at(oldrow + ext_len); - - // 3. Extend the DMI nodes of that old row - // + 1 is to extend an extra node from the root - for (PoolPosType dmi_pos = matrix_[oldrow].dmi_pos; - dmi_pos < matrix_[oldrow].dmi_pos + matrix_[oldrow].dmi_num + 1; - dmi_pos++) { - DictMatchInfo *dmi = dmi_pool_ + dmi_pos; - if (dmi_pos == matrix_[oldrow].dmi_pos + matrix_[oldrow].dmi_num) { - dmi = NULL; // The last one, NULL means extending from the root. - } else { - // If the dmi is covered by the fixed arrange, ignore it. - if (fixed_hzs_ > 0 && - pys_decoded_len_ - ext_len - dmi->splstr_len < - spl_start_[fixed_hzs_]) { - continue; - } - // If it is not in mode for composing phrase, and the source DMI node - // is marked for composing phrase, ignore this node. - if (dmi->c_phrase != 0 && !dmi_c_phrase_) { - continue; - } - } - - // For example, if "gao" is extended, "g ao" is not allowed. - // or "zh" has been passed, "z h" is not allowed. - // Both word and word-connection will be prevented. - if (longest_ext > ext_len) { - if (NULL == dmi && 0 == matrix_[oldrow].dmi_has_full_id) { - continue; - } - - // "z h" is not allowed. - if (NULL != dmi && spl_trie_->is_half_id(dmi->spl_id)) { - continue; - } - } - - dep_->splids_extended = 0; - if (NULL != dmi) { - uint16 prev_ids_num = dmi->dict_level; - if ((!dmi_c_phrase_ && prev_ids_num >= kMaxLemmaSize) || - (dmi_c_phrase_ && prev_ids_num >= kMaxRowNum)) { - continue; - } - - DictMatchInfo *d = dmi; - while (d) { - dep_->splids[--prev_ids_num] = d->spl_id; - if ((PoolPosType)-1 == d->dmi_fr) - break; - d = dmi_pool_ + d->dmi_fr; - } - assert(0 == prev_ids_num); - dep_->splids_extended = dmi->dict_level; - } - dep_->splids[dep_->splids_extended] = spl_idx; - dep_->ext_len = ext_len; - dep_->splid_end_split = splid_end_split; - - dep_->id_num = 1; - dep_->id_start = spl_idx; - if (spl_trie_->is_half_id(spl_idx)) { - // Get the full id list - dep_->id_num = spl_trie_->half_to_full(spl_idx, &(dep_->id_start)); - assert(dep_->id_num > 0); - } - - uint16 new_dmi_num; - - new_dmi_num = extend_dmi(dep_, dmi); - - if (new_dmi_num > 0) { - if (dmi_c_phrase_) { - dmi_pool_[dmi_pool_used_].c_phrase = 1; - } - matrix_[pys_decoded_len_].dmi_num += new_dmi_num; - dmi_pool_used_ += new_dmi_num; - - if (!spl_trie_->is_half_id(spl_idx)) - matrix_[pys_decoded_len_].dmi_has_full_id = 1; - } - - // If get candiate lemmas, try to extend the path - if (lpi_total_ > 0) { - uint16 fr_row; - if (NULL == dmi) { - fr_row = oldrow; - } else { - assert(oldrow >= dmi->splstr_len); - fr_row = oldrow - dmi->splstr_len; - } - for (PoolPosType mtrx_nd_pos = matrix_[fr_row].mtrx_nd_pos; - mtrx_nd_pos < matrix_[fr_row].mtrx_nd_pos + - matrix_[fr_row].mtrx_nd_num; - mtrx_nd_pos++) { - MatrixNode *mtrx_nd = mtrx_nd_pool_ + mtrx_nd_pos; - - extend_mtrx_nd(mtrx_nd, lpi_items_, lpi_total_, - dmi_pool_used_ - new_dmi_num, pys_decoded_len_); - if (longest_ext == 0) - longest_ext = ext_len; - } - } - } // for dmi_pos - } // for ext_len - mtrx_nd_pool_used_ += matrix_[pys_decoded_len_].mtrx_nd_num; - - if (dmi_c_phrase_) - return true; - - return (matrix_[pys_decoded_len_].mtrx_nd_num != 0 || spl_matched); -} - -void MatrixSearch::prepare_candidates() { - // Get candiates from the first un-fixed step. - uint16 lma_size_max = kMaxLemmaSize; - if (lma_size_max > spl_id_num_ - fixed_hzs_) - lma_size_max = spl_id_num_ - fixed_hzs_; - - uint16 lma_size = lma_size_max; - - // If the full sentense candidate's unfixed part may be the same with a normal - // lemma. Remove the lemma candidate in this case. - char16 fullsent[kMaxLemmaSize + 1]; - char16 *pfullsent = NULL; - uint16 sent_len; - pfullsent = get_candidate0(fullsent, kMaxLemmaSize + 1, &sent_len, true); - - // If the unfixed part contains more than one ids, it is not necessary to - // check whether a lemma's string is the same to the unfixed part of the full - // sentence candidate, so, set it to NULL; - if (sent_len > kMaxLemmaSize) - pfullsent = NULL; - - lpi_total_ = 0; - size_t lpi_num_full_match = 0; // Number of items which are fully-matched. - while (lma_size > 0) { - size_t lma_num; - lma_num = get_lpis(spl_id_ + fixed_hzs_, lma_size, - lpi_items_ + lpi_total_, - size_t(kMaxLmaPsbItems - lpi_total_), - pfullsent, lma_size == lma_size_max); - - if (lma_num > 0) { - lpi_total_ += lma_num; - // For next lemma candidates which are not the longest, it is not - // necessary to compare with the full sentence candiate. - pfullsent = NULL; - } - if (lma_size == lma_size_max) { - lpi_num_full_match = lpi_total_; - } - lma_size--; - } - - // Sort those partially-matched items by their unified scores. - myqsort(lpi_items_ + lpi_num_full_match, lpi_total_ - lpi_num_full_match, - sizeof(LmaPsbItem), cmp_lpi_with_unified_psb); - - if (kPrintDebug0) { - printf("-----Prepare candidates, score:\n"); - for (size_t a = 0; a < lpi_total_; a++) { - printf("[%03d]%d ", a, lpi_items_[a].psb); - if ((a + 1) % 6 == 0) printf("\n"); - } - printf("\n"); - } - - if (kPrintDebug0) { - printf("--- lpi_total_ = %d\n", lpi_total_); - } -} - -const char* MatrixSearch::get_pystr(size_t *decoded_len) { - if (!inited_ || NULL == decoded_len) - return NULL; - - *decoded_len = pys_decoded_len_; - return pys_; -} - -void MatrixSearch::merge_fixed_lmas(size_t del_spl_pos) { - if (fixed_lmas_ == 0) - return; - // Update spelling segmentation information first. - spl_id_num_ -= 1; - uint16 del_py_len = spl_start_[del_spl_pos + 1] - spl_start_[del_spl_pos]; - for (size_t pos = del_spl_pos; pos <= spl_id_num_; pos++) { - spl_start_[pos] = spl_start_[pos + 1] - del_py_len; - if (pos == spl_id_num_) - break; - spl_id_[pos] = spl_id_[pos + 1]; - } - - // Begin to merge. - uint16 phrase_len = 0; - - // Update the spelling ids to the composing phrase. - // We need to convert these ids into full id in the future. - memcpy(c_phrase_.spl_ids, spl_id_, spl_id_num_ * sizeof(uint16)); - memcpy(c_phrase_.spl_start, spl_start_, (spl_id_num_ + 1) * sizeof(uint16)); - - // If composing phrase has not been created, first merge all fixed - // lemmas into a composing phrase without deletion. - if (fixed_lmas_ > 1 || kLemmaIdComposing != lma_id_[0]) { - uint16 bp = 1; // Begin position of real fixed lemmas. - // There is no existing composing phrase. - if (kLemmaIdComposing != lma_id_[0]) { - c_phrase_.sublma_num = 0; - bp = 0; - } - - uint16 sub_num = c_phrase_.sublma_num; - for (uint16 pos = bp; pos <= fixed_lmas_; pos++) { - c_phrase_.sublma_start[sub_num + pos - bp] = lma_start_[pos]; - if (lma_start_[pos] > del_spl_pos) { - c_phrase_.sublma_start[sub_num + pos - bp] -= 1; - } - - if (pos == fixed_lmas_) - break; - - uint16 lma_len; - char16 *lma_str = c_phrase_.chn_str + - c_phrase_.sublma_start[sub_num] + phrase_len; - - lma_len = get_lemma_str(lma_id_[pos], lma_str, kMaxRowNum - phrase_len); - assert(lma_len == lma_start_[pos + 1] - lma_start_[pos]); - phrase_len += lma_len; - } - assert(phrase_len == lma_start_[fixed_lmas_]); - c_phrase_.length = phrase_len; // will be deleted by 1 - c_phrase_.sublma_num += fixed_lmas_ - bp; - } else { - for (uint16 pos = 0; pos <= c_phrase_.sublma_num; pos++) { - if (c_phrase_.sublma_start[pos] > del_spl_pos) { - c_phrase_.sublma_start[pos] -= 1; - } - } - phrase_len = c_phrase_.length; - } - - assert(phrase_len > 0); - if (1 == phrase_len) { - // After the only one is deleted, nothing will be left. - fixed_lmas_ = 0; - return; - } - - // Delete the Chinese character in the merged phrase. - // The corresponding elements in spl_ids and spl_start of the - // phrase have been deleted. - char16 *chn_str = c_phrase_.chn_str + del_spl_pos; - for (uint16 pos = 0; - pos < c_phrase_.sublma_start[c_phrase_.sublma_num] - del_spl_pos; - pos++) { - chn_str[pos] = chn_str[pos + 1]; - } - c_phrase_.length -= 1; - - // If the deleted spelling id is in a sub lemma which contains more than - // one id, del_a_sub will be false; but if the deleted id is in a sub lemma - // which only contains 1 id, the whole sub lemma needs to be deleted, so - // del_a_sub will be true. - bool del_a_sub = false; - for (uint16 pos = 1; pos <= c_phrase_.sublma_num; pos++) { - if (c_phrase_.sublma_start[pos - 1] == - c_phrase_.sublma_start[pos]) { - del_a_sub = true; - } - if (del_a_sub) { - c_phrase_.sublma_start[pos - 1] = - c_phrase_.sublma_start[pos]; - } - } - if (del_a_sub) - c_phrase_.sublma_num -= 1; - - return; -} - -void MatrixSearch::get_spl_start_id() { - lma_id_num_ = 0; - lma_start_[0] = 0; - - spl_id_num_ = 0; - spl_start_[0] = 0; - if (!inited_ || 0 == pys_decoded_len_ || - 0 == matrix_[pys_decoded_len_].mtrx_nd_num) - return; - - // Calculate number of lemmas and spellings - // Only scan those part which is not fixed. - lma_id_num_ = fixed_lmas_; - spl_id_num_ = fixed_hzs_; - - MatrixNode *mtrx_nd = mtrx_nd_pool_ + matrix_[pys_decoded_len_].mtrx_nd_pos; - while (mtrx_nd != mtrx_nd_pool_) { - if (fixed_hzs_ > 0) { - if (mtrx_nd->step <= spl_start_[fixed_hzs_]) - break; - } - - // Update the spelling segamentation information - unsigned char word_splstr_len = 0; - PoolPosType dmi_fr = mtrx_nd->dmi_fr; - if ((PoolPosType)-1 != dmi_fr) - word_splstr_len = dmi_pool_[dmi_fr].splstr_len; - - while ((PoolPosType)-1 != dmi_fr) { - spl_start_[spl_id_num_ + 1] = mtrx_nd->step - - (word_splstr_len - dmi_pool_[dmi_fr].splstr_len); - spl_id_[spl_id_num_] = dmi_pool_[dmi_fr].spl_id; - spl_id_num_++; - dmi_fr = dmi_pool_[dmi_fr].dmi_fr; - } - - // Update the lemma segmentation information - lma_start_[lma_id_num_ + 1] = spl_id_num_; - lma_id_[lma_id_num_] = mtrx_nd->id; - lma_id_num_++; - - mtrx_nd = mtrx_nd->from; - } - - // Reverse the result of spelling info - for (size_t pos = fixed_hzs_; - pos < fixed_hzs_ + (spl_id_num_ - fixed_hzs_ + 1) / 2; pos++) { - if (spl_id_num_ + fixed_hzs_ - pos != pos + 1) { - spl_start_[pos + 1] ^= spl_start_[spl_id_num_ - pos + fixed_hzs_]; - spl_start_[spl_id_num_ - pos + fixed_hzs_] ^= spl_start_[pos + 1]; - spl_start_[pos + 1] ^= spl_start_[spl_id_num_ - pos + fixed_hzs_]; - - spl_id_[pos] ^= spl_id_[spl_id_num_ + fixed_hzs_ - pos - 1]; - spl_id_[spl_id_num_ + fixed_hzs_- pos - 1] ^= spl_id_[pos]; - spl_id_[pos] ^= spl_id_[spl_id_num_ + fixed_hzs_- pos - 1]; - } - } - - // Reverse the result of lemma info - for (size_t pos = fixed_lmas_; - pos < fixed_lmas_ + (lma_id_num_ - fixed_lmas_ + 1) / 2; pos++) { - assert(lma_id_num_ + fixed_lmas_ - pos - 1 >= pos); - - if (lma_id_num_ + fixed_lmas_ - pos > pos + 1) { - lma_start_[pos + 1] ^= lma_start_[lma_id_num_ - pos + fixed_lmas_]; - lma_start_[lma_id_num_ - pos + fixed_lmas_] ^= lma_start_[pos + 1]; - lma_start_[pos + 1] ^= lma_start_[lma_id_num_ - pos + fixed_lmas_]; - - lma_id_[pos] ^= lma_id_[lma_id_num_ - 1 - pos + fixed_lmas_]; - lma_id_[lma_id_num_ - 1 - pos + fixed_lmas_] ^= lma_id_[pos]; - lma_id_[pos] ^= lma_id_[lma_id_num_ - 1 - pos + fixed_lmas_]; - } - } - - for (size_t pos = fixed_lmas_ + 1; pos <= lma_id_num_; pos++) { - if (pos < lma_id_num_) - lma_start_[pos] = lma_start_[pos - 1] + - (lma_start_[pos] - lma_start_[pos + 1]); - else - lma_start_[pos] = lma_start_[pos - 1] + lma_start_[pos] - - lma_start_[fixed_lmas_]; - } - - // Find the last fixed position - fixed_hzs_ = 0; - for (size_t pos = spl_id_num_; pos > 0; pos--) { - if (NULL != matrix_[spl_start_[pos]].mtrx_nd_fixed) { - fixed_hzs_ = pos; - break; - } - } - - return; -} - -size_t MatrixSearch::get_spl_start(const uint16 *&spl_start) { - get_spl_start_id(); - spl_start = spl_start_; - return spl_id_num_; -} - -size_t MatrixSearch::extend_dmi(DictExtPara *dep, DictMatchInfo *dmi_s) { - if (dmi_pool_used_ >= kDmiPoolSize) return 0; - - if (dmi_c_phrase_) - return extend_dmi_c(dep, dmi_s); - - LpiCache& lpi_cache = LpiCache::get_instance(); - uint16 splid = dep->splids[dep->splids_extended]; - - bool cached = false; - if (0 == dep->splids_extended) - cached = lpi_cache.is_cached(splid); - - // 1. If this is a half Id, get its corresponding full starting Id and - // number of full Id. - size_t ret_val = 0; - PoolPosType mtrx_dmi_fr = (PoolPosType)-1; // From which dmi node - - lpi_total_ = 0; - - MileStoneHandle from_h[3]; - from_h[0] = 0; - from_h[1] = 0; - - if (0 != dep->splids_extended) { - from_h[0] = dmi_s->dict_handles[0]; - from_h[1] = dmi_s->dict_handles[1]; - } - - // 2. Begin exgtending in the system dictionary - size_t lpi_num = 0; - MileStoneHandle handles[2]; - handles[0] = handles[1] = 0; - if (from_h[0] > 0 || NULL == dmi_s) { - handles[0] = dict_trie_->extend_dict(from_h[0], dep, lpi_items_, - kMaxLmaPsbItems, &lpi_num); - } - if (handles[0] > 0) - lpi_total_ = lpi_num; - - if (NULL == dmi_s) { // from root - assert(0 != handles[0]); - mtrx_dmi_fr = dmi_pool_used_; - } - - // 3. Begin extending in the user dictionary - if (NULL != user_dict_ && (from_h[1] > 0 || NULL == dmi_s)) { - handles[1] = user_dict_->extend_dict(from_h[1], dep, - lpi_items_ + lpi_total_, - kMaxLmaPsbItems - lpi_total_, - &lpi_num); - if (handles[1] > 0) { - if (kPrintDebug0) { - for (size_t t = 0; t < lpi_num; t++) { - printf("--Extend in user dict: uid:%d uscore:%d\n", lpi_items_[lpi_total_ + t].id, - lpi_items_[lpi_total_ + t].psb); - } - } - lpi_total_ += lpi_num; - } - } - - if (0 != handles[0] || 0 != handles[1]) { - if (dmi_pool_used_ >= kDmiPoolSize) return 0; - - DictMatchInfo *dmi_add = dmi_pool_ + dmi_pool_used_; - if (NULL == dmi_s) { - fill_dmi(dmi_add, handles, - (PoolPosType)-1, splid, - 1, 1, dep->splid_end_split, dep->ext_len, - spl_trie_->is_half_id(splid) ? 0 : 1); - } else { - fill_dmi(dmi_add, handles, - dmi_s - dmi_pool_, splid, 1, - dmi_s->dict_level + 1, dep->splid_end_split, - dmi_s->splstr_len + dep->ext_len, - spl_trie_->is_half_id(splid) ? 0 : dmi_s->all_full_id); - } - - ret_val = 1; - } - - if (!cached) { - if (0 == lpi_total_) - return ret_val; - - if (kPrintDebug0) { - printf("--- lpi_total_ = %d\n", lpi_total_); - } - - myqsort(lpi_items_, lpi_total_, sizeof(LmaPsbItem), cmp_lpi_with_psb); - if (NULL == dmi_s && spl_trie_->is_half_id(splid)) - lpi_total_ = lpi_cache.put_cache(splid, lpi_items_, lpi_total_); - } else { - assert(spl_trie_->is_half_id(splid)); - lpi_total_ = lpi_cache.get_cache(splid, lpi_items_, kMaxLmaPsbItems); - } - - return ret_val; -} - -size_t MatrixSearch::extend_dmi_c(DictExtPara *dep, DictMatchInfo *dmi_s) { - lpi_total_ = 0; - - uint16 pos = dep->splids_extended; - assert(dmi_c_phrase_); - if (pos >= c_phrase_.length) - return 0; - - uint16 splid = dep->splids[pos]; - if (splid == c_phrase_.spl_ids[pos]) { - DictMatchInfo *dmi_add = dmi_pool_ + dmi_pool_used_; - MileStoneHandle handles[2]; // Actually never used. - if (NULL == dmi_s) - fill_dmi(dmi_add, handles, - (PoolPosType)-1, splid, - 1, 1, dep->splid_end_split, dep->ext_len, - spl_trie_->is_half_id(splid) ? 0 : 1); - else - fill_dmi(dmi_add, handles, - dmi_s - dmi_pool_, splid, 1, - dmi_s->dict_level + 1, dep->splid_end_split, - dmi_s->splstr_len + dep->ext_len, - spl_trie_->is_half_id(splid) ? 0 : dmi_s->all_full_id); - - if (pos == c_phrase_.length - 1) { - lpi_items_[0].id = kLemmaIdComposing; - lpi_items_[0].psb = 0; // 0 is bigger than normal lemma score. - lpi_total_ = 1; - } - return 1; - } - return 0; -} - -size_t MatrixSearch::extend_mtrx_nd(MatrixNode *mtrx_nd, LmaPsbItem lpi_items[], - size_t lpi_num, PoolPosType dmi_fr, - size_t res_row) { - assert(NULL != mtrx_nd); - matrix_[res_row].mtrx_nd_fixed = NULL; - - if (mtrx_nd_pool_used_ >= kMtrxNdPoolSize - kMaxNodeARow) - return 0; - - if (0 == mtrx_nd->step) { - // Because the list is sorted, if the source step is 0, it is only - // necessary to pick up the first kMaxNodeARow items. - if (lpi_num > kMaxNodeARow) - lpi_num = kMaxNodeARow; - } - - MatrixNode *mtrx_nd_res_min = mtrx_nd_pool_ + matrix_[res_row].mtrx_nd_pos; - for (size_t pos = 0; pos < lpi_num; pos++) { - float score = mtrx_nd->score + lpi_items[pos].psb; - if (pos > 0 && score - PRUMING_SCORE > mtrx_nd_res_min->score) - break; - - // Try to add a new node - size_t mtrx_nd_num = matrix_[res_row].mtrx_nd_num; - MatrixNode *mtrx_nd_res = mtrx_nd_res_min + mtrx_nd_num; - bool replace = false; - // Find its position - while (mtrx_nd_res > mtrx_nd_res_min && score < (mtrx_nd_res - 1)->score) { - if (static_cast(mtrx_nd_res - mtrx_nd_res_min) < kMaxNodeARow) - *mtrx_nd_res = *(mtrx_nd_res - 1); - mtrx_nd_res--; - replace = true; - } - if (replace || (mtrx_nd_num < kMaxNodeARow && - matrix_[res_row].mtrx_nd_pos + mtrx_nd_num < kMtrxNdPoolSize)) { - mtrx_nd_res->id = lpi_items[pos].id; - mtrx_nd_res->score = score; - mtrx_nd_res->from = mtrx_nd; - mtrx_nd_res->dmi_fr = dmi_fr; - mtrx_nd_res->step = res_row; - if (matrix_[res_row].mtrx_nd_num < kMaxNodeARow) - matrix_[res_row].mtrx_nd_num++; - } - } - return matrix_[res_row].mtrx_nd_num; -} - -PoolPosType MatrixSearch::match_dmi(size_t step_to, uint16 spl_ids[], - uint16 spl_id_num) { - if (pys_decoded_len_ < step_to || 0 == matrix_[step_to].dmi_num) { - return static_cast(-1); - } - - for (PoolPosType dmi_pos = 0; dmi_pos < matrix_[step_to].dmi_num; dmi_pos++) { - DictMatchInfo *dmi = dmi_pool_ + matrix_[step_to].dmi_pos + dmi_pos; - - if (dmi->dict_level != spl_id_num) - continue; - - bool matched = true; - for (uint16 spl_pos = 0; spl_pos < spl_id_num; spl_pos++) { - if (spl_ids[spl_id_num - spl_pos - 1] != dmi->spl_id) { - matched = false; - break; - } - - dmi = dmi_pool_ + dmi->dmi_fr; - } - if (matched) { - return matrix_[step_to].dmi_pos + dmi_pos; - } - } - - return static_cast(-1); -} - -char16* MatrixSearch::get_candidate0(char16 *cand_str, size_t max_len, - uint16 *retstr_len, - bool only_unfixed) { - if (pys_decoded_len_ == 0 || - matrix_[pys_decoded_len_].mtrx_nd_num == 0) - return NULL; - - LemmaIdType idxs[kMaxRowNum]; - size_t id_num = 0; - - MatrixNode *mtrx_nd = mtrx_nd_pool_ + matrix_[pys_decoded_len_].mtrx_nd_pos; - - if (kPrintDebug0) { - printf("--- sentence score: %f\n", mtrx_nd->score); - } - - if (kPrintDebug1) { - printf("==============Sentence DMI (reverse order) begin===========>>\n"); - } - - while (mtrx_nd != NULL) { - idxs[id_num] = mtrx_nd->id; - id_num++; - - if (kPrintDebug1) { - printf("---MatrixNode [step: %d, lma_idx: %d, total score:%.5f]\n", - mtrx_nd->step, mtrx_nd->id, mtrx_nd->score); - debug_print_dmi(mtrx_nd->dmi_fr, 1); - } - - mtrx_nd = mtrx_nd->from; - } - - if (kPrintDebug1) { - printf("<<==============Sentence DMI (reverse order) end=============\n"); - } - - size_t ret_pos = 0; - do { - id_num--; - if (0 == idxs[id_num]) - continue; - - char16 str[kMaxLemmaSize + 1]; - uint16 str_len = get_lemma_str(idxs[id_num], str, kMaxLemmaSize + 1); - if (str_len > 0 && max_len - ret_pos > str_len) { - if (!only_unfixed) - utf16_strncpy(cand_str + ret_pos, str, str_len); - else if (ret_pos >= fixed_hzs_) - utf16_strncpy(cand_str + ret_pos - fixed_hzs_, str, str_len); - - ret_pos += str_len; - } else { - return NULL; - } - } while (id_num != 0); - - if (!only_unfixed) { - if (NULL != retstr_len) - *retstr_len = ret_pos; - cand_str[ret_pos] = (char16)'\0'; - } else { - if (NULL != retstr_len) - *retstr_len = ret_pos - fixed_hzs_; - cand_str[ret_pos - fixed_hzs_] = (char16)'\0'; - } - return cand_str; -} - -size_t MatrixSearch::get_lpis(const uint16* splid_str, size_t splid_str_len, - LmaPsbItem* lma_buf, size_t max_lma_buf, - const char16 *pfullsent, bool sort_by_psb) { - if (splid_str_len > kMaxLemmaSize) - return 0; - - size_t num1 = dict_trie_->get_lpis(splid_str, splid_str_len, - lma_buf, max_lma_buf); - size_t num2 = 0; - if (NULL != user_dict_) { - num2 = user_dict_->get_lpis(splid_str, splid_str_len, - lma_buf + num1, max_lma_buf - num1); - } - - size_t num = num1 + num2; - - if (0 == num) - return 0; - - // Remove repeated items. - if (splid_str_len > 1) { - LmaPsbStrItem *lpsis = reinterpret_cast(lma_buf + num); - size_t lpsi_num = (max_lma_buf - num) * sizeof(LmaPsbItem) / - sizeof(LmaPsbStrItem); - assert(lpsi_num > num); - if (num > lpsi_num) num = lpsi_num; - lpsi_num = num; - - for (size_t pos = 0; pos < lpsi_num; pos++) { - lpsis[pos].lpi = lma_buf[pos]; - get_lemma_str(lma_buf[pos].id, lpsis[pos].str, kMaxLemmaSize + 1); - } - - myqsort(lpsis, lpsi_num, sizeof(LmaPsbStrItem), cmp_lpsi_with_str); - - size_t remain_num = 0; - for (size_t pos = 0; pos < lpsi_num; pos++) { - if (pos > 0 && utf16_strcmp(lpsis[pos].str, lpsis[pos - 1].str) == 0) { - if (lpsis[pos].lpi.psb < lpsis[pos - 1].lpi.psb) { - assert(remain_num > 0); - lma_buf[remain_num - 1] = lpsis[pos].lpi; - } - continue; - } - if (NULL != pfullsent && utf16_strcmp(lpsis[pos].str, pfullsent) == 0) - continue; - - lma_buf[remain_num] = lpsis[pos].lpi; - remain_num++; - } - - // Update the result number - num = remain_num; - } else { - // For single character, some characters have more than one spelling, for - // example, "de" and "di" are all valid for a Chinese character, so when - // the user input "d", repeated items are generated. - // For single character lemmas, Hanzis will be gotten - for (size_t pos = 0; pos < num; pos++) { - char16 hanzis[2]; - get_lemma_str(lma_buf[pos].id, hanzis, 2); - lma_buf[pos].hanzi = hanzis[0]; - } - - myqsort(lma_buf, num, sizeof(LmaPsbItem), cmp_lpi_with_hanzi); - - size_t remain_num = 0; - for (size_t pos = 0; pos < num; pos++) { - if (pos > 0 && lma_buf[pos].hanzi == lma_buf[pos - 1].hanzi) { - if (NULL != pfullsent && - static_cast(0) == pfullsent[1] && - lma_buf[pos].hanzi == pfullsent[0]) - continue; - - if (lma_buf[pos].psb < lma_buf[pos - 1].psb) { - assert(remain_num > 0); - assert(lma_buf[remain_num - 1].hanzi == lma_buf[pos].hanzi); - lma_buf[remain_num - 1] = lma_buf[pos]; - } - continue; - } - if (NULL != pfullsent && - static_cast(0) == pfullsent[1] && - lma_buf[pos].hanzi == pfullsent[0]) - continue; - - lma_buf[remain_num] = lma_buf[pos]; - remain_num++; - } - - num = remain_num; - } - - if (sort_by_psb) { - myqsort(lma_buf, num, sizeof(LmaPsbItem), cmp_lpi_with_psb); - } - return num; -} - -uint16 MatrixSearch::get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, - uint16 str_max) { - uint16 str_len = 0; - - if (is_system_lemma(id_lemma)) { - str_len = dict_trie_->get_lemma_str(id_lemma, str_buf, str_max); - } else if (is_user_lemma(id_lemma)) { - if (NULL != user_dict_) { - str_len = user_dict_->get_lemma_str(id_lemma, str_buf, str_max); - } else { - str_len = 0; - str_buf[0] = static_cast('\0'); - } - } else if (is_composing_lemma(id_lemma)) { - if (str_max <= 1) - return 0; - str_len = c_phrase_.sublma_start[c_phrase_.sublma_num]; - if (str_len > str_max - 1) - str_len = str_max - 1; - utf16_strncpy(str_buf, c_phrase_.chn_str, str_len); - str_buf[str_len] = (char16)'\0'; - return str_len; - } - - return str_len; -} - -uint16 MatrixSearch::get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, - uint16 splids_max, bool arg_valid) { - uint16 splid_num = 0; - - if (arg_valid) { - for (splid_num = 0; splid_num < splids_max; splid_num++) { - if (spl_trie_->is_half_id(splids[splid_num])) - break; - } - if (splid_num == splids_max) - return splid_num; - } - - if (is_system_lemma(id_lemma)) { - splid_num = dict_trie_->get_lemma_splids(id_lemma, splids, splids_max, - arg_valid); - } else if (is_user_lemma(id_lemma)) { - if (NULL != user_dict_) { - splid_num = user_dict_->get_lemma_splids(id_lemma, splids, splids_max, - arg_valid); - } else { - splid_num = 0; - } - } else if (is_composing_lemma(id_lemma)) { - if (c_phrase_.length > splids_max) { - return 0; - } - for (uint16 pos = 0; pos < c_phrase_.length; pos++) { - splids[pos] = c_phrase_.spl_ids[pos]; - if (spl_trie_->is_half_id(splids[pos])) { - return 0; - } - } - } - return splid_num; -} - -size_t MatrixSearch::inner_predict(const char16 *fixed_buf, uint16 fixed_len, - char16 predict_buf[][kMaxPredictSize + 1], - size_t buf_len) { - size_t res_total = 0; - memset(npre_items_, 0, sizeof(NPredictItem) * npre_items_len_); - // In order to shorten the comments, j-character candidates predicted by - // i-character prefix are called P(i,j). All candiates predicted by - // i-character prefix are called P(i,*) - // Step 1. Get P(kMaxPredictSize, *) and sort them, here - // P(kMaxPredictSize, *) == P(kMaxPredictSize, 1) - for (size_t len = fixed_len; len >0; len--) { - // How many blank items are available - size_t this_max = npre_items_len_ - res_total; - size_t res_this; - // If the history is longer than 1, and we can not get prediction from - // lemmas longer than 2, in this case, we will add lemmas with - // highest scores as the prediction result. - if (fixed_len > 1 && 1 == len && 0 == res_total) { - // Try to find if recent n (n>1) characters can be a valid lemma in system - // dictionary. - bool nearest_n_word = false; - for (size_t nlen = 2; nlen <= fixed_len; nlen++) { - if (dict_trie_->get_lemma_id(fixed_buf + fixed_len - nlen, nlen) > 0) { - nearest_n_word = true; - break; - } - } - res_this = dict_trie_->predict_top_lmas(nearest_n_word ? len : 0, - npre_items_ + res_total, - this_max, res_total); - res_total += res_this; - } - - // How many blank items are available - this_max = npre_items_len_ - res_total; - res_this = 0; - if (!kOnlyUserDictPredict) { - res_this = - dict_trie_->predict(fixed_buf + fixed_len - len, len, - npre_items_ + res_total, this_max, - res_total); - } - - if (NULL != user_dict_) { - res_this = res_this + - user_dict_->predict(fixed_buf + fixed_len - len, len, - npre_items_ + res_total + res_this, - this_max - res_this, res_total + res_this); - } - - if (kPredictLimitGt1) { - myqsort(npre_items_ + res_total, res_this, sizeof(NPredictItem), - cmp_npre_by_score); - - if (len > 3) { - if (res_this > kMaxPredictNumByGt3) - res_this = kMaxPredictNumByGt3; - } else if (3 == len) { - if (res_this > kMaxPredictNumBy3) - res_this = kMaxPredictNumBy3; - } else if (2 == len) { - if (res_this > kMaxPredictNumBy2) - res_this = kMaxPredictNumBy2; - } - } - - res_total += res_this; - } - - res_total = remove_duplicate_npre(npre_items_, res_total); - - if (kPreferLongHistoryPredict) { - myqsort(npre_items_, res_total, sizeof(NPredictItem), - cmp_npre_by_hislen_score); - } else { - myqsort(npre_items_, res_total, sizeof(NPredictItem), - cmp_npre_by_score); - } - - if (buf_len < res_total) { - res_total = buf_len; - } - - if (kPrintDebug2) { - printf("/////////////////Predicted Items Begin////////////////////>>\n"); - for (size_t i = 0; i < res_total; i++) { - printf("---"); - for (size_t j = 0; j < kMaxPredictSize; j++) { - printf("%d ", npre_items_[i].pre_hzs[j]); - } - printf("\n"); - } - printf("< kMaxPredictSize || 0 == buf_len) - return 0; - - return inner_predict(fixed_buf, fixed_len, predict_buf, buf_len); -} - -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/mystdlib.cpp b/PinyinIME/jni/share/mystdlib.cpp deleted file mode 100644 index 93bbcc9..0000000 --- a/PinyinIME/jni/share/mystdlib.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -namespace ime_pinyin { - -// For debug purpose. You can add a fixed version of qsort and bsearch functions -// here so that the output will be totally the same under different platforms. - -void myqsort(void *p, size_t n, size_t es, - int (*cmp)(const void *, const void *)) { - qsort(p,n, es, cmp); -} - -void *mybsearch(const void *k, const void *b, - size_t n, size_t es, - int (*cmp)(const void *, const void *)) { - return bsearch(k, b, n, es, cmp); -} -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/ngram.cpp b/PinyinIME/jni/share/ngram.cpp deleted file mode 100644 index d95477a..0000000 --- a/PinyinIME/jni/share/ngram.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include "../include/mystdlib.h" -#include "../include/ngram.h" - -namespace ime_pinyin { - -#define ADD_COUNT 0.3 - -int comp_double(const void *p1, const void *p2) { - if (*static_cast(p1) < *static_cast(p2)) - return -1; - if (*static_cast(p1) > *static_cast(p2)) - return 1; - return 0; -} - -inline double distance(double freq, double code) { - // return fabs(freq - code); - return freq * fabs(log(freq) - log(code)); -} - -// Find the index of the code value which is nearest to the given freq -int qsearch_nearest(double code_book[], double freq, int start, int end) { - if (start == end) - return start; - - if (start + 1 == end) { - if (distance(freq, code_book[end]) > distance(freq, code_book[start])) - return start; - return end; - } - - int mid = (start + end) / 2; - - if (code_book[mid] > freq) - return qsearch_nearest(code_book, freq, start, mid); - else - return qsearch_nearest(code_book, freq, mid, end); -} - -size_t update_code_idx(double freqs[], size_t num, double code_book[], - CODEBOOK_TYPE *code_idx) { - size_t changed = 0; - for (size_t pos = 0; pos < num; pos++) { - CODEBOOK_TYPE idx; - idx = qsearch_nearest(code_book, freqs[pos], 0, kCodeBookSize - 1); - if (idx != code_idx[pos]) - changed++; - code_idx[pos] = idx; - } - return changed; -} - -double recalculate_kernel(double freqs[], size_t num, double code_book[], - CODEBOOK_TYPE *code_idx) { - double ret = 0; - - size_t *item_num = new size_t[kCodeBookSize]; - assert(item_num); - memset(item_num, 0, sizeof(size_t) * kCodeBookSize); - - double *cb_new = new double[kCodeBookSize]; - assert(cb_new); - memset(cb_new, 0, sizeof(double) * kCodeBookSize); - - for (size_t pos = 0; pos < num; pos++) { - ret += distance(freqs[pos], code_book[code_idx[pos]]); - - cb_new[code_idx[pos]] += freqs[pos]; - item_num[code_idx[pos]] += 1; - } - - for (size_t code = 0; code < kCodeBookSize; code++) { - assert(item_num[code] > 0); - code_book[code] = cb_new[code] / item_num[code]; - } - - delete [] item_num; - delete [] cb_new; - - return ret; -} - -void iterate_codes(double freqs[], size_t num, double code_book[], - CODEBOOK_TYPE *code_idx) { - size_t iter_num = 0; - double delta_last = 0; - do { - size_t changed = update_code_idx(freqs, num, code_book, code_idx); - - double delta = recalculate_kernel(freqs, num, code_book, code_idx); - - if (kPrintDebug0) { - printf("---Unigram codebook iteration: %d : %d, %.9f\n", - iter_num, changed, delta); - } - iter_num++; - - if (iter_num > 1 && - (delta == 0 || fabs(delta_last - delta)/fabs(delta) < 0.000000001)) - break; - delta_last = delta; - } while (true); -} - - -NGram* NGram::instance_ = NULL; - -NGram::NGram() { - initialized_ = false; - idx_num_ = 0; - lma_freq_idx_ = NULL; - sys_score_compensation_ = 0; - -#ifdef ___BUILD_MODEL___ - freq_codes_df_ = NULL; -#endif - freq_codes_ = NULL; -} - -NGram::~NGram() { - if (NULL != lma_freq_idx_) - free(lma_freq_idx_); - -#ifdef ___BUILD_MODEL___ - if (NULL != freq_codes_df_) - free(freq_codes_df_); -#endif - - if (NULL != freq_codes_) - free(freq_codes_); -} - -NGram& NGram::get_instance() { - if (NULL == instance_) - instance_ = new NGram(); - return *instance_; -} - -bool NGram::save_ngram(FILE *fp) { - if (!initialized_ || NULL == fp) - return false; - - if (0 == idx_num_ || NULL == freq_codes_ || NULL == lma_freq_idx_) - return false; - - if (fwrite(&idx_num_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(freq_codes_, sizeof(LmaScoreType), kCodeBookSize, fp) != - kCodeBookSize) - return false; - - if (fwrite(lma_freq_idx_, sizeof(CODEBOOK_TYPE), idx_num_, fp) != idx_num_) - return false; - - return true; -} - -bool NGram::load_ngram(FILE *fp) { - if (NULL == fp) - return false; - - initialized_ = false; - - if (fread(&idx_num_, sizeof(size_t), 1, fp) != 1 ) - return false; - - if (NULL != lma_freq_idx_) - free(lma_freq_idx_); - - if (NULL != freq_codes_) - free(freq_codes_); - - lma_freq_idx_ = static_cast - (malloc(idx_num_ * sizeof(CODEBOOK_TYPE))); - freq_codes_ = static_cast - (malloc(kCodeBookSize * sizeof(LmaScoreType))); - - if (NULL == lma_freq_idx_ || NULL == freq_codes_) - return false; - - if (fread(freq_codes_, sizeof(LmaScoreType), kCodeBookSize, fp) != - kCodeBookSize) - return false; - - if (fread(lma_freq_idx_, sizeof(CODEBOOK_TYPE), idx_num_, fp) != idx_num_) - return false; - - initialized_ = true; - - total_freq_none_sys_ = 0; - return true; -} - -void NGram::set_total_freq_none_sys(size_t freq_none_sys) { - total_freq_none_sys_ = freq_none_sys; - if (0 == total_freq_none_sys_) { - sys_score_compensation_ = 0; - } else { - double factor = static_cast(total_freq_none_sys_) / ( - kSysDictTotalFreq + total_freq_none_sys_); - sys_score_compensation_ = static_cast( - log(factor) * kLogValueAmplifier); - } -} - -// The caller makes sure this oject is initialized. -float NGram::get_uni_psb(LemmaIdType lma_id) { - return static_cast(freq_codes_[lma_freq_idx_[lma_id]]) + - sys_score_compensation_; -} - -float NGram::convert_psb_to_score(double psb) { - float score = static_cast( - log(psb) * static_cast(kLogValueAmplifier)); - if (score > static_cast(kMaxScore)) { - score = static_cast(kMaxScore); - } - return score; -} - -#ifdef ___BUILD_MODEL___ -bool NGram::build_unigram(LemmaEntry *lemma_arr, size_t lemma_num, - LemmaIdType next_idx_unused) { - if (NULL == lemma_arr || 0 == lemma_num || next_idx_unused <= 1) - return false; - - double total_freq = 0; - double *freqs = new double[next_idx_unused]; - if (NULL == freqs) - return false; - - freqs[0] = ADD_COUNT; - total_freq += freqs[0]; - LemmaIdType idx_now = 0; - for (size_t pos = 0; pos < lemma_num; pos++) { - if (lemma_arr[pos].idx_by_hz == idx_now) - continue; - idx_now++; - - assert(lemma_arr[pos].idx_by_hz == idx_now); - - freqs[idx_now] = lemma_arr[pos].freq; - if (freqs[idx_now] <= 0) - freqs[idx_now] = 0.3; - - total_freq += freqs[idx_now]; - } - - double max_freq = 0; - idx_num_ = idx_now + 1; - assert(idx_now + 1 == next_idx_unused); - - for (size_t pos = 0; pos < idx_num_; pos++) { - freqs[pos] = freqs[pos] / total_freq; - assert(freqs[pos] > 0); - if (freqs[pos] > max_freq) - max_freq = freqs[pos]; - } - - // calculate the code book - if (NULL == freq_codes_df_) - freq_codes_df_ = new double[kCodeBookSize]; - assert(freq_codes_df_); - memset(freq_codes_df_, 0, sizeof(double) * kCodeBookSize); - - if (NULL == freq_codes_) - freq_codes_ = new LmaScoreType[kCodeBookSize]; - assert(freq_codes_); - memset(freq_codes_, 0, sizeof(LmaScoreType) * kCodeBookSize); - - size_t freq_pos = 0; - for (size_t code_pos = 0; code_pos < kCodeBookSize; code_pos++) { - bool found = true; - - while (found) { - found = false; - double cand = freqs[freq_pos]; - for (size_t i = 0; i < code_pos; i++) - if (freq_codes_df_[i] == cand) { - found = true; - break; - } - if (found) - freq_pos++; - } - - freq_codes_df_[code_pos] = freqs[freq_pos]; - freq_pos++; - } - - myqsort(freq_codes_df_, kCodeBookSize, sizeof(double), comp_double); - - if (NULL == lma_freq_idx_) - lma_freq_idx_ = new CODEBOOK_TYPE[idx_num_]; - assert(lma_freq_idx_); - - iterate_codes(freqs, idx_num_, freq_codes_df_, lma_freq_idx_); - - delete [] freqs; - - if (kPrintDebug0) { - printf("\n------Language Model Unigram Codebook------\n"); - } - - for (size_t code_pos = 0; code_pos < kCodeBookSize; code_pos++) { - double log_score = log(freq_codes_df_[code_pos]); - float final_score = convert_psb_to_score(freq_codes_df_[code_pos]); - if (kPrintDebug0) { - printf("code:%d, probability:%.9f, log score:%.3f, final score: %.3f\n", - code_pos, freq_codes_df_[code_pos], log_score, final_score); - } - freq_codes_[code_pos] = static_cast(final_score); - } - - initialized_ = true; - return true; -} -#endif - -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/pinyinime.cpp b/PinyinIME/jni/share/pinyinime.cpp deleted file mode 100644 index 550da7b..0000000 --- a/PinyinIME/jni/share/pinyinime.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "../include/pinyinime.h" -#include "../include/dicttrie.h" -#include "../include/matrixsearch.h" -#include "../include/spellingtrie.h" - -#ifdef __cplusplus -extern "C" { -#endif - - using namespace ime_pinyin; - - // The maximum number of the prediction items. - static const size_t kMaxPredictNum = 500; - - // Used to search Pinyin string and give the best candidate. - MatrixSearch* matrix_search = NULL; - - char16 predict_buf[kMaxPredictNum][kMaxPredictSize + 1]; - - bool im_open_decoder(const char *fn_sys_dict, const char *fn_usr_dict) { - if (NULL != matrix_search) - delete matrix_search; - - matrix_search = new MatrixSearch(); - if (NULL == matrix_search) { - return false; - } - - return matrix_search->init(fn_sys_dict, fn_usr_dict); - } - - bool im_open_decoder_fd(int sys_fd, long start_offset, long length, - const char *fn_usr_dict) { - if (NULL != matrix_search) - delete matrix_search; - - matrix_search = new MatrixSearch(); - if (NULL == matrix_search) - return false; - - return matrix_search->init_fd(sys_fd, start_offset, length, fn_usr_dict); - } - - void im_close_decoder() { - if (NULL != matrix_search) { - matrix_search->close(); - delete matrix_search; - } - matrix_search = NULL; - } - - void im_set_max_lens(size_t max_sps_len, size_t max_hzs_len) { - if (NULL != matrix_search) { - matrix_search->set_max_lens(max_sps_len, max_hzs_len); - } - } - - void im_flush_cache() { - if (NULL != matrix_search) - matrix_search->flush_cache(); - } - - // To be updated. - size_t im_search(const char* pybuf, size_t pylen) { - if (NULL == matrix_search) - return 0; - - matrix_search->search(pybuf, pylen); - return matrix_search->get_candidate_num(); - } - - size_t im_delsearch(size_t pos, bool is_pos_in_splid, - bool clear_fixed_this_step) { - if (NULL == matrix_search) - return 0; - matrix_search->delsearch(pos, is_pos_in_splid, clear_fixed_this_step); - return matrix_search->get_candidate_num(); - } - - void im_reset_search() { - if (NULL == matrix_search) - return; - - matrix_search->reset_search(); - } - - // To be removed - size_t im_add_letter(char ch) { - return 0; - } - - const char* im_get_sps_str(size_t *decoded_len) { - if (NULL == matrix_search) - return NULL; - - return matrix_search->get_pystr(decoded_len); - } - - char16* im_get_candidate(size_t cand_id, char16* cand_str, - size_t max_len) { - if (NULL == matrix_search) - return NULL; - - return matrix_search->get_candidate(cand_id, cand_str, max_len); - } - - size_t im_get_spl_start_pos(const uint16 *&spl_start) { - if (NULL == matrix_search) - return 0; - - return matrix_search->get_spl_start(spl_start); - } - - size_t im_choose(size_t choice_id) { - if (NULL == matrix_search) - return 0; - - return matrix_search->choose(choice_id); - } - - size_t im_cancel_last_choice() { - if (NULL == matrix_search) - return 0; - - return matrix_search->cancel_last_choice(); - } - - size_t im_get_fixed_len() { - if (NULL == matrix_search) - return 0; - - return matrix_search->get_fixedlen(); - } - - // To be removed - bool im_cancel_input() { - return true; - } - - - size_t im_get_predicts(const char16 *his_buf, - char16 (*&pre_buf)[kMaxPredictSize + 1]) { - if (NULL == his_buf) - return 0; - - size_t fixed_len = utf16_strlen(his_buf); - const char16 *fixed_ptr = his_buf; - if (fixed_len > kMaxPredictSize) { - fixed_ptr += fixed_len - kMaxPredictSize; - fixed_len = kMaxPredictSize; - } - - pre_buf = predict_buf; - return matrix_search->get_predicts(his_buf, pre_buf, kMaxPredictNum); - } - - void im_enable_shm_as_szm(bool enable) { - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - spl_trie.szm_enable_shm(enable); - } - - void im_enable_ym_as_szm(bool enable) { - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - spl_trie.szm_enable_ym(enable); - } - -#ifdef __cplusplus -} -#endif diff --git a/PinyinIME/jni/share/searchutility.cpp b/PinyinIME/jni/share/searchutility.cpp deleted file mode 100644 index 281da38..0000000 --- a/PinyinIME/jni/share/searchutility.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "../include/mystdlib.h" -#include "../include/searchutility.h" - -namespace ime_pinyin { - -bool is_system_lemma(LemmaIdType lma_id) { - return (0 < lma_id && lma_id <= kSysDictIdEnd); -} - -bool is_user_lemma(LemmaIdType lma_id) { - return (kUserDictIdStart <= lma_id && lma_id <= kUserDictIdEnd); -} - -bool is_composing_lemma(LemmaIdType lma_id) { - return (kLemmaIdComposing == lma_id); -} - -int cmp_lpi_with_psb(const void *p1, const void *p2) { - if ((static_cast(p1))->psb > - (static_cast(p2))->psb) - return 1; - if ((static_cast(p1))->psb < - (static_cast(p2))->psb) - return -1; - return 0; -} - -int cmp_lpi_with_unified_psb(const void *p1, const void *p2) { - const LmaPsbItem *item1 = static_cast(p1); - const LmaPsbItem *item2 = static_cast(p2); - - // The real unified psb is psb1 / lma_len1 and psb2 * lma_len2 - // But we use psb1 * lma_len2 and psb2 * lma_len1 to get better - // precision. - size_t up1 = item1->psb * (item2->lma_len); - size_t up2 = item2->psb * (item1->lma_len); - if (up1 < up2) { - return -1; - } - if (up1 > up2) { - return 1; - } - return 0; -} - -int cmp_lpi_with_id(const void *p1, const void *p2) { - if ((static_cast(p1))->id < - (static_cast(p2))->id) - return -1; - if ((static_cast(p1))->id > - (static_cast(p2))->id) - return 1; - return 0; -} - -int cmp_lpi_with_hanzi(const void *p1, const void *p2) { - if ((static_cast(p1))->hanzi < - (static_cast(p2))->hanzi) - return -1; - if ((static_cast(p1))->hanzi > - (static_cast(p2))->hanzi) - return 1; - - return 0; -} - -int cmp_lpsi_with_str(const void *p1, const void *p2) { - return utf16_strcmp((static_cast(p1))->str, - (static_cast(p2))->str); -} - - -int cmp_hanzis_1(const void *p1, const void *p2) { - if (*static_cast(p1) < - *static_cast(p2)) - return -1; - - if (*static_cast(p1) > - *static_cast(p2)) - return 1; - return 0; -} - -int cmp_hanzis_2(const void *p1, const void *p2) { - return utf16_strncmp(static_cast(p1), - static_cast(p2), 2); -} - -int cmp_hanzis_3(const void *p1, const void *p2) { - return utf16_strncmp(static_cast(p1), - static_cast(p2), 3); -} - -int cmp_hanzis_4(const void *p1, const void *p2) { - return utf16_strncmp(static_cast(p1), - static_cast(p2), 4); -} - -int cmp_hanzis_5(const void *p1, const void *p2) { - return utf16_strncmp(static_cast(p1), - static_cast(p2), 5); -} - -int cmp_hanzis_6(const void *p1, const void *p2) { - return utf16_strncmp(static_cast(p1), - static_cast(p2), 6); -} - -int cmp_hanzis_7(const void *p1, const void *p2) { - return utf16_strncmp(static_cast(p1), - static_cast(p2), 7); -} - -int cmp_hanzis_8(const void *p1, const void *p2) { - return utf16_strncmp(static_cast(p1), - static_cast(p2), 8); -} - -int cmp_npre_by_score(const void *p1, const void *p2) { - if ((static_cast(p1))->psb > - (static_cast(p2))->psb) - return 1; - - if ((static_cast(p1))->psb < - (static_cast(p2))->psb) - return -1; - - return 0; -} - -int cmp_npre_by_hislen_score(const void *p1, const void *p2) { - if ((static_cast(p1))->his_len < - (static_cast(p2))->his_len) - return 1; - - if ((static_cast(p1))->his_len > - (static_cast(p2))->his_len) - return -1; - - if ((static_cast(p1))->psb > - (static_cast(p2))->psb) - return 1; - - if ((static_cast(p1))->psb < - (static_cast(p2))->psb) - return -1; - - return 0; -} - -int cmp_npre_by_hanzi_score(const void *p1, const void *p2) { - int ret_v = (utf16_strncmp((static_cast(p1))->pre_hzs, - (static_cast(p2))->pre_hzs, kMaxPredictSize)); - if (0 != ret_v) - return ret_v; - - if ((static_cast(p1))->psb > - (static_cast(p2))->psb) - return 1; - - if ((static_cast(p1))->psb < - (static_cast(p2))->psb) - return -1; - - return 0; -} - -size_t remove_duplicate_npre(NPredictItem *npre_items, size_t npre_num) { - if (NULL == npre_items || 0 == npre_num) - return 0; - - myqsort(npre_items, npre_num, sizeof(NPredictItem), cmp_npre_by_hanzi_score); - - size_t remain_num = 1; // The first one is reserved. - for (size_t pos = 1; pos < npre_num; pos++) { - if (utf16_strncmp(npre_items[pos].pre_hzs, - npre_items[remain_num - 1].pre_hzs, - kMaxPredictSize) != 0) { - if (remain_num != pos) { - npre_items[remain_num] = npre_items[pos]; - } - remain_num++; - } - } - return remain_num; -} - -size_t align_to_size_t(size_t size) { - size_t s = sizeof(size_t); - return (size + s -1) / s * s; -} - -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/spellingtable.cpp b/PinyinIME/jni/share/spellingtable.cpp deleted file mode 100644 index 6005e20..0000000 --- a/PinyinIME/jni/share/spellingtable.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include "../include/spellingtable.h" - -namespace ime_pinyin { - -#ifdef ___BUILD_MODEL___ - -const char SpellingTable:: - kNotSupportList[kNotSupportNum][kMaxSpellingSize + 1] = {"HM", "HNG", "NG"}; - -// "" is the biggest, so that all empty strings will be moved to the end -// _eb mean empty is biggest -int compare_raw_spl_eb(const void* p1, const void* p2) { - if ('\0' == (static_cast(p1))->str[0]) - return 1; - - if ('\0' == (static_cast(p2))->str[0]) - return -1; - - return strcmp((static_cast(p1))->str, - (static_cast(p2))->str); -} - -size_t get_odd_next(size_t value) { - size_t v_next = value; - while (true) { - size_t v_next_sqrt = (size_t)sqrt(v_next); - - bool is_odd = true; - for (size_t v_dv = 2; v_dv < v_next_sqrt + 1; v_dv++) { - if (v_next % v_dv == 0) { - is_odd = false; - break; - } - } - - if (is_odd) - return v_next; - - v_next++; - } - - // never reach here - return 0; -} - -SpellingTable::SpellingTable() { - need_score_ = false; - raw_spellings_ = NULL; - spelling_buf_ = NULL; - spelling_num_ = 0; - total_freq_ = 0; - frozen_ = true; -} - -SpellingTable::~SpellingTable() { - free_resource(); -} - -size_t SpellingTable::get_hash_pos(const char* spelling_str) { - size_t hash_pos = 0; - for (size_t pos = 0; pos < spelling_size_; pos++) { - if ('\0' == spelling_str[pos]) - break; - hash_pos += (size_t)spelling_str[pos]; - } - - hash_pos = hash_pos % spelling_max_num_; - return hash_pos; -} - -size_t SpellingTable::hash_pos_next(size_t hash_pos) { - hash_pos += 123; - hash_pos = hash_pos % spelling_max_num_; - return hash_pos; -} - -void SpellingTable::free_resource() { - if (NULL != raw_spellings_) - delete [] raw_spellings_; - raw_spellings_ = NULL; - - if (NULL != spelling_buf_) - delete [] spelling_buf_; - spelling_buf_ = NULL; -} - -bool SpellingTable::init_table(size_t pure_spl_size, size_t spl_max_num, - bool need_score) { - if (pure_spl_size == 0 || spl_max_num ==0) - return false; - - need_score_ = need_score; - - free_resource(); - - spelling_size_ = pure_spl_size + 1; - if (need_score) - spelling_size_ += 1; - spelling_max_num_ = get_odd_next(spl_max_num); - spelling_num_ = 0; - - raw_spellings_ = new RawSpelling[spelling_max_num_]; - spelling_buf_ = new char[spelling_max_num_ * (spelling_size_)]; - if (NULL == raw_spellings_ || NULL == spelling_buf_) { - free_resource(); - return false; - } - - memset(raw_spellings_, 0, spelling_max_num_ * sizeof(RawSpelling)); - memset(spelling_buf_, 0, spelling_max_num_ * (spelling_size_)); - frozen_ = false; - total_freq_ = 0; - return true; -} - -bool SpellingTable::put_spelling(const char* spelling_str, double freq) { - if (frozen_ || NULL == spelling_str) - return false; - - for (size_t pos = 0; pos < kNotSupportNum; pos++) { - if (strcmp(spelling_str, kNotSupportList[pos]) == 0) { - return false; - } - } - - total_freq_ += freq; - - size_t hash_pos = get_hash_pos(spelling_str); - - raw_spellings_[hash_pos].str[spelling_size_ - 1] = '\0'; - - if (strncmp(raw_spellings_[hash_pos].str, spelling_str, - spelling_size_ - 1) == 0) { - raw_spellings_[hash_pos].freq += freq; - return true; - } - - size_t hash_pos_ori = hash_pos; - - while (true) { - if (strncmp(raw_spellings_[hash_pos].str, - spelling_str, spelling_size_ - 1) == 0) { - raw_spellings_[hash_pos].freq += freq; - return true; - } - - if ('\0' == raw_spellings_[hash_pos].str[0]) { - raw_spellings_[hash_pos].freq += freq; - strncpy(raw_spellings_[hash_pos].str, spelling_str, spelling_size_ - 1); - raw_spellings_[hash_pos].str[spelling_size_ - 1] = '\0'; - spelling_num_++; - return true; - } - - hash_pos = hash_pos_next(hash_pos); - if (hash_pos_ori == hash_pos) - return false; - } - - // never reach here - return false; -} - -bool SpellingTable::contain(const char* spelling_str) { - if (NULL == spelling_str || NULL == spelling_buf_ || frozen_) - return false; - - size_t hash_pos = get_hash_pos(spelling_str); - - if ('\0' == raw_spellings_[hash_pos].str[0]) - return false; - - if (strncmp(raw_spellings_[hash_pos].str, spelling_str, spelling_size_ - 1) - == 0) - return true; - - size_t hash_pos_ori = hash_pos; - - while (true) { - hash_pos = hash_pos_next(hash_pos); - if (hash_pos_ori == hash_pos) - return false; - - if ('\0' == raw_spellings_[hash_pos].str[0]) - return false; - - if (strncmp(raw_spellings_[hash_pos].str, spelling_str, spelling_size_ - 1) - == 0) - return true; - } - - // never reach here - return false; -} - -const char* SpellingTable::arrange(size_t *item_size, size_t *spl_num) { - if (NULL == raw_spellings_ || NULL == spelling_buf_ || - NULL == item_size || NULL == spl_num) - return NULL; - - qsort(raw_spellings_, spelling_max_num_, sizeof(RawSpelling), - compare_raw_spl_eb); - - // After sorting, only the first spelling_num_ items are valid. - // Copy them to the destination buffer. - for (size_t pos = 0; pos < spelling_num_; pos++) { - strncpy(spelling_buf_ + pos * spelling_size_, raw_spellings_[pos].str, - spelling_size_); - } - - if (need_score_) { - if (kPrintDebug0) - printf("------------Spelling Possiblities--------------\n"); - - double max_score = 0; - double min_score = 0; - - // After sorting, only the first spelling_num_ items are valid. - for (size_t pos = 0; pos < spelling_num_; pos++) { - raw_spellings_[pos].freq /= total_freq_; - if (need_score_) { - if (0 == pos) { - max_score = raw_spellings_[0].freq; - min_score = max_score; - } else { - if (raw_spellings_[pos].freq > max_score) - max_score = raw_spellings_[pos].freq; - if (raw_spellings_[pos].freq < min_score) - min_score = raw_spellings_[pos].freq; - } - } - } - - if (kPrintDebug0) - printf("-----max psb: %f, min psb: %f\n", max_score, min_score); - - max_score = log(max_score); - min_score = log(min_score); - - if (kPrintDebug0) - printf("-----max log value: %f, min log value: %f\n", - max_score, min_score); - - // The absolute value of min_score is bigger than that of max_score because - // both of them are negative after log function. - score_amplifier_ = 1.0 * 255 / min_score; - - double average_score = 0; - for (size_t pos = 0; pos < spelling_num_; pos++) { - double score = log(raw_spellings_[pos].freq) * score_amplifier_; - assert(score >= 0); - - average_score += score; - - // Because of calculation precision issue, score might be a little bigger - // than 255 after being amplified. - if (score > 255) - score = 255; - char *this_spl_buf = spelling_buf_ + pos * spelling_size_; - this_spl_buf[spelling_size_ - 1] = - static_cast((unsigned char)score); - - if (kPrintDebug0) { - printf("---pos:%d, %s, psb:%d\n", pos, this_spl_buf, - (unsigned char)this_spl_buf[spelling_size_ -1]); - } - } - average_score /= spelling_num_; - assert(average_score <= 255); - average_score_ = static_cast(average_score); - - if (kPrintDebug0) - printf("\n----Score Amplifier: %f, Average Score: %d\n", score_amplifier_, - average_score_); - } - - *item_size = spelling_size_; - *spl_num = spelling_num_; - frozen_ = true; - return spelling_buf_; -} - -float SpellingTable::get_score_amplifier() { - return static_cast(score_amplifier_); -} - -unsigned char SpellingTable::get_average_score() { - return average_score_; -} - -#endif // ___BUILD_MODEL___ -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/spellingtrie.cpp b/PinyinIME/jni/share/spellingtrie.cpp deleted file mode 100644 index 85be46b..0000000 --- a/PinyinIME/jni/share/spellingtrie.cpp +++ /dev/null @@ -1,828 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "../include/dictdef.h" - -#ifdef ___BUILD_MODEL___ -#include "../include/spellingtable.h" -#endif - -#include "../include/spellingtrie.h" - -namespace ime_pinyin { - -SpellingTrie* SpellingTrie::instance_ = NULL; - -// z/c/s is for Zh/Ch/Sh -const char SpellingTrie::kHalfId2Sc_[kFullSplIdStart + 1] = - "0ABCcDEFGHIJKLMNOPQRSsTUVWXYZz"; - -// Bit 0 : is it a Shengmu char? -// Bit 1 : is it a Yunmu char? (one char is a Yunmu) -// Bit 2 : is it enabled in ShouZiMu(first char) mode? -unsigned char SpellingTrie::char_flags_[] = { - // a b c d e f g - 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, - // h i j k l m n - 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - // o p q r s t - 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, - // u v w x y z - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01 -}; - -int compare_spl(const void* p1, const void* p2) { - return strcmp((const char*)(p1), (const char*)(p2)); -} - -SpellingTrie::SpellingTrie() { - spelling_buf_ = NULL; - spelling_size_ = 0; - spelling_num_ = 0; - spl_ym_ids_ = NULL; - splstr_queried_ = NULL; - splstr16_queried_ = NULL; - root_ = NULL; - dumb_node_ = NULL; - splitter_node_ = NULL; - instance_ = NULL; - ym_buf_ = NULL; - f2h_ = NULL; - - szm_enable_shm(true); - szm_enable_ym(true); - -#ifdef ___BUILD_MODEL___ - node_num_ = 0; -#endif -} - -SpellingTrie::~SpellingTrie() { - if (NULL != spelling_buf_) - delete [] spelling_buf_; - - if (NULL != splstr_queried_) - delete [] splstr_queried_; - - if (NULL != splstr16_queried_) - delete [] splstr16_queried_; - - if (NULL != spl_ym_ids_) - delete [] spl_ym_ids_; - - if (NULL != root_) { - free_son_trie(root_); - delete root_; - } - - if (NULL != dumb_node_) { - delete [] dumb_node_; - } - - if (NULL != splitter_node_) { - delete [] splitter_node_; - } - - if (NULL != instance_) { - delete instance_; - instance_ = NULL; - } - - if (NULL != ym_buf_) - delete [] ym_buf_; - - if (NULL != f2h_) - delete [] f2h_; -} - -bool SpellingTrie::if_valid_id_update(uint16 *splid) const { - if (NULL == splid || 0 == *splid) - return false; - - if (*splid >= kFullSplIdStart) - return true; - if (*splid < kFullSplIdStart) { - char ch = kHalfId2Sc_[*splid]; - if (ch > 'Z') { - return true; - } else { - if (szm_is_enabled(ch)) { - return true; - } else if (is_yunmu_char(ch)) { - assert(h2f_num_[*splid] > 0); - *splid = h2f_start_[*splid]; - return true; - } - } - } - return false; -} - -bool SpellingTrie::is_half_id(uint16 splid) const { - if (0 == splid || splid >= kFullSplIdStart) - return false; - - return true; -} - -bool SpellingTrie::is_full_id(uint16 splid) const { - if (splid < kFullSplIdStart || splid >= kFullSplIdStart + spelling_num_) - return false; - return true; -} - -bool SpellingTrie::half_full_compatible(uint16 half_id, uint16 full_id) const { - uint16 half_fr_full = full_to_half(full_id); - - if (half_fr_full == half_id) - return true; - - // &~0x20 is used to conver the char to upper case. - // So that Zh/Ch/Sh(whose char is z/c/s) can be matched with Z/C/S. - char ch_f = (kHalfId2Sc_[half_fr_full] & (~0x20)); - char ch_h = kHalfId2Sc_[half_id]; - if (ch_f == ch_h) - return true; - - return false; -} - -bool SpellingTrie::is_half_id_yunmu(uint16 splid) const { - if (0 == splid || splid >= kFullSplIdStart) - return false; - - char ch = kHalfId2Sc_[splid]; - // If ch >= 'a', that means the half id is one of Zh/Ch/Sh - if (ch >= 'a') { - return false; - } - - return char_flags_[ch - 'A'] & kHalfIdYunmuMask; -} - -bool SpellingTrie::is_shengmu_char(char ch) const { - return char_flags_[ch - 'A'] & kHalfIdShengmuMask; -} - -bool SpellingTrie::is_yunmu_char(char ch) const { - return char_flags_[ch - 'A'] & kHalfIdYunmuMask; -} - -bool SpellingTrie::is_szm_char(char ch) const { - return is_shengmu_char(ch) || is_yunmu_char(ch); -} - -bool SpellingTrie::szm_is_enabled(char ch) const { - return char_flags_[ch - 'A'] & kHalfIdSzmMask; -} - -void SpellingTrie::szm_enable_shm(bool enable) { - if (enable) { - for (char ch = 'A'; ch <= 'Z'; ch++) { - if (is_shengmu_char(ch)) - char_flags_[ch - 'A'] = char_flags_[ch - 'A'] | kHalfIdSzmMask; - } - } else { - for (char ch = 'A'; ch <= 'Z'; ch++) { - if (is_shengmu_char(ch)) - char_flags_[ch - 'A'] = char_flags_[ch - 'A'] & (kHalfIdSzmMask ^ 0xff); - } - } -} - -void SpellingTrie::szm_enable_ym(bool enable) { - if (enable) { - for (char ch = 'A'; ch <= 'Z'; ch++) { - if (is_yunmu_char(ch)) - char_flags_[ch - 'A'] = char_flags_[ch - 'A'] | kHalfIdSzmMask; - } - } else { - for (char ch = 'A'; ch <= 'Z'; ch++) { - if (is_yunmu_char(ch)) - char_flags_[ch - 'A'] = char_flags_[ch - 'A'] & (kHalfIdSzmMask ^ 0xff); - } - } -} - -bool SpellingTrie::is_szm_enabled(char ch) const { - return char_flags_[ch - 'A'] & kHalfIdSzmMask; -} - -const SpellingTrie* SpellingTrie::get_cpinstance() { - return &get_instance(); -} - -SpellingTrie& SpellingTrie::get_instance() { - if (NULL == instance_) - instance_ = new SpellingTrie(); - - return *instance_; -} - -uint16 SpellingTrie::half2full_num(uint16 half_id) const { - if (NULL == root_ || half_id >= kFullSplIdStart) - return 0; - return h2f_num_[half_id]; -} - -uint16 SpellingTrie::half_to_full(uint16 half_id, uint16 *spl_id_start) const { - if (NULL == spl_id_start || NULL == root_ || half_id >= kFullSplIdStart) - return 0; - - *spl_id_start = h2f_start_[half_id]; - return h2f_num_[half_id]; -} - -uint16 SpellingTrie::full_to_half(uint16 full_id) const { - if (NULL == root_ || full_id < kFullSplIdStart || - full_id > spelling_num_ + kFullSplIdStart) - return 0; - - return f2h_[full_id - kFullSplIdStart]; -} - -void SpellingTrie::free_son_trie(SpellingNode* node) { - if (NULL == node) - return; - - for (size_t pos = 0; pos < node->num_of_son; pos++) { - free_son_trie(node->first_son + pos); - } - - if (NULL != node->first_son) - delete [] node->first_son; -} - -bool SpellingTrie::construct(const char* spelling_arr, size_t item_size, - size_t item_num, float score_amplifier, - unsigned char average_score) { - if (spelling_arr == NULL) - return false; - - memset(h2f_start_, 0, sizeof(uint16) * kFullSplIdStart); - memset(h2f_num_, 0, sizeof(uint16) * kFullSplIdStart); - - // If the arr is the same as the buf, means this function is called by - // load_table(), the table data are ready; otherwise the array should be - // saved. - if (spelling_arr != spelling_buf_) { - if (NULL != spelling_buf_) - delete [] spelling_buf_; - spelling_buf_ = new char[item_size * item_num]; - if (NULL == spelling_buf_) - return false; - memcpy(spelling_buf_, spelling_arr, sizeof(char) * item_size * item_num); - } - - spelling_size_ = item_size; - spelling_num_ = item_num; - - score_amplifier_ = score_amplifier; - average_score_ = average_score; - - if (NULL != splstr_queried_) - delete [] splstr_queried_; - splstr_queried_ = new char[spelling_size_]; - if (NULL == splstr_queried_) - return false; - - if (NULL != splstr16_queried_) - delete [] splstr16_queried_; - splstr16_queried_ = new char16[spelling_size_]; - if (NULL == splstr16_queried_) - return false; - - // First, sort the buf to ensure they are in ascendant order - qsort(spelling_buf_, spelling_num_, spelling_size_, compare_spl); - -#ifdef ___BUILD_MODEL___ - node_num_ = 1; -#endif - - root_ = new SpellingNode(); - memset(root_, 0, sizeof(SpellingNode)); - - dumb_node_ = new SpellingNode(); - memset(dumb_node_, 0, sizeof(SpellingNode)); - dumb_node_->score = average_score_; - - splitter_node_ = new SpellingNode(); - memset(splitter_node_, 0, sizeof(SpellingNode)); - splitter_node_->score = average_score_; - - memset(level1_sons_, 0, sizeof(SpellingNode*) * kValidSplCharNum); - - root_->first_son = construct_spellings_subset(0, spelling_num_, 0, root_); - - // Root's score should be cleared. - root_->score = 0; - - if (NULL == root_->first_son) - return false; - - h2f_start_[0] = h2f_num_[0] = 0; - - if (!build_f2h()) - return false; - -#ifdef ___BUILD_MODEL___ - if (kPrintDebug0) { - printf("---SpellingTrie Nodes: %d\n", node_num_); - } - return build_ym_info(); -#else - return true; -#endif -} - -#ifdef ___BUILD_MODEL___ -const char* SpellingTrie::get_ym_str(const char *spl_str) { - bool start_ZCS = false; - if (is_shengmu_char(*spl_str)) { - if ('Z' == *spl_str || 'C' == *spl_str || 'S' == *spl_str) - start_ZCS = true; - spl_str += 1; - if (start_ZCS && 'h' == *spl_str) - spl_str += 1; - } - return spl_str; -} - -bool SpellingTrie::build_ym_info() { - bool sucess; - SpellingTable *spl_table = new SpellingTable(); - - sucess = spl_table->init_table(kMaxPinyinSize - 1, 2 * kMaxYmNum, false); - assert(sucess); - - for (uint16 pos = 0; pos < spelling_num_; pos++) { - const char *spl_str = spelling_buf_ + spelling_size_ * pos; - spl_str = get_ym_str(spl_str); - if ('\0' != spl_str[0]) { - sucess = spl_table->put_spelling(spl_str, 0); - assert(sucess); - } - } - - size_t ym_item_size; // '\0' is included - size_t ym_num; - const char* ym_buf; - ym_buf = spl_table->arrange(&ym_item_size, &ym_num); - - if (NULL != ym_buf_) - delete [] ym_buf_; - ym_buf_ = new char[ym_item_size * ym_num]; - if (NULL == ym_buf_) { - delete spl_table; - return false; - } - - memcpy(ym_buf_, ym_buf, sizeof(char) * ym_item_size * ym_num); - ym_size_ = ym_item_size; - ym_num_ = ym_num; - - delete spl_table; - - // Generate the maping from the spelling ids to the Yunmu ids. - if (spl_ym_ids_) - delete spl_ym_ids_; - spl_ym_ids_ = new uint8[spelling_num_ + kFullSplIdStart]; - if (NULL == spl_ym_ids_) - return false; - - memset(spl_ym_ids_, 0, sizeof(uint8) * (spelling_num_ + kFullSplIdStart)); - - for (uint16 id = 1; id < spelling_num_ + kFullSplIdStart; id++) { - const char *str = get_spelling_str(id); - - str = get_ym_str(str); - if ('\0' != str[0]) { - uint8 ym_id = get_ym_id(str); - spl_ym_ids_[id] = ym_id; - assert(ym_id > 0); - } else { - spl_ym_ids_[id] = 0; - } - } - return true; -} -#endif - -SpellingNode* SpellingTrie::construct_spellings_subset( - size_t item_start, size_t item_end, size_t level, SpellingNode* parent) { - if (level >= spelling_size_ || item_end <= item_start || NULL == parent) - return NULL; - - SpellingNode *first_son = NULL; - uint16 num_of_son = 0; - unsigned char min_son_score = 255; - - const char *spelling_last_start = spelling_buf_ + spelling_size_ * item_start; - char char_for_node = spelling_last_start[level]; - assert(char_for_node >= 'A' && char_for_node <= 'Z' || - 'h' == char_for_node); - - // Scan the array to find how many sons - for (size_t i = item_start + 1; i < item_end; i++) { - const char *spelling_current = spelling_buf_ + spelling_size_ * i; - char char_current = spelling_current[level]; - if (char_current != char_for_node) { - num_of_son++; - char_for_node = char_current; - } - } - num_of_son++; - - // Allocate memory -#ifdef ___BUILD_MODEL___ - node_num_ += num_of_son; -#endif - first_son = new SpellingNode[num_of_son]; - memset(first_son, 0, sizeof(SpellingNode)*num_of_son); - - // Now begin construct tree - size_t son_pos = 0; - - spelling_last_start = spelling_buf_ + spelling_size_ * item_start; - char_for_node = spelling_last_start[level]; - - bool spelling_endable = true; - if (spelling_last_start[level + 1] != '\0') - spelling_endable = false; - - size_t item_start_next = item_start; - - for (size_t i = item_start + 1; i < item_end; i++) { - const char *spelling_current = spelling_buf_ + spelling_size_ * i; - char char_current = spelling_current[level]; - assert(is_valid_spl_char(char_current)); - - if (char_current != char_for_node) { - // Construct a node - SpellingNode *node_current = first_son + son_pos; - node_current->char_this_node = char_for_node; - - // For quick search in the first level - if (0 == level) - level1_sons_[char_for_node - 'A'] = node_current; - - if (spelling_endable) { - node_current->spelling_idx = kFullSplIdStart + item_start_next; - } - - if (spelling_last_start[level + 1] != '\0' || i - item_start_next > 1) { - size_t real_start = item_start_next; - if (spelling_last_start[level + 1] == '\0') - real_start++; - - node_current->first_son = - construct_spellings_subset(real_start, i, level + 1, - node_current); - - if (real_start == item_start_next + 1) { - uint16 score_this = static_cast( - spelling_last_start[spelling_size_ - 1]); - if (score_this < node_current->score) - node_current->score = score_this; - } - } else { - node_current->first_son = NULL; - node_current->score = static_cast( - spelling_last_start[spelling_size_ - 1]); - } - - if (node_current->score < min_son_score) - min_son_score = node_current->score; - - bool is_half = false; - if (level == 0 && is_szm_char(char_for_node)) { - node_current->spelling_idx = - static_cast(char_for_node - 'A' + 1); - - if (char_for_node > 'C') - node_current->spelling_idx++; - if (char_for_node > 'S') - node_current->spelling_idx++; - - h2f_num_[node_current->spelling_idx] = i - item_start_next; - is_half = true; - } else if (level == 1 && char_for_node == 'h') { - char ch_level0 = spelling_last_start[0]; - uint16 part_id = 0; - if (ch_level0 == 'C') - part_id = 'C' - 'A' + 1 + 1; - else if (ch_level0 == 'S') - part_id = 'S' - 'A' + 1 + 2; - else if (ch_level0 == 'Z') - part_id = 'Z' - 'A' + 1 + 3; - if (0 != part_id) { - node_current->spelling_idx = part_id; - h2f_num_[node_current->spelling_idx] = i - item_start_next; - is_half = true; - } - } - - if (is_half) { - if (h2f_num_[node_current->spelling_idx] > 0) - h2f_start_[node_current->spelling_idx] = - item_start_next + kFullSplIdStart; - else - h2f_start_[node_current->spelling_idx] = 0; - } - - // for next sibling - spelling_last_start = spelling_current; - char_for_node = char_current; - item_start_next = i; - spelling_endable = true; - if (spelling_current[level + 1] != '\0') - spelling_endable = false; - - son_pos++; - } - } - - // the last one - SpellingNode *node_current = first_son + son_pos; - node_current->char_this_node = char_for_node; - - // For quick search in the first level - if (0 == level) - level1_sons_[char_for_node - 'A'] = node_current; - - if (spelling_endable) { - node_current->spelling_idx = kFullSplIdStart + item_start_next; - } - - if (spelling_last_start[level + 1] != '\0' || - item_end - item_start_next > 1) { - size_t real_start = item_start_next; - if (spelling_last_start[level + 1] == '\0') - real_start++; - - node_current->first_son = - construct_spellings_subset(real_start, item_end, level + 1, - node_current); - - if (real_start == item_start_next + 1) { - uint16 score_this = static_cast( - spelling_last_start[spelling_size_ - 1]); - if (score_this < node_current->score) - node_current->score = score_this; - } - } else { - node_current->first_son = NULL; - node_current->score = static_cast( - spelling_last_start[spelling_size_ - 1]); - } - - if (node_current->score < min_son_score) - min_son_score = node_current->score; - - assert(son_pos + 1 == num_of_son); - - bool is_half = false; - if (level == 0 && szm_is_enabled(char_for_node)) { - node_current->spelling_idx = static_cast(char_for_node - 'A' + 1); - - if (char_for_node > 'C') - node_current->spelling_idx++; - if (char_for_node > 'S') - node_current->spelling_idx++; - - h2f_num_[node_current->spelling_idx] = item_end - item_start_next; - is_half = true; - } else if (level == 1 && char_for_node == 'h') { - char ch_level0 = spelling_last_start[0]; - uint16 part_id = 0; - if (ch_level0 == 'C') - part_id = 'C' - 'A' + 1 + 1; - else if (ch_level0 == 'S') - part_id = 'S' - 'A' + 1 + 2; - else if (ch_level0 == 'Z') - part_id = 'Z' - 'A' + 1 + 3; - if (0 != part_id) { - node_current->spelling_idx = part_id; - h2f_num_[node_current->spelling_idx] = item_end - item_start_next; - is_half = true; - } - } - if (is_half) { - if (h2f_num_[node_current->spelling_idx] > 0) - h2f_start_[node_current->spelling_idx] = - item_start_next + kFullSplIdStart; - else - h2f_start_[node_current->spelling_idx] = 0; - } - - parent->num_of_son = num_of_son; - parent->score = min_son_score; - return first_son; -} - -bool SpellingTrie::save_spl_trie(FILE *fp) { - if (NULL == fp || NULL == spelling_buf_) - return false; - - if (fwrite(&spelling_size_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(&spelling_num_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fwrite(&score_amplifier_, sizeof(float), 1, fp) != 1) - return false; - - if (fwrite(&average_score_, sizeof(unsigned char), 1, fp) != 1) - return false; - - if (fwrite(spelling_buf_, sizeof(char) * spelling_size_, - spelling_num_, fp) != spelling_num_) - return false; - - return true; -} - -bool SpellingTrie::load_spl_trie(FILE *fp) { - if (NULL == fp) - return false; - - if (fread(&spelling_size_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fread(&spelling_num_, sizeof(size_t), 1, fp) != 1) - return false; - - if (fread(&score_amplifier_, sizeof(float), 1, fp) != 1) - return false; - - if (fread(&average_score_, sizeof(unsigned char), 1, fp) != 1) - return false; - - if (NULL != spelling_buf_) - delete [] spelling_buf_; - - spelling_buf_ = new char[spelling_size_ * spelling_num_]; - if (NULL == spelling_buf_) - return false; - - if (fread(spelling_buf_, sizeof(char) * spelling_size_, - spelling_num_, fp) != spelling_num_) - return false; - - return construct(spelling_buf_, spelling_size_, spelling_num_, - score_amplifier_, average_score_); -} - -bool SpellingTrie::build_f2h() { - if (NULL != f2h_) - delete [] f2h_; - f2h_ = new uint16[spelling_num_]; - if (NULL == f2h_) - return false; - - for (uint16 hid = 0; hid < kFullSplIdStart; hid++) { - for (uint16 fid = h2f_start_[hid]; - fid < h2f_start_[hid] + h2f_num_[hid]; fid++) - f2h_[fid - kFullSplIdStart] = hid; - } - - return true; -} - -size_t SpellingTrie::get_spelling_num() { - return spelling_num_; -} - -uint8 SpellingTrie::get_ym_id(const char *ym_str) { - if (NULL == ym_str || NULL == ym_buf_) - return 0; - - for (uint8 pos = 0; pos < ym_num_; pos++) - if (strcmp(ym_buf_ + ym_size_ * pos, ym_str) == 0) - return pos + 1; - - return 0; -} - -const char* SpellingTrie::get_spelling_str(uint16 splid) { - splstr_queried_[0] = '\0'; - - if (splid >= kFullSplIdStart) { - splid -= kFullSplIdStart; - snprintf(splstr_queried_, spelling_size_, "%s", - spelling_buf_ + splid * spelling_size_); - } else { - if (splid == 'C' - 'A' + 1 + 1) { - snprintf(splstr_queried_, spelling_size_, "%s", "Ch"); - } else if (splid == 'S' - 'A' + 1 + 2) { - snprintf(splstr_queried_, spelling_size_, "%s", "Sh"); - } else if (splid == 'Z' - 'A' + 1 + 3) { - snprintf(splstr_queried_, spelling_size_, "%s", "Zh"); - } else { - if (splid > 'C' - 'A' + 1) - splid--; - if (splid > 'S' - 'A' + 1) - splid--; - splstr_queried_[0] = 'A' + splid - 1; - splstr_queried_[1] = '\0'; - } - } - return splstr_queried_; -} - -const char16* SpellingTrie::get_spelling_str16(uint16 splid) { - splstr16_queried_[0] = '\0'; - - if (splid >= kFullSplIdStart) { - splid -= kFullSplIdStart; - for (size_t pos = 0; pos < spelling_size_; pos++) { - splstr16_queried_[pos] = static_cast - (spelling_buf_[splid * spelling_size_ + pos]); - } - } else { - if (splid == 'C' - 'A' + 1 + 1) { - splstr16_queried_[0] = static_cast('C'); - splstr16_queried_[1] = static_cast('h'); - splstr16_queried_[2] = static_cast('\0'); - } else if (splid == 'S' - 'A' + 1 + 2) { - splstr16_queried_[0] = static_cast('S'); - splstr16_queried_[1] = static_cast('h'); - splstr16_queried_[2] = static_cast('\0'); - } else if (splid == 'Z' - 'A' + 1 + 3) { - splstr16_queried_[0] = static_cast('Z'); - splstr16_queried_[1] = static_cast('h'); - splstr16_queried_[2] = static_cast('\0'); - } else { - if (splid > 'C' - 'A' + 1) - splid--; - if (splid > 'S' - 'A' + 1) - splid--; - splstr16_queried_[0] = 'A' + splid - 1; - splstr16_queried_[1] = '\0'; - } - } - return splstr16_queried_; -} - -size_t SpellingTrie::get_spelling_str16(uint16 splid, char16 *splstr16, - size_t splstr16_len) { - if (NULL == splstr16 || splstr16_len < kMaxPinyinSize + 1) return 0; - - if (splid >= kFullSplIdStart) { - splid -= kFullSplIdStart; - for (size_t pos = 0; pos <= kMaxPinyinSize; pos++) { - splstr16[pos] = static_cast - (spelling_buf_[splid * spelling_size_ + pos]); - if (static_cast('\0') == splstr16[pos]) { - return pos; - } - } - } else { - if (splid == 'C' - 'A' + 1 + 1) { - splstr16[0] = static_cast('C'); - splstr16[1] = static_cast('h'); - splstr16[2] = static_cast('\0'); - return 2; - } else if (splid == 'S' - 'A' + 1 + 2) { - splstr16[0] = static_cast('S'); - splstr16[1] = static_cast('h'); - splstr16[2] = static_cast('\0'); - return 2; - } else if (splid == 'Z' - 'A' + 1 + 3) { - splstr16[0] = static_cast('Z'); - splstr16[1] = static_cast('h'); - splstr16[2] = static_cast('\0'); - return 2; - } else { - if (splid > 'C' - 'A' + 1) - splid--; - if (splid > 'S' - 'A' + 1) - splid--; - splstr16[0] = 'A' + splid - 1; - splstr16[1] = '\0'; - return 1; - } - } - - // Not reachable. - return 0; -} - -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/splparser.cpp b/PinyinIME/jni/share/splparser.cpp deleted file mode 100644 index d75aec6..0000000 --- a/PinyinIME/jni/share/splparser.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "../include/splparser.h" - -namespace ime_pinyin { - -SpellingParser::SpellingParser() { - spl_trie_ = SpellingTrie::get_cpinstance(); -} - -bool SpellingParser::is_valid_to_parse(char ch) { - return SpellingTrie::is_valid_spl_char(ch); -} - -uint16 SpellingParser::splstr_to_idxs(const char *splstr, uint16 str_len, - uint16 spl_idx[], uint16 start_pos[], - uint16 max_size, bool &last_is_pre) { - if (NULL == splstr || 0 == max_size || 0 == str_len) - return 0; - - if (!SpellingTrie::is_valid_spl_char(splstr[0])) - return 0; - - last_is_pre = false; - - const SpellingNode *node_this = spl_trie_->root_; - - uint16 str_pos = 0; - uint16 idx_num = 0; - if (NULL != start_pos) - start_pos[0] = 0; - bool last_is_splitter = false; - - while (str_pos < str_len) { - char char_this = splstr[str_pos]; - // all characters outside of [a, z] are considered as splitters - if (!SpellingTrie::is_valid_spl_char(char_this)) { - // test if the current node is endable - uint16 id_this = node_this->spelling_idx; - if (spl_trie_->if_valid_id_update(&id_this)) { - spl_idx[idx_num] = id_this; - - idx_num++; - str_pos++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - if (idx_num >= max_size) - return idx_num; - - node_this = spl_trie_->root_; - last_is_splitter = true; - continue; - } else { - if (last_is_splitter) { - str_pos++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - continue; - } else { - return idx_num; - } - } - } - - last_is_splitter = false; - - SpellingNode *found_son = NULL; - - if (0 == str_pos) { - if (char_this >= 'a') - found_son = spl_trie_->level1_sons_[char_this - 'a']; - else - found_son = spl_trie_->level1_sons_[char_this - 'A']; - } else { - SpellingNode *first_son = node_this->first_son; - // Because for Zh/Ch/Sh nodes, they are the last in the buffer and - // frequently used, so we scan from the end. - for (int i = 0; i < node_this->num_of_son; i++) { - SpellingNode *this_son = first_son + i; - if (SpellingTrie::is_same_spl_char( - this_son->char_this_node, char_this)) { - found_son = this_son; - break; - } - } - } - - // found, just move the current node pointer to the the son - if (NULL != found_son) { - node_this = found_son; - } else { - // not found, test if it is endable - uint16 id_this = node_this->spelling_idx; - if (spl_trie_->if_valid_id_update(&id_this)) { - // endable, remember the index - spl_idx[idx_num] = id_this; - - idx_num++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - if (idx_num >= max_size) - return idx_num; - node_this = spl_trie_->root_; - continue; - } else { - return idx_num; - } - } - - str_pos++; - } - - uint16 id_this = node_this->spelling_idx; - if (spl_trie_->if_valid_id_update(&id_this)) { - // endable, remember the index - spl_idx[idx_num] = id_this; - - idx_num++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - } - - last_is_pre = !last_is_splitter; - - return idx_num; -} - -uint16 SpellingParser::splstr_to_idxs_f(const char *splstr, uint16 str_len, - uint16 spl_idx[], uint16 start_pos[], - uint16 max_size, bool &last_is_pre) { - uint16 idx_num = splstr_to_idxs(splstr, str_len, spl_idx, start_pos, - max_size, last_is_pre); - for (uint16 pos = 0; pos < idx_num; pos++) { - if (spl_trie_->is_half_id_yunmu(spl_idx[pos])) { - spl_trie_->half_to_full(spl_idx[pos], spl_idx + pos); - if (pos == idx_num - 1) { - last_is_pre = false; - } - } - } - return idx_num; -} - -uint16 SpellingParser::splstr16_to_idxs(const char16 *splstr, uint16 str_len, - uint16 spl_idx[], uint16 start_pos[], - uint16 max_size, bool &last_is_pre) { - if (NULL == splstr || 0 == max_size || 0 == str_len) - return 0; - - if (!SpellingTrie::is_valid_spl_char(splstr[0])) - return 0; - - last_is_pre = false; - - const SpellingNode *node_this = spl_trie_->root_; - - uint16 str_pos = 0; - uint16 idx_num = 0; - if (NULL != start_pos) - start_pos[0] = 0; - bool last_is_splitter = false; - - while (str_pos < str_len) { - char16 char_this = splstr[str_pos]; - // all characters outside of [a, z] are considered as splitters - if (!SpellingTrie::is_valid_spl_char(char_this)) { - // test if the current node is endable - uint16 id_this = node_this->spelling_idx; - if (spl_trie_->if_valid_id_update(&id_this)) { - spl_idx[idx_num] = id_this; - - idx_num++; - str_pos++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - if (idx_num >= max_size) - return idx_num; - - node_this = spl_trie_->root_; - last_is_splitter = true; - continue; - } else { - if (last_is_splitter) { - str_pos++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - continue; - } else { - return idx_num; - } - } - } - - last_is_splitter = false; - - SpellingNode *found_son = NULL; - - if (0 == str_pos) { - if (char_this >= 'a') - found_son = spl_trie_->level1_sons_[char_this - 'a']; - else - found_son = spl_trie_->level1_sons_[char_this - 'A']; - } else { - SpellingNode *first_son = node_this->first_son; - // Because for Zh/Ch/Sh nodes, they are the last in the buffer and - // frequently used, so we scan from the end. - for (int i = 0; i < node_this->num_of_son; i++) { - SpellingNode *this_son = first_son + i; - if (SpellingTrie::is_same_spl_char( - this_son->char_this_node, char_this)) { - found_son = this_son; - break; - } - } - } - - // found, just move the current node pointer to the the son - if (NULL != found_son) { - node_this = found_son; - } else { - // not found, test if it is endable - uint16 id_this = node_this->spelling_idx; - if (spl_trie_->if_valid_id_update(&id_this)) { - // endable, remember the index - spl_idx[idx_num] = id_this; - - idx_num++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - if (idx_num >= max_size) - return idx_num; - node_this = spl_trie_->root_; - continue; - } else { - return idx_num; - } - } - - str_pos++; - } - - uint16 id_this = node_this->spelling_idx; - if (spl_trie_->if_valid_id_update(&id_this)) { - // endable, remember the index - spl_idx[idx_num] = id_this; - - idx_num++; - if (NULL != start_pos) - start_pos[idx_num] = str_pos; - } - - last_is_pre = !last_is_splitter; - - return idx_num; -} - -uint16 SpellingParser::splstr16_to_idxs_f(const char16 *splstr, uint16 str_len, - uint16 spl_idx[], uint16 start_pos[], - uint16 max_size, bool &last_is_pre) { - uint16 idx_num = splstr16_to_idxs(splstr, str_len, spl_idx, start_pos, - max_size, last_is_pre); - for (uint16 pos = 0; pos < idx_num; pos++) { - if (spl_trie_->is_half_id_yunmu(spl_idx[pos])) { - spl_trie_->half_to_full(spl_idx[pos], spl_idx + pos); - if (pos == idx_num - 1) { - last_is_pre = false; - } - } - } - return idx_num; -} - -uint16 SpellingParser::get_splid_by_str(const char *splstr, uint16 str_len, - bool *is_pre) { - if (NULL == is_pre) - return 0; - - uint16 spl_idx[2]; - uint16 start_pos[3]; - - if (splstr_to_idxs(splstr, str_len, spl_idx, start_pos, 2, *is_pre) != 1) - return 0; - - if (start_pos[1] != str_len) - return 0; - return spl_idx[0]; -} - -uint16 SpellingParser::get_splid_by_str_f(const char *splstr, uint16 str_len, - bool *is_pre) { - if (NULL == is_pre) - return 0; - - uint16 spl_idx[2]; - uint16 start_pos[3]; - - if (splstr_to_idxs(splstr, str_len, spl_idx, start_pos, 2, *is_pre) != 1) - return 0; - - if (start_pos[1] != str_len) - return 0; - if (spl_trie_->is_half_id_yunmu(spl_idx[0])) { - spl_trie_->half_to_full(spl_idx[0], spl_idx); - *is_pre = false; - } - - return spl_idx[0]; -} - -uint16 SpellingParser::get_splids_parallel(const char *splstr, uint16 str_len, - uint16 splidx[], uint16 max_size, - uint16 &full_id_num, bool &is_pre) { - if (max_size <= 0 || !is_valid_to_parse(splstr[0])) - return 0; - - splidx[0] = get_splid_by_str(splstr, str_len, &is_pre); - full_id_num = 0; - if (0 != splidx[0]) { - if (splidx[0] >= kFullSplIdStart) - full_id_num = 1; - return 1; - } - return 0; -} - -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/sync.cpp b/PinyinIME/jni/share/sync.cpp deleted file mode 100644 index 91e27b8..0000000 --- a/PinyinIME/jni/share/sync.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../include/sync.h" -#include -#include - -#ifdef ___SYNC_ENABLED___ - -namespace ime_pinyin { - -Sync::Sync() - : userdict_(NULL), - dictfile_(NULL), - last_count_(0) { -}; - -Sync::~Sync() { -} - - -bool Sync::begin(const char * filename) { - if (userdict_) { - finish(); - } - - if (!filename) { - return false; - } - - dictfile_ = strdup(filename); - if (!dictfile_) { - return false; - } - - userdict_ = new UserDict(); - if (!userdict_) { - free(dictfile_); - dictfile_ = NULL; - return false; - } - - if (userdict_->load_dict((const char*)dictfile_, kUserDictIdStart, - kUserDictIdEnd) == false) { - delete userdict_; - userdict_ = NULL; - free(dictfile_); - dictfile_ = NULL; - return false; - } - - userdict_->set_limit(kUserDictMaxLemmaCount, kUserDictMaxLemmaSize, kUserDictRatio); - - return true; -} - -int Sync::put_lemmas(char16 * lemmas, int len) { - return userdict_->put_lemmas_no_sync_from_utf16le_string(lemmas, len); -} - -int Sync::get_lemmas(char16 * str, int size) { - return userdict_->get_sync_lemmas_in_utf16le_string_from_beginning(str, size, &last_count_); -} - -int Sync::get_last_got_count() { - return last_count_; -} - -int Sync::get_total_count() { - return userdict_->get_sync_count(); -} - -void Sync::clear_last_got() { - if (last_count_ < 0) { - return; - } - userdict_->clear_sync_lemmas(0, last_count_); - last_count_ = 0; -} - -void Sync::finish() { - if (userdict_) { - userdict_->close_dict(); - delete userdict_; - userdict_ = NULL; - free(dictfile_); - dictfile_ = NULL; - last_count_ = 0; - } -} - -int Sync::get_capacity() { - UserDict::UserDictStat stat; - userdict_->state(&stat); - return stat.limit_lemma_count - stat.lemma_count; -} - -} -#endif diff --git a/PinyinIME/jni/share/userdict.cpp b/PinyinIME/jni/share/userdict.cpp deleted file mode 100644 index 5064296..0000000 --- a/PinyinIME/jni/share/userdict.cpp +++ /dev/null @@ -1,2262 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../include/userdict.h" -#include "../include/splparser.h" -#include "../include/ngram.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ime_pinyin { - -#ifdef ___DEBUG_PERF___ -static uint64 _ellapse_ = 0; -static struct timeval _tv_start_, _tv_end_; -#define DEBUG_PERF_BEGIN \ - do { \ - gettimeofday(&_tv_start_, NULL); \ - } while(0) -#define DEBUG_PERF_END \ - do { \ - gettimeofday(&_tv_end_, NULL); \ - _ellapse_ = (_tv_end_.tv_sec - _tv_start_.tv_sec) * 1000000 + \ - (_tv_end_.tv_usec - _tv_start_.tv_usec); \ - } while(0) -#define LOGD_PERF(message) \ - LOGD("PERFORMANCE[%s] %llu usec.", message, _ellapse_); -#else -#define DEBUG_PERF_BEGIN -#define DEBUG_PERF_END -#define LOGD_PERF(message) -#endif - -// XXX File load and write are thread-safe by g_mutex_ -static pthread_mutex_t g_mutex_ = PTHREAD_MUTEX_INITIALIZER; -static struct timeval g_last_update_ = {0, 0}; - -inline uint32 UserDict::get_dict_file_size(UserDictInfo * info) { - return (4 + info->lemma_size + (info->lemma_count << 3) -#ifdef ___PREDICT_ENABLED___ - + (info->lemma_count << 2) -#endif -#ifdef ___SYNC_ENABLED___ - + (info->sync_count << 2) -#endif - + sizeof(*info)); -} - -inline LmaScoreType UserDict::translate_score(int raw_score) { - // 1) ori_freq: original user frequency - uint32 ori_freq = extract_score_freq(raw_score); - // 2) lmt_off: lmt index (week offset for example) - uint64 lmt_off = ((raw_score & 0xffff0000) >> 16); - if (kUserDictLMTBitWidth < 16) { - uint64 mask = ~(1 << kUserDictLMTBitWidth); - lmt_off &= mask; - } - // 3) now_off: current time index (current week offset for example) - // assuming load_time_ is around current time - uint64 now_off = load_time_.tv_sec; - now_off = (now_off - kUserDictLMTSince) / kUserDictLMTGranularity; - now_off = (now_off << (64 - kUserDictLMTBitWidth)); - now_off = (now_off >> (64 - kUserDictLMTBitWidth)); - // 4) factor: decide expand-factor - int delta = now_off - lmt_off; - if (delta > 4) - delta = 4; - int factor = 80 - (delta << 4); - - double tf = (double)(dict_info_.total_nfreq + total_other_nfreq_); - return (LmaScoreType)(log((double)factor * (double)ori_freq / tf) - * NGram::kLogValueAmplifier); -} - -inline int UserDict::extract_score_freq(int raw_score) { - // Frequence stored in lowest 16 bits - int freq = (raw_score & 0x0000ffff); - return freq; -} - -inline uint64 UserDict::extract_score_lmt(int raw_score) { - uint64 lmt = ((raw_score & 0xffff0000) >> 16); - if (kUserDictLMTBitWidth < 16) { - uint64 mask = ~(1 << kUserDictLMTBitWidth); - lmt &= mask; - } - lmt = lmt * kUserDictLMTGranularity + kUserDictLMTSince; - return lmt; -} - -inline int UserDict::build_score(uint64 lmt, int freq) { - lmt = (lmt - kUserDictLMTSince) / kUserDictLMTGranularity; - lmt = (lmt << (64 - kUserDictLMTBitWidth)); - lmt = (lmt >> (64 - kUserDictLMTBitWidth)); - uint16 lmt16 = (uint16)lmt; - int s = freq; - s &= 0x0000ffff; - s = (lmt16 << 16) | s; - return s; -} - -inline int64 UserDict::utf16le_atoll(uint16 *s, int len) { - int64 ret = 0; - if (len <= 0) - return ret; - - int flag = 1; - const uint16 * endp = s + len; - if (*s == '-') { - flag = -1; - s++; - } else if (*s == '+') { - s++; - } - - while (*s >= '0' && *s <= '9' && s < endp) { - ret += ret * 10 + (*s) - '0'; - s++; - } - return ret * flag; -} - -inline int UserDict::utf16le_lltoa(int64 v, uint16 *s, int size) { - if (!s || size <= 0) - return 0; - uint16 *endp = s + size; - int ret_len = 0; - if (v < 0) { - *(s++) = '-'; - ++ret_len; - v *= -1; - } - - uint16 *b = s; - while (s < endp && v != 0) { - *(s++) = '0' + (v % 10); - v = v / 10; - ++ret_len; - } - - if (v != 0) - return 0; - - --s; - - while (b < s) { - *b = *s; - ++b, --s; - } - - return ret_len; -} - -inline void UserDict::set_lemma_flag(uint32 offset, uint8 flag) { - offset &= kUserDictOffsetMask; - lemmas_[offset] |= flag; -} - -inline char UserDict::get_lemma_flag(uint32 offset) { - offset &= kUserDictOffsetMask; - return (char)(lemmas_[offset]); -} - -inline char UserDict::get_lemma_nchar(uint32 offset) { - offset &= kUserDictOffsetMask; - return (char)(lemmas_[offset + 1]); -} - -inline uint16 * UserDict::get_lemma_spell_ids(uint32 offset) { - offset &= kUserDictOffsetMask; - return (uint16 *)(lemmas_ + offset + 2); -} - -inline uint16 * UserDict::get_lemma_word(uint32 offset) { - offset &= kUserDictOffsetMask; - uint8 nchar = get_lemma_nchar(offset); - return (uint16 *)(lemmas_ + offset + 2 + (nchar << 1)); -} - -inline LemmaIdType UserDict::get_max_lemma_id() { - // When a lemma is deleted, we don't not claim its id back for - // simplicity and performance - return start_id_ + dict_info_.lemma_count - 1; -} - -inline bool UserDict::is_valid_lemma_id(LemmaIdType id) { - if (id >= start_id_ && id <= get_max_lemma_id()) - return true; - return false; -} - -inline bool UserDict::is_valid_state() { - if (state_ == USER_DICT_NONE) - return false; - return true; -} - -UserDict::UserDict() - : start_id_(0), - version_(0), - lemmas_(NULL), - offsets_(NULL), - scores_(NULL), - ids_(NULL), -#ifdef ___PREDICT_ENABLED___ - predicts_(NULL), -#endif -#ifdef ___SYNC_ENABLED___ - syncs_(NULL), - sync_count_size_(0), -#endif - offsets_by_id_(NULL), - lemma_count_left_(0), - lemma_size_left_(0), - dict_file_(NULL), - state_(USER_DICT_NONE) { - memset(&dict_info_, 0, sizeof(dict_info_)); - memset(&load_time_, 0, sizeof(load_time_)); -#ifdef ___CACHE_ENABLED___ - cache_init(); -#endif -} - -UserDict::~UserDict() { - close_dict(); -} - -bool UserDict::load_dict(const char *file_name, LemmaIdType start_id, - LemmaIdType end_id) { -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_BEGIN; -#endif - dict_file_ = strdup(file_name); - if (!dict_file_) - return false; - - start_id_ = start_id; - - if (false == validate(file_name) && false == reset(file_name)) { - goto error; - } - if (false == load(file_name, start_id)) { - goto error; - } - - state_ = USER_DICT_SYNC; - - gettimeofday(&load_time_, NULL); - -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_END; - LOGD_PERF("load_dict"); -#endif - return true; - error: - free((void*)dict_file_); - start_id_ = 0; - return false; -} - -bool UserDict::close_dict() { - if (state_ == USER_DICT_NONE) - return true; - if (state_ == USER_DICT_SYNC) - goto out; - - // If dictionary is written back by others, - // we can not simply write back here - // To do a safe flush, we have to discard all newly added - // lemmas and try to reload dict file. - pthread_mutex_lock(&g_mutex_); - if (load_time_.tv_sec > g_last_update_.tv_sec || - (load_time_.tv_sec == g_last_update_.tv_sec && - load_time_.tv_usec > g_last_update_.tv_usec)) { - write_back(); - gettimeofday(&g_last_update_, NULL); - } - pthread_mutex_unlock(&g_mutex_); - - out: - free((void*)dict_file_); - free(lemmas_); - free(offsets_); - free(offsets_by_id_); - free(scores_); - free(ids_); -#ifdef ___PREDICT_ENABLED___ - free(predicts_); -#endif - - version_ = 0; - dict_file_ = NULL; - lemmas_ = NULL; -#ifdef ___SYNC_ENABLED___ - syncs_ = NULL; - sync_count_size_ = 0; -#endif - offsets_ = NULL; - offsets_by_id_ = NULL; - scores_ = NULL; - ids_ = NULL; -#ifdef ___PREDICT_ENABLED___ - predicts_ = NULL; -#endif - - memset(&dict_info_, 0, sizeof(dict_info_)); - lemma_count_left_ = 0; - lemma_size_left_ = 0; - state_ = USER_DICT_NONE; - - return true; -} - -size_t UserDict::number_of_lemmas() { - return dict_info_.lemma_count; -} - -void UserDict::reset_milestones(uint16 from_step, MileStoneHandle from_handle) { - return; -} - -MileStoneHandle UserDict::extend_dict(MileStoneHandle from_handle, - const DictExtPara *dep, - LmaPsbItem *lpi_items, - size_t lpi_max, size_t *lpi_num) { - if (is_valid_state() == false) - return 0; - - bool need_extend = false; - -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_BEGIN; -#endif - *lpi_num = _get_lpis(dep->splids, dep->splids_extended + 1, - lpi_items, lpi_max, &need_extend); -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_END; - LOGD_PERF("extend_dict"); -#endif - return ((*lpi_num > 0 || need_extend) ? 1 : 0); -} - -int UserDict::is_fuzzy_prefix_spell_id( - const uint16 * id1, uint16 len1, const UserDictSearchable *searchable) { - if (len1 < searchable->splids_len) - return 0; - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - uint32 i = 0; - for (i = 0; i < searchable->splids_len; i++) { - const char py1 = *spl_trie.get_spelling_str(id1[i]); - uint16 off = 8 * (i % 4); - const char py2 = ((searchable->signature[i/4] & (0xff << off)) >> off); - if (py1 == py2) - continue; - return 0; - } - return 1; -} - -int UserDict::fuzzy_compare_spell_id( - const uint16 * id1, uint16 len1, const UserDictSearchable *searchable) { - if (len1 < searchable->splids_len) - return -1; - if (len1 > searchable->splids_len) - return 1; - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - uint32 i = 0; - for (i = 0; i < len1; i++) { - const char py1 = *spl_trie.get_spelling_str(id1[i]); - uint16 off = 8 * (i % 4); - const char py2 = ((searchable->signature[i/4] & (0xff << off)) >> off); - if (py1 == py2) - continue; - if (py1 > py2) - return 1; - return -1; - } - return 0; -} - -bool UserDict::is_prefix_spell_id( - const uint16 * fullids, uint16 fulllen, - const UserDictSearchable *searchable) { - if (fulllen < searchable->splids_len) - return false; - - uint32 i = 0; - for (; i < searchable->splids_len; i++) { - uint16 start_id = searchable->splid_start[i]; - uint16 count = searchable->splid_count[i]; - if (fullids[i] >= start_id && fullids[i] < start_id + count) - continue; - else - return false; - } - return true; -} - -bool UserDict::equal_spell_id( - const uint16 * fullids, uint16 fulllen, - const UserDictSearchable *searchable) { - if (fulllen != searchable->splids_len) - return false; - - uint32 i = 0; - for (; i < fulllen; i++) { - uint16 start_id = searchable->splid_start[i]; - uint16 count = searchable->splid_count[i]; - if (fullids[i] >= start_id && fullids[i] < start_id + count) - continue; - else - return false; - } - return true; -} - -int32 UserDict::locate_first_in_offsets(const UserDictSearchable * searchable) { - int32 begin = 0; - int32 end = dict_info_.lemma_count - 1; - int32 middle = -1; - - int32 first_prefix = middle; - int32 last_matched = middle; - - while (begin <= end) { - middle = (begin + end) >> 1; - uint32 offset = offsets_[middle]; - uint8 nchar = get_lemma_nchar(offset); - const uint16 * splids = get_lemma_spell_ids(offset); - int cmp = fuzzy_compare_spell_id(splids, nchar, searchable); - int pre = is_fuzzy_prefix_spell_id(splids, nchar, searchable); - - if (pre) - first_prefix = middle; - - if (cmp < 0) { - begin = middle + 1; - } else if (cmp > 0) { - end = middle - 1; - } else { - end = middle - 1; - last_matched = middle; - } - } - - return first_prefix; -} - -void UserDict::prepare_locate(UserDictSearchable *searchable, - const uint16 *splid_str, - uint16 splid_str_len) { - searchable->splids_len = splid_str_len; - memset(searchable->signature, 0, sizeof(searchable->signature)); - - SpellingTrie &spl_trie = SpellingTrie::get_instance(); - uint32 i = 0; - for (; i < splid_str_len; i++) { - if (spl_trie.is_half_id(splid_str[i])) { - searchable->splid_count[i] = - spl_trie.half_to_full(splid_str[i], - &(searchable->splid_start[i])); - } else { - searchable->splid_count[i] = 1; - searchable->splid_start[i] = splid_str[i]; - } - const unsigned char py = *spl_trie.get_spelling_str(splid_str[i]); - searchable->signature[i>>2] |= (py << (8 * (i % 4))); - } -} - -size_t UserDict::get_lpis(const uint16 *splid_str, uint16 splid_str_len, - LmaPsbItem *lpi_items, size_t lpi_max) { - return _get_lpis(splid_str, splid_str_len, lpi_items, lpi_max, NULL); -} - -size_t UserDict::_get_lpis(const uint16 *splid_str, - uint16 splid_str_len, LmaPsbItem *lpi_items, - size_t lpi_max, bool * need_extend) { - bool tmp_extend; - if (!need_extend) - need_extend = &tmp_extend; - - *need_extend = false; - - if (is_valid_state() == false) - return 0; - if (lpi_max <= 0) - return 0; - - if (0 == pthread_mutex_trylock(&g_mutex_)) { - if (load_time_.tv_sec < g_last_update_.tv_sec || - (load_time_.tv_sec == g_last_update_.tv_sec && - load_time_.tv_usec < g_last_update_.tv_usec)) { - // Others updated disk file, have to reload - pthread_mutex_unlock(&g_mutex_); - flush_cache(); - } else { - pthread_mutex_unlock(&g_mutex_); - } - } else { - } - - UserDictSearchable searchable; - prepare_locate(&searchable, splid_str, splid_str_len); - - uint32 max_off = dict_info_.lemma_count; -#ifdef ___CACHE_ENABLED___ - int32 middle; - uint32 start, count; - bool cached = cache_hit(&searchable, &start, &count); - if (cached) { - middle = start; - max_off = start + count; - } else { - middle = locate_first_in_offsets(&searchable); - start = middle; - } -#else - int32 middle = locate_first_in_offsets(&searchable); -#endif - - if (middle == -1) { -#ifdef ___CACHE_ENABLED___ - if (!cached) - cache_push(USER_DICT_MISS_CACHE, &searchable, 0, 0); -#endif - return 0; - } - - size_t lpi_current = 0; - - bool fuzzy_break = false; - bool prefix_break = false; - while ((size_t)middle < max_off && !fuzzy_break && !prefix_break) { - if (lpi_current >= lpi_max) - break; - uint32 offset = offsets_[middle]; - // Ignore deleted lemmas - if (offset & kUserDictOffsetFlagRemove) { - middle++; - continue; - } - uint8 nchar = get_lemma_nchar(offset); - uint16 * splids = get_lemma_spell_ids(offset); -#ifdef ___CACHE_ENABLED___ - if (!cached && 0 != fuzzy_compare_spell_id(splids, nchar, &searchable)) { -#else - if (0 != fuzzy_compare_spell_id(splids, nchar, &searchable)) { -#endif - fuzzy_break = true; - } - - if (prefix_break == false) { - if (is_fuzzy_prefix_spell_id(splids, nchar, &searchable)) { - if (*need_extend == false && - is_prefix_spell_id(splids, nchar, &searchable)) { - *need_extend = true; - } - } else { - prefix_break = true; - } - } - - if (equal_spell_id(splids, nchar, &searchable) == true) { - lpi_items[lpi_current].psb = translate_score(scores_[middle]); - lpi_items[lpi_current].id = ids_[middle]; - lpi_items[lpi_current].lma_len = nchar; - lpi_current++; - } - middle++; - } - -#ifdef ___CACHE_ENABLED___ - if (!cached) { - count = middle - start; - cache_push(USER_DICT_CACHE, &searchable, start, count); - } -#endif - - return lpi_current; -} - -uint16 UserDict::get_lemma_str(LemmaIdType id_lemma, char16* str_buf, - uint16 str_max) { - if (is_valid_state() == false) - return 0; - if (is_valid_lemma_id(id_lemma) == false) - return 0; - uint32 offset = offsets_by_id_[id_lemma - start_id_]; - uint8 nchar = get_lemma_nchar(offset); - char16 * str = get_lemma_word(offset); - uint16 m = nchar < str_max -1 ? nchar : str_max - 1; - int i = 0; - for (; i < m; i++) { - str_buf[i] = str[i]; - } - str_buf[i] = 0; - return m; -} - -uint16 UserDict::get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, - uint16 splids_max, bool arg_valid) { - if (is_valid_lemma_id(id_lemma) == false) - return 0; - uint32 offset = offsets_by_id_[id_lemma - start_id_]; - uint8 nchar = get_lemma_nchar(offset); - const uint16 * ids = get_lemma_spell_ids(offset); - int i = 0; - for (; i < nchar && i < splids_max; i++) - splids[i] = ids[i]; - return i; -} - -size_t UserDict::predict(const char16 last_hzs[], uint16 hzs_len, - NPredictItem *npre_items, size_t npre_max, - size_t b4_used) { - uint32 new_added = 0; -#ifdef ___PREDICT_ENABLED___ - int32 end = dict_info_.lemma_count - 1; - int j = locate_first_in_predicts((const uint16*)last_hzs, hzs_len); - if (j == -1) - return 0; - - while (j <= end) { - uint32 offset = predicts_[j]; - // Ignore deleted lemmas - if (offset & kUserDictOffsetFlagRemove) { - j++; - continue; - } - uint32 nchar = get_lemma_nchar(offset); - uint16 * words = get_lemma_word(offset); - uint16 * splids = get_lemma_spell_ids(offset); - - if (nchar <= hzs_len) { - j++; - continue; - } - - if (memcmp(words, last_hzs, hzs_len << 1) == 0) { - if (new_added >= npre_max) { - return new_added; - } - uint32 cpy_len = - (nchar < kMaxPredictSize ? (nchar << 1) : (kMaxPredictSize << 1)) - - (hzs_len << 1); - npre_items[new_added].his_len = hzs_len; - npre_items[new_added].psb = get_lemma_score(words, splids, nchar); - memcpy(npre_items[new_added].pre_hzs, words + hzs_len, cpy_len); - if ((cpy_len >> 1) < kMaxPredictSize) { - npre_items[new_added].pre_hzs[cpy_len >> 1] = 0; - } - new_added++; - } else { - break; - } - - j++; - } -#endif - return new_added; -} - -int32 UserDict::locate_in_offsets(char16 lemma_str[], uint16 splid_str[], - uint16 lemma_len) { - int32 max_off = dict_info_.lemma_count; - - UserDictSearchable searchable; - prepare_locate(&searchable, splid_str, lemma_len); -#ifdef ___CACHE_ENABLED___ - int32 off; - uint32 start, count; - bool cached = load_cache(&searchable, &start, &count); - if (cached) { - off = start; - max_off = start + count; - } else { - off = locate_first_in_offsets(&searchable); - start = off; - } -#else - int32 off = locate_first_in_offsets(&searchable); -#endif - - if (off == -1) { - return off; - } - - while (off < max_off) { - uint32 offset = offsets_[off]; - if (offset & kUserDictOffsetFlagRemove) { - off++; - continue; - } - uint16 * splids = get_lemma_spell_ids(offset); -#ifdef ___CACHE_ENABLED___ - if (!cached && 0 != fuzzy_compare_spell_id(splids, lemma_len, &searchable)) - break; -#else - if (0 != fuzzy_compare_spell_id(splids, lemma_len, &searchable)) - break; -#endif - if (equal_spell_id(splids, lemma_len, &searchable) == true) { - uint16 * str = get_lemma_word(offset); - uint32 i = 0; - for (i = 0; i < lemma_len; i++) { - if (str[i] == lemma_str[i]) - continue; - break; - } - if (i < lemma_len) { - off++; - continue; - } -#ifdef ___CACHE_ENABLED___ - // No need to save_cache here, since current function is invoked by - // put_lemma. It's rarely possible for a user input same lemma twice. - // That means first time user type a new lemma, it is newly added into - // user dictionary, then it's possible that user type the same lemma - // again. - // Another reason save_cache can not be invoked here is this function - // aborts when lemma is found, and it never knows the count. -#endif - return off; - } - off++; - } - - return -1; -} - -#ifdef ___PREDICT_ENABLED___ -uint32 UserDict::locate_where_to_insert_in_predicts( - const uint16 * words, int lemma_len) { - int32 begin = 0; - int32 end = dict_info_.lemma_count - 1; - int32 middle = end; - - uint32 last_matched = middle; - - while (begin <= end) { - middle = (begin + end) >> 1; - uint32 offset = offsets_[middle]; - uint8 nchar = get_lemma_nchar(offset); - const uint16 * ws = get_lemma_word(offset); - - uint32 minl = nchar < lemma_len ? nchar : lemma_len; - uint32 k = 0; - int cmp = 0; - - for (; k < minl; k++) { - if (ws[k] < words[k]) { - cmp = -1; - break; - } else if (ws[k] > words[k]) { - cmp = 1; - break; - } - } - if (cmp == 0) { - if (nchar < lemma_len) - cmp = -1; - else if (nchar > lemma_len) - cmp = 1; - } - - if (cmp < 0) { - begin = middle + 1; - last_matched = middle; - } else if (cmp > 0) { - end = middle - 1; - } else { - end = middle - 1; - last_matched = middle; - } - } - - return last_matched; -} - -int32 UserDict::locate_first_in_predicts(const uint16 * words, int lemma_len) { - int32 begin = 0; - int32 end = dict_info_.lemma_count - 1; - int32 middle = -1; - - int32 last_matched = middle; - - while (begin <= end) { - middle = (begin + end) >> 1; - uint32 offset = offsets_[middle]; - uint8 nchar = get_lemma_nchar(offset); - const uint16 * ws = get_lemma_word(offset); - - uint32 minl = nchar < lemma_len ? nchar : lemma_len; - uint32 k = 0; - int cmp = 0; - - for (; k < minl; k++) { - if (ws[k] < words[k]) { - cmp = -1; - break; - } else if (ws[k] > words[k]) { - cmp = 1; - break; - } - } - if (cmp == 0) { - if (nchar >= lemma_len) - last_matched = middle; - if (nchar < lemma_len) - cmp = -1; - else if (nchar > lemma_len) - cmp = 1; - } - - if (cmp < 0) { - begin = middle + 1; - } else if (cmp > 0) { - end = middle - 1; - } else { - end = middle - 1; - } - } - - return last_matched; -} - -#endif - -LemmaIdType UserDict::get_lemma_id(char16 lemma_str[], uint16 splids[], - uint16 lemma_len) { - int32 off = locate_in_offsets(lemma_str, splids, lemma_len); - if (off == -1) { - return 0; - } - - return ids_[off]; -} - -LmaScoreType UserDict::get_lemma_score(LemmaIdType lemma_id) { - if (is_valid_state() == false) - return 0; - if (is_valid_lemma_id(lemma_id) == false) - return 0; - - return translate_score(_get_lemma_score(lemma_id)); -} - -LmaScoreType UserDict::get_lemma_score(char16 lemma_str[], uint16 splids[], - uint16 lemma_len) { - if (is_valid_state() == false) - return 0; - return translate_score(_get_lemma_score(lemma_str, splids, lemma_len)); -} - -int UserDict::_get_lemma_score(LemmaIdType lemma_id) { - if (is_valid_state() == false) - return 0; - if (is_valid_lemma_id(lemma_id) == false) - return 0; - - uint32 offset = offsets_by_id_[lemma_id - start_id_]; - - uint32 nchar = get_lemma_nchar(offset); - uint16 * spl = get_lemma_spell_ids(offset); - uint16 * wrd = get_lemma_word(offset); - - int32 off = locate_in_offsets(wrd, spl, nchar); - if (off == -1) { - return 0; - } - - return scores_[off]; -} - -int UserDict::_get_lemma_score(char16 lemma_str[], uint16 splids[], - uint16 lemma_len) { - if (is_valid_state() == false) - return 0; - - int32 off = locate_in_offsets(lemma_str, splids, lemma_len); - if (off == -1) { - return 0; - } - - return scores_[off]; -} - -#ifdef ___SYNC_ENABLED___ -void UserDict::remove_lemma_from_sync_list(uint32 offset) { - offset &= kUserDictOffsetMask; - uint32 i = 0; - for (; i < dict_info_.sync_count; i++) { - unsigned int off = (syncs_[i] & kUserDictOffsetMask); - if (off == offset) - break; - } - if (i < dict_info_.sync_count) { - syncs_[i] = syncs_[dict_info_.sync_count - 1]; - dict_info_.sync_count--; - } -} -#endif - -#ifdef ___PREDICT_ENABLED___ -void UserDict::remove_lemma_from_predict_list(uint32 offset) { - offset &= kUserDictOffsetMask; - uint32 i = 0; - for (; i < dict_info_.lemma_count; i++) { - unsigned int off = (predicts_[i] & kUserDictOffsetMask); - if (off == offset) { - predicts_[i] |= kUserDictOffsetFlagRemove; - break; - } - } -} -#endif - -bool UserDict::remove_lemma_by_offset_index(int offset_index) { - if (is_valid_state() == false) - return 0; - - int32 off = offset_index; - if (off == -1) { - return false; - } - - uint32 offset = offsets_[off]; - uint32 nchar = get_lemma_nchar(offset); - - offsets_[off] |= kUserDictOffsetFlagRemove; - -#ifdef ___SYNC_ENABLED___ - // Remove corresponding sync item - remove_lemma_from_sync_list(offset); -#endif - -#ifdef ___PREDICT_ENABLED___ - remove_lemma_from_predict_list(offset); -#endif - dict_info_.free_count++; - dict_info_.free_size += (2 + (nchar << 2)); - - if (state_ < USER_DICT_OFFSET_DIRTY) - state_ = USER_DICT_OFFSET_DIRTY; - return true; -} - -bool UserDict::remove_lemma(LemmaIdType lemma_id) { - if (is_valid_state() == false) - return 0; - if (is_valid_lemma_id(lemma_id) == false) - return false; - uint32 offset = offsets_by_id_[lemma_id - start_id_]; - - uint32 nchar = get_lemma_nchar(offset); - uint16 * spl = get_lemma_spell_ids(offset); - uint16 * wrd = get_lemma_word(offset); - - int32 off = locate_in_offsets(wrd, spl, nchar); - - return remove_lemma_by_offset_index(off); -} - -void UserDict::flush_cache() { - LemmaIdType start_id = start_id_; - const char * file = strdup(dict_file_); - if (!file) - return; - close_dict(); - load_dict(file, start_id, kUserDictIdEnd); - free((void*)file); -#ifdef ___CACHE_ENABLED___ - cache_init(); -#endif - return; -} - -bool UserDict::reset(const char *file) { - FILE *fp = fopen(file, "w+"); - if (!fp) { - return false; - } - uint32 version = kUserDictVersion; - size_t wred = fwrite(&version, 1, 4, fp); - UserDictInfo info; - memset(&info, 0, sizeof(info)); - // By default, no limitation for lemma count and size - // thereby, reclaim_ratio is never used - wred += fwrite(&info, 1, sizeof(info), fp); - if (wred != sizeof(info) + sizeof(version)) { - fclose(fp); - unlink(file); - return false; - } - fclose(fp); - return true; -} - -bool UserDict::validate(const char *file) { - // b is ignored in POSIX compatible os including Linux - // while b is important flag for Windows to specify binary mode - FILE *fp = fopen(file, "rb"); - if (!fp) { - return false; - } - - size_t size; - size_t readed; - uint32 version; - UserDictInfo dict_info; - - // validate - int err = fseek(fp, 0, SEEK_END); - if (err) { - goto error; - } - - size = ftell(fp); - if (size < 4 + sizeof(dict_info)) { - goto error; - } - - err = fseek(fp, 0, SEEK_SET); - if (err) { - goto error; - } - - readed = fread(&version, 1, sizeof(version), fp); - if (readed < sizeof(version)) { - goto error; - } - if (version != kUserDictVersion) { - goto error; - } - - err = fseek(fp, -1 * sizeof(dict_info), SEEK_END); - if (err) { - goto error; - } - - readed = fread(&dict_info, 1, sizeof(dict_info), fp); - if (readed != sizeof(dict_info)) { - goto error; - } - - if (size != get_dict_file_size(&dict_info)) { - goto error; - } - - fclose(fp); - return true; - - error: - fclose(fp); - return false; -} - -bool UserDict::load(const char *file, LemmaIdType start_id) { - if (0 != pthread_mutex_trylock(&g_mutex_)) { - return false; - } - // b is ignored in POSIX compatible os including Linux - // while b is important flag for Windows to specify binary mode - FILE *fp = fopen(file, "rb"); - if (!fp) { - pthread_mutex_unlock(&g_mutex_); - return false; - } - - size_t readed, toread; - UserDictInfo dict_info; - uint8 *lemmas = NULL; - uint32 *offsets = NULL; -#ifdef ___SYNC_ENABLED___ - uint32 *syncs = NULL; -#endif - uint32 *scores = NULL; - uint32 *ids = NULL; - uint32 *offsets_by_id = NULL; -#ifdef ___PREDICT_ENABLED___ - uint32 *predicts = NULL; -#endif - size_t i; - int err; - - err = fseek(fp, -1 * sizeof(dict_info), SEEK_END); - if (err) goto error; - - readed = fread(&dict_info, 1, sizeof(dict_info), fp); - if (readed != sizeof(dict_info)) goto error; - - lemmas = (uint8 *)malloc( - dict_info.lemma_size + - (kUserDictPreAlloc * (2 + (kUserDictAverageNchar << 2)))); - - if (!lemmas) goto error; - - offsets = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); - if (!offsets) goto error; - -#ifdef ___PREDICT_ENABLED___ - predicts = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); - if (!predicts) goto error; -#endif - -#ifdef ___SYNC_ENABLED___ - syncs = (uint32 *)malloc((dict_info.sync_count + kUserDictPreAlloc) << 2); - if (!syncs) goto error; -#endif - - scores = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); - if (!scores) goto error; - - ids = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); - if (!ids) goto error; - - offsets_by_id = (uint32 *)malloc( - (dict_info.lemma_count + kUserDictPreAlloc) << 2); - if (!offsets_by_id) goto error; - - err = fseek(fp, 4, SEEK_SET); - if (err) goto error; - - readed = 0; - while (readed < dict_info.lemma_size && !ferror(fp) && !feof(fp)) { - readed += fread(lemmas + readed, 1, dict_info.lemma_size - readed, fp); - } - if (readed < dict_info.lemma_size) - goto error; - - toread = (dict_info.lemma_count << 2); - readed = 0; - while (readed < toread && !ferror(fp) && !feof(fp)) { - readed += fread((((uint8*)offsets) + readed), 1, toread - readed, fp); - } - if (readed < toread) - goto error; - -#ifdef ___PREDICT_ENABLED___ - toread = (dict_info.lemma_count << 2); - readed = 0; - while (readed < toread && !ferror(fp) && !feof(fp)) { - readed += fread((((uint8*)predicts) + readed), 1, toread - readed, fp); - } - if (readed < toread) - goto error; -#endif - - readed = 0; - while (readed < toread && !ferror(fp) && !feof(fp)) { - readed += fread((((uint8*)scores) + readed), 1, toread - readed, fp); - } - if (readed < toread) - goto error; - -#ifdef ___SYNC_ENABLED___ - toread = (dict_info.sync_count << 2); - readed = 0; - while (readed < toread && !ferror(fp) && !feof(fp)) { - readed += fread((((uint8*)syncs) + readed), 1, toread - readed, fp); - } - if (readed < toread) - goto error; -#endif - - for (i = 0; i < dict_info.lemma_count; i++) { - ids[i] = start_id + i; - offsets_by_id[i] = offsets[i]; - } - - lemmas_ = lemmas; - offsets_ = offsets; -#ifdef ___SYNC_ENABLED___ - syncs_ = syncs; - sync_count_size_ = dict_info.sync_count + kUserDictPreAlloc; -#endif - offsets_by_id_ = offsets_by_id; - scores_ = scores; - ids_ = ids; -#ifdef ___PREDICT_ENABLED___ - predicts_ = predicts; -#endif - lemma_count_left_ = kUserDictPreAlloc; - lemma_size_left_ = kUserDictPreAlloc * (2 + (kUserDictAverageNchar << 2)); - memcpy(&dict_info_, &dict_info, sizeof(dict_info)); - state_ = USER_DICT_SYNC; - - fclose(fp); - - pthread_mutex_unlock(&g_mutex_); - return true; - - error: - if (lemmas) free(lemmas); - if (offsets) free(offsets); -#ifdef ___SYNC_ENABLED___ - if (syncs) free(syncs); -#endif - if (scores) free(scores); - if (ids) free(ids); - if (offsets_by_id) free(offsets_by_id); -#ifdef ___PREDICT_ENABLED___ - if (predicts) free(predicts); -#endif - fclose(fp); - pthread_mutex_unlock(&g_mutex_); - return false; -} - -void UserDict::write_back() { - // XXX write back is only allowed from close_dict due to thread-safe sake - if (state_ == USER_DICT_NONE || state_ == USER_DICT_SYNC) - return; - int fd = open(dict_file_, O_WRONLY); - if (fd == -1) - return; - switch (state_) { - case USER_DICT_DEFRAGMENTED: - write_back_all(fd); - break; - case USER_DICT_LEMMA_DIRTY: - write_back_lemma(fd); - break; - case USER_DICT_OFFSET_DIRTY: - write_back_offset(fd); - break; - case USER_DICT_SCORE_DIRTY: - write_back_score(fd); - break; -#ifdef ___SYNC_ENABLED___ - case USER_DICT_SYNC_DIRTY: - write_back_sync(fd); - break; -#endif - default: - break; - } - // It seems truncate is not need on Linux, Windows except Mac - // I am doing it here anyway for safety. - off_t cur = lseek(fd, 0, SEEK_CUR); - ftruncate(fd, cur); - close(fd); - state_ = USER_DICT_SYNC; -} - -#ifdef ___SYNC_ENABLED___ -void UserDict::write_back_sync(int fd) { - int err = lseek(fd, 4 + dict_info_.lemma_size - + (dict_info_.lemma_count << 3) -#ifdef ___PREDICT_ENABLED___ - + (dict_info_.lemma_count << 2) -#endif - , SEEK_SET); - if (err == -1) - return; - write(fd, syncs_, dict_info_.sync_count << 2); - write(fd, &dict_info_, sizeof(dict_info_)); -} -#endif - -void UserDict::write_back_offset(int fd) { - int err = lseek(fd, 4 + dict_info_.lemma_size, SEEK_SET); - if (err == -1) - return; - write(fd, offsets_, dict_info_.lemma_count << 2); -#ifdef ___PREDICT_ENABLED___ - write(fd, predicts_, dict_info_.lemma_count << 2); -#endif - write(fd, scores_, dict_info_.lemma_count << 2); -#ifdef ___SYNC_ENABLED___ - write(fd, syncs_, dict_info_.sync_count << 2); -#endif - write(fd, &dict_info_, sizeof(dict_info_)); -} - -void UserDict::write_back_score(int fd) { - int err = lseek(fd, 4 + dict_info_.lemma_size - + (dict_info_.lemma_count << 2) -#ifdef ___PREDICT_ENABLED___ - + (dict_info_.lemma_count << 2) -#endif - , SEEK_SET); - if (err == -1) - return; - write(fd, scores_, dict_info_.lemma_count << 2); -#ifdef ___SYNC_ENABLED___ - write(fd, syncs_, dict_info_.sync_count << 2); -#endif - write(fd, &dict_info_, sizeof(dict_info_)); -} - -void UserDict::write_back_lemma(int fd) { - int err = lseek(fd, 4, SEEK_SET); - if (err == -1) - return; - // New lemmas are always appended, no need to write whole lemma block - size_t need_write = kUserDictPreAlloc * - (2 + (kUserDictAverageNchar << 2)) - lemma_size_left_; - err = lseek(fd, dict_info_.lemma_size - need_write, SEEK_CUR); - if (err == -1) - return; - write(fd, lemmas_ + dict_info_.lemma_size - need_write, need_write); - - write(fd, offsets_, dict_info_.lemma_count << 2); -#ifdef ___PREDICT_ENABLED___ - write(fd, predicts_, dict_info_.lemma_count << 2); -#endif - write(fd, scores_, dict_info_.lemma_count << 2); -#ifdef ___SYNC_ENABLED___ - write(fd, syncs_, dict_info_.sync_count << 2); -#endif - write(fd, &dict_info_, sizeof(dict_info_)); -} - -void UserDict::write_back_all(int fd) { - // XXX lemma_size is handled differently in writeall - // and writelemma. I update lemma_size and lemma_count in different - // places for these two cases. Should fix it to make it consistent. - int err = lseek(fd, 4, SEEK_SET); - if (err == -1) - return; - write(fd, lemmas_, dict_info_.lemma_size); - write(fd, offsets_, dict_info_.lemma_count << 2); -#ifdef ___PREDICT_ENABLED___ - write(fd, predicts_, dict_info_.lemma_count << 2); -#endif - write(fd, scores_, dict_info_.lemma_count << 2); -#ifdef ___SYNC_ENABLED___ - write(fd, syncs_, dict_info_.sync_count << 2); -#endif - write(fd, &dict_info_, sizeof(dict_info_)); -} - -#ifdef ___CACHE_ENABLED___ -bool UserDict::load_cache(UserDictSearchable *searchable, - uint32 *offset, uint32 *length) { - UserDictCache *cache = &caches_[searchable->splids_len - 1]; - if (cache->head == cache->tail) - return false; - - uint16 j, sig_len = kMaxLemmaSize / 4; - uint16 i = cache->head; - while (1) { - j = 0; - for (; j < sig_len; j++) { - if (cache->signatures[i][j] != searchable->signature[j]) - break; - } - if (j < sig_len) { - i++; - if (i >= kUserDictCacheSize) - i -= kUserDictCacheSize; - if (i == cache->tail) - break; - continue; - } - *offset = cache->offsets[i]; - *length = cache->lengths[i]; - return true; - } - return false; -} - -void UserDict::save_cache(UserDictSearchable *searchable, - uint32 offset, uint32 length) { - UserDictCache *cache = &caches_[searchable->splids_len - 1]; - uint16 next = cache->tail; - - cache->offsets[next] = offset; - cache->lengths[next] = length; - uint16 sig_len = kMaxLemmaSize / 4; - uint16 j = 0; - for (; j < sig_len; j++) { - cache->signatures[next][j] = searchable->signature[j]; - } - - if (++next >= kUserDictCacheSize) { - next -= kUserDictCacheSize; - } - if (next == cache->head) { - cache->head++; - if (cache->head >= kUserDictCacheSize) { - cache->head -= kUserDictCacheSize; - } - } - cache->tail = next; -} - -void UserDict::reset_cache() { - memset(caches_, 0, sizeof(caches_)); -} - -bool UserDict::load_miss_cache(UserDictSearchable *searchable) { - UserDictMissCache *cache = &miss_caches_[searchable->splids_len - 1]; - if (cache->head == cache->tail) - return false; - - uint16 j, sig_len = kMaxLemmaSize / 4; - uint16 i = cache->head; - while (1) { - j = 0; - for (; j < sig_len; j++) { - if (cache->signatures[i][j] != searchable->signature[j]) - break; - } - if (j < sig_len) { - i++; - if (i >= kUserDictMissCacheSize) - i -= kUserDictMissCacheSize; - if (i == cache->tail) - break; - continue; - } - return true; - } - return false; -} - -void UserDict::save_miss_cache(UserDictSearchable *searchable) { - UserDictMissCache *cache = &miss_caches_[searchable->splids_len - 1]; - uint16 next = cache->tail; - - uint16 sig_len = kMaxLemmaSize / 4; - uint16 j = 0; - for (; j < sig_len; j++) { - cache->signatures[next][j] = searchable->signature[j]; - } - - if (++next >= kUserDictMissCacheSize) { - next -= kUserDictMissCacheSize; - } - if (next == cache->head) { - cache->head++; - if (cache->head >= kUserDictMissCacheSize) { - cache->head -= kUserDictMissCacheSize; - } - } - cache->tail = next; -} - -void UserDict::reset_miss_cache() { - memset(miss_caches_, 0, sizeof(miss_caches_)); -} - -void UserDict::cache_init() { - reset_cache(); - reset_miss_cache(); -} - -bool UserDict::cache_hit(UserDictSearchable *searchable, - uint32 *offset, uint32 *length) { - bool hit = load_miss_cache(searchable); - if (hit) { - *offset = 0; - *length = 0; - return true; - } - hit = load_cache(searchable, offset, length); - if (hit) { - return true; - } - return false; -} - -void UserDict::cache_push(UserDictCacheType type, - UserDictSearchable *searchable, - uint32 offset, uint32 length) { - switch (type) { - case USER_DICT_MISS_CACHE: - save_miss_cache(searchable); - break; - case USER_DICT_CACHE: - save_cache(searchable, offset, length); - break; - default: - break; - } -} - -#endif - -void UserDict::defragment(void) { -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_BEGIN; -#endif - if (is_valid_state() == false) - return; - // Fixup offsets_, set REMOVE flag to lemma's flag if needed - size_t first_freed = 0; - size_t first_inuse = 0; - while (first_freed < dict_info_.lemma_count) { - // Find first freed offset - while ((offsets_[first_freed] & kUserDictOffsetFlagRemove) == 0 && - first_freed < dict_info_.lemma_count) { - first_freed++; - } - if (first_freed < dict_info_.lemma_count) { - // Save REMOVE flag to lemma flag - int off = offsets_[first_freed]; - set_lemma_flag(off, kUserDictLemmaFlagRemove); - } else { - break; - } - // Find first inuse offse after first_freed - first_inuse = first_freed + 1; - while ((offsets_[first_inuse] & kUserDictOffsetFlagRemove) && - (first_inuse < dict_info_.lemma_count)) { - // Save REMOVE flag to lemma flag - int off = offsets_[first_inuse]; - set_lemma_flag(off, kUserDictLemmaFlagRemove); - first_inuse++; - } - if (first_inuse >= dict_info_.lemma_count) { - break; - } - // Swap offsets_ - int tmp = offsets_[first_inuse]; - offsets_[first_inuse] = offsets_[first_freed]; - offsets_[first_freed] = tmp; - // Move scores_, no need to swap - tmp = scores_[first_inuse]; - scores_[first_inuse] = scores_[first_freed]; - scores_[first_freed] = tmp; - // Swap ids_ - LemmaIdType tmpid = ids_[first_inuse]; - ids_[first_inuse] = ids_[first_freed]; - ids_[first_freed] = tmpid; - // Go on - first_freed++; - } -#ifdef ___PREDICT_ENABLED___ - // Fixup predicts_ - first_freed = 0; - first_inuse = 0; - while (first_freed < dict_info_.lemma_count) { - // Find first freed offset - while ((predicts_[first_freed] & kUserDictOffsetFlagRemove) == 0 && - first_freed < dict_info_.lemma_count) { - first_freed++; - } - if (first_freed >= dict_info_.lemma_count) - break; - // Find first inuse offse after first_freed - first_inuse = first_freed + 1; - while ((predicts_[first_inuse] & kUserDictOffsetFlagRemove) - && (first_inuse < dict_info_.lemma_count)) { - first_inuse++; - } - if (first_inuse >= dict_info_.lemma_count) { - break; - } - // Swap offsets_ - int tmp = predicts_[first_inuse]; - predicts_[first_inuse] = predicts_[first_freed]; - predicts_[first_freed] = tmp; - // Go on - first_freed++; - } -#endif - dict_info_.lemma_count = first_freed; - // Fixup lemmas_ - size_t begin = 0; - size_t end = 0; - size_t dst = 0; - int total_size = dict_info_.lemma_size + lemma_size_left_; - int total_count = dict_info_.lemma_count + lemma_count_left_; - size_t real_size = total_size - lemma_size_left_; - while (dst < real_size) { - unsigned char flag = get_lemma_flag(dst); - unsigned char nchr = get_lemma_nchar(dst); - if ((flag & kUserDictLemmaFlagRemove) == 0) { - dst += nchr * 4 + 2; - continue; - } - break; - } - if (dst >= real_size) - return; - - end = dst; - while (end < real_size) { - begin = end + get_lemma_nchar(end) * 4 + 2; - repeat: - // not used any more - if (begin >= real_size) - break; - unsigned char flag = get_lemma_flag(begin); - unsigned char nchr = get_lemma_nchar(begin); - if (flag & kUserDictLemmaFlagRemove) { - begin += nchr * 4 + 2; - goto repeat; - } - end = begin + nchr * 4 + 2; - while (end < real_size) { - unsigned char eflag = get_lemma_flag(end); - unsigned char enchr = get_lemma_nchar(end); - if ((eflag & kUserDictLemmaFlagRemove) == 0) { - end += enchr * 4 + 2; - continue; - } - break; - } - memmove(lemmas_ + dst, lemmas_ + begin, end - begin); - for (size_t j = 0; j < dict_info_.lemma_count; j++) { - if (offsets_[j] >= begin && offsets_[j] < end) { - offsets_[j] -= (begin - dst); - offsets_by_id_[ids_[j] - start_id_] = offsets_[j]; - } -#ifdef ___PREDICT_ENABLED___ - if (predicts_[j] >= begin && predicts_[j] < end) { - predicts_[j] -= (begin - dst); - } -#endif - } -#ifdef ___SYNC_ENABLED___ - for (size_t j = 0; j < dict_info_.sync_count; j++) { - if (syncs_[j] >= begin && syncs_[j] < end) { - syncs_[j] -= (begin - dst); - } - } -#endif - dst += (end - begin); - } - - dict_info_.free_count = 0; - dict_info_.free_size = 0; - dict_info_.lemma_size = dst; - lemma_size_left_ = total_size - dict_info_.lemma_size; - lemma_count_left_ = total_count - dict_info_.lemma_count; - - // XXX Without following code, - // offsets_by_id_ is not reordered. - // That's to say, all removed lemmas' ids are not collected back. - // There may not be room for addition of new lemmas due to - // offsests_by_id_ reason, although lemma_size_left_ is fixed. - // By default, we do want defrag as fast as possible, because - // during defrag procedure, other peers can not write new lemmas - // to user dictionary file. - // XXX If write-back is invoked immediately after - // this defragment, no need to fix up following in-mem data. - for (uint32 i = 0; i < dict_info_.lemma_count; i++) { - ids_[i] = start_id_ + i; - offsets_by_id_[i] = offsets_[i]; - } - - state_ = USER_DICT_DEFRAGMENTED; - -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_END; - LOGD_PERF("defragment"); -#endif -} - -#ifdef ___SYNC_ENABLED___ -void UserDict::clear_sync_lemmas(unsigned int start, unsigned int end) { - if (is_valid_state() == false) - return; - if (end > dict_info_.sync_count) - end = dict_info_.sync_count; - memmove(syncs_ + start, syncs_ + end, (dict_info_.sync_count - end) << 2); - dict_info_.sync_count -= (end - start); - if (state_ < USER_DICT_SYNC_DIRTY) - state_ = USER_DICT_SYNC_DIRTY; -} - -int UserDict::get_sync_count() { - if (is_valid_state() == false) - return 0; - return dict_info_.sync_count; -} - -LemmaIdType UserDict::put_lemma_no_sync(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count, uint64 lmt) { - int again = 0; - begin: - LemmaIdType id; - uint32 * syncs_bak = syncs_; - syncs_ = NULL; - id = _put_lemma(lemma_str, splids, lemma_len, count, lmt); - syncs_ = syncs_bak; - if (id == 0 && again == 0) { - if ((dict_info_.limit_lemma_count > 0 && - dict_info_.lemma_count >= dict_info_.limit_lemma_count) - || (dict_info_.limit_lemma_size > 0 && - dict_info_.lemma_size + (2 + (lemma_len << 2)) - > dict_info_.limit_lemma_size)) { - // XXX Always reclaim and defrag in sync code path - // sync thread is background thread and ok with heavy work - reclaim(); - defragment(); - flush_cache(); - again = 1; - goto begin; - } - } - return id; -} - -int UserDict::put_lemmas_no_sync_from_utf16le_string(char16 * lemmas, int len) { - int newly_added = 0; - - SpellingParser * spl_parser = new SpellingParser(); - if (!spl_parser) { - return 0; - } -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_BEGIN; -#endif - char16 *ptr = lemmas; - - // Extract pinyin,words,frequence,last_mod_time - char16 * p = ptr, * py16 = ptr; - char16 * hz16 = NULL; - int py16_len = 0; - uint16 * splid = NULL; - int splid_size = 0; - int splid_len = 0; - int hz16_len = 0; - char16 * fr16 = NULL; - int fr16_len = 0; - - while (p - ptr < len) { - // Pinyin - py16 = p; - splid_len = 0; - while (*p != 0x2c && (p - ptr) < len) { - if (*p == 0x20) - splid_len++; - p++; - } - splid_len++; - if (p - ptr == len) - break; - py16_len = p - py16; - if (splid_size < splid_len) { - char16 * tid = (char16*)realloc(splid, splid_len * 2); - if (!tid) - break; - splid = tid; - splid_size = splid_len * 2; - } - bool is_pre; - int splidl = spl_parser->splstr16_to_idxs_f( - py16, py16_len, splid, NULL, splid_size, is_pre); - if (splidl != splid_len) - break; - // Phrase - hz16 = ++p; - while (*p != 0x2c && (p - ptr) < len) { - p++; - } - hz16_len = p - hz16; - if (hz16_len != splid_len) - break; - // Frequency - fr16 = ++p; - fr16_len = 0; - while (*p != 0x2c && (p - ptr) < len) { - p++; - } - fr16_len = p - fr16; - uint32 intf = (uint32)utf16le_atoll(fr16, fr16_len); - // Last modified time - fr16 = ++p; - fr16_len = 0; - while (*p != 0x3b && (p - ptr) < len) { - p++; - } - fr16_len = p - fr16; - uint64 last_mod = utf16le_atoll(fr16, fr16_len); - - put_lemma_no_sync(hz16, splid, splid_len, intf, last_mod); - newly_added++; - - p++; - } - - if (splid) - free(splid); - -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_END; - LOGD_PERF("put_lemmas_no_sync_from_utf16le_string"); -#endif - return newly_added; -} - -int UserDict::get_sync_lemmas_in_utf16le_string_from_beginning( - char16 * str, int size, int * count) { - int len = 0; - *count = 0; - - int left_len = size; - - if (is_valid_state() == false) - return len; - - SpellingTrie * spl_trie = &SpellingTrie::get_instance(); - if (!spl_trie) { - return 0; - } - - uint32 i; - for (i = 0; i < dict_info_.sync_count; i++) { - int offset = syncs_[i]; - uint32 nchar = get_lemma_nchar(offset); - uint16 *spl = get_lemma_spell_ids(offset); - uint16 *wrd = get_lemma_word(offset); - int score = _get_lemma_score(wrd, spl, nchar); - - static char score_temp[32], *pscore_temp = score_temp; - static char16 temp[256], *ptemp = temp; - - pscore_temp = score_temp; - ptemp = temp; - - uint32 j; - // Add pinyin - for (j = 0; j < nchar; j++) { - int ret_len = spl_trie->get_spelling_str16( - spl[j], ptemp, temp + sizeof(temp) - ptemp); - if (ret_len <= 0) - break; - ptemp += ret_len; - if (ptemp < temp + sizeof(temp) - 1) { - *(ptemp++) = ' '; - } else { - j = 0; - break; - } - } - if (j < nchar) { - continue; - } - ptemp--; - if (ptemp < temp + sizeof(temp) - 1) { - *(ptemp++) = ','; - } else { - continue; - } - // Add phrase - for (j = 0; j < nchar; j++) { - if (ptemp < temp + sizeof(temp) - 1) { - *(ptemp++) = wrd[j]; - } else { - break; - } - } - if (j < nchar) { - continue; - } - if (ptemp < temp + sizeof(temp) - 1) { - *(ptemp++) = ','; - } else { - continue; - } - // Add frequency - uint32 intf = extract_score_freq(score); - int ret_len = utf16le_lltoa(intf, ptemp, temp + sizeof(temp) - ptemp); - if (ret_len <= 0) - continue; - ptemp += ret_len; - if (ptemp < temp + sizeof(temp) - 1) { - *(ptemp++) = ','; - } else { - continue; - } - // Add last modified time - uint64 last_mod = extract_score_lmt(score); - ret_len = utf16le_lltoa(last_mod, ptemp, temp + sizeof(temp) - ptemp); - if (ret_len <= 0) - continue; - ptemp += ret_len; - if (ptemp < temp + sizeof(temp) - 1) { - *(ptemp++) = ';'; - } else { - continue; - } - - // Write to string - int need_len = ptemp - temp; - if (need_len > left_len) - break; - memcpy(str + len, temp, need_len * 2); - left_len -= need_len; - - len += need_len; - (*count)++; - } - - if (len > 0) { - if (state_ < USER_DICT_SYNC_DIRTY) - state_ = USER_DICT_SYNC_DIRTY; - } - return len; -} - -#endif - -bool UserDict::state(UserDictStat * stat) { - if (is_valid_state() == false) - return false; - if (!stat) - return false; - stat->version = version_; - stat->file_name = dict_file_; - stat->load_time.tv_sec = load_time_.tv_sec; - stat->load_time.tv_usec = load_time_.tv_usec; - pthread_mutex_lock(&g_mutex_); - stat->last_update.tv_sec = g_last_update_.tv_sec; - stat->last_update.tv_usec = g_last_update_.tv_usec; - pthread_mutex_unlock(&g_mutex_); - stat->disk_size = get_dict_file_size(&dict_info_); - stat->lemma_count = dict_info_.lemma_count; - stat->lemma_size = dict_info_.lemma_size; - stat->delete_count = dict_info_.free_count; - stat->delete_size = dict_info_.free_size; -#ifdef ___SYNC_ENABLED___ - stat->sync_count = dict_info_.sync_count; -#endif - stat->limit_lemma_count = dict_info_.limit_lemma_count; - stat->limit_lemma_size = dict_info_.limit_lemma_size; - stat->reclaim_ratio = dict_info_.reclaim_ratio; - return true; -} - -void UserDict::set_limit(uint32 max_lemma_count, - uint32 max_lemma_size, uint32 reclaim_ratio) { - dict_info_.limit_lemma_count = max_lemma_count; - dict_info_.limit_lemma_size = max_lemma_size; - if (reclaim_ratio > 100) - reclaim_ratio = 100; - dict_info_.reclaim_ratio = reclaim_ratio; -} - -void UserDict::reclaim() { - if (is_valid_state() == false) - return; - - switch (dict_info_.reclaim_ratio) { - case 0: - return; - case 100: - // TODO: CLEAR to be implemented - assert(false); - return; - default: - break; - } - - // XXX Reclaim is only based on count, not size - uint32 count = dict_info_.lemma_count; - int rc = count * dict_info_.reclaim_ratio / 100; - - UserDictScoreOffsetPair * score_offset_pairs = NULL; - score_offset_pairs = (UserDictScoreOffsetPair *)malloc( - sizeof(UserDictScoreOffsetPair) * rc); - if (score_offset_pairs == NULL) { - return; - } - - for (int i = 0; i < rc; i++) { - int s = scores_[i]; - score_offset_pairs[i].score = s; - score_offset_pairs[i].offset_index = i; - } - - for (int i = (rc + 1) / 2; i >= 0; i--) - shift_down(score_offset_pairs, i, rc); - - for (uint32 i = rc; i < dict_info_.lemma_count; i++) { - int s = scores_[i]; - if (s < score_offset_pairs[0].score) { - score_offset_pairs[0].score = s; - score_offset_pairs[0].offset_index = i; - shift_down(score_offset_pairs, 0, rc); - } - } - - for (int i = 0; i < rc; i++) { - int off = score_offset_pairs[i].offset_index; - remove_lemma_by_offset_index(off); - } - if (rc > 0) { - if (state_ < USER_DICT_OFFSET_DIRTY) - state_ = USER_DICT_OFFSET_DIRTY; - } - - free(score_offset_pairs); -} - -inline void UserDict::swap(UserDictScoreOffsetPair * sop, int i, int j) { - int s = sop[i].score; - int p = sop[i].offset_index; - sop[i].score = sop[j].score; - sop[i].offset_index = sop[j].offset_index; - sop[j].score = s; - sop[j].offset_index = p; -} - -void UserDict::shift_down(UserDictScoreOffsetPair * sop, int i, int n) { - int par = i; - while (par < n) { - int left = par * 2 + 1; - int right = left + 1; - if (left >= n && right >= n) - break; - if (right >= n) { - if (sop[left].score > sop[par].score) { - swap(sop, left, par); - par = left; - continue; - } - } else if (sop[left].score > sop[right].score && - sop[left].score > sop[par].score) { - swap(sop, left, par); - par = left; - continue; - } else if (sop[right].score > sop[left].score && - sop[right].score > sop[par].score) { - swap(sop, right, par); - par = right; - continue; - } - break; - } -} - -LemmaIdType UserDict::put_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count) { - return _put_lemma(lemma_str, splids, lemma_len, count, time(NULL)); -} - -LemmaIdType UserDict::_put_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count, uint64 lmt) { -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_BEGIN; -#endif - if (is_valid_state() == false) - return 0; - int32 off = locate_in_offsets(lemma_str, splids, lemma_len); - if (off != -1) { - int delta_score = count - scores_[off]; - dict_info_.total_nfreq += delta_score; - scores_[off] = build_score(lmt, count); - if (state_ < USER_DICT_SCORE_DIRTY) - state_ = USER_DICT_SCORE_DIRTY; -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_END; - LOGD_PERF("_put_lemma(update)"); -#endif - return ids_[off]; - } else { - if ((dict_info_.limit_lemma_count > 0 && - dict_info_.lemma_count >= dict_info_.limit_lemma_count) - || (dict_info_.limit_lemma_size > 0 && - dict_info_.lemma_size + (2 + (lemma_len << 2)) - > dict_info_.limit_lemma_size)) { - // XXX Don't defragment here, it's too time-consuming. - return 0; - } - int flushed = 0; - if (lemma_count_left_ == 0 || - lemma_size_left_ < (size_t)(2 + (lemma_len << 2))) { - - // XXX When there is no space for new lemma, we flush to disk - // flush_cache() may be called by upper user - // and better place shoule be found instead of here - flush_cache(); - flushed = 1; - // Or simply return and do nothing - // return 0; - } -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_END; - LOGD_PERF(flushed ? "_put_lemma(flush+add)" : "_put_lemma(add)"); -#endif - LemmaIdType id = append_a_lemma(lemma_str, splids, lemma_len, count, lmt); -#ifdef ___SYNC_ENABLED___ - if (syncs_ && id != 0) { - queue_lemma_for_sync(id); - } -#endif - return id; - } - return 0; -} - -#ifdef ___SYNC_ENABLED___ -void UserDict::queue_lemma_for_sync(LemmaIdType id) { - if (dict_info_.sync_count < sync_count_size_) { - syncs_[dict_info_.sync_count++] = offsets_by_id_[id - start_id_]; - } else { - uint32 * syncs = (uint32*)realloc( - syncs_, (sync_count_size_ + kUserDictPreAlloc) << 2); - if (syncs) { - sync_count_size_ += kUserDictPreAlloc; - syncs_ = syncs; - syncs_[dict_info_.sync_count++] = offsets_by_id_[id - start_id_]; - } - } -} -#endif - -LemmaIdType UserDict::update_lemma(LemmaIdType lemma_id, int16 delta_count, - bool selected) { -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_BEGIN; -#endif - if (is_valid_state() == false) - return 0; - if (is_valid_lemma_id(lemma_id) == false) - return 0; - uint32 offset = offsets_by_id_[lemma_id - start_id_]; - uint8 lemma_len = get_lemma_nchar(offset); - char16 * lemma_str = get_lemma_word(offset); - uint16 * splids = get_lemma_spell_ids(offset); - - int32 off = locate_in_offsets(lemma_str, splids, lemma_len); - if (off != -1) { - int score = scores_[off]; - int count = extract_score_freq(score); - uint64 lmt = extract_score_lmt(score); - if (count + delta_count > kUserDictMaxFrequency || - count + delta_count < count) { - delta_count = kUserDictMaxFrequency - count; - } - count += delta_count; - dict_info_.total_nfreq += delta_count; - if (selected) { - lmt = time(NULL); - } - scores_[off] = build_score(lmt, count); - if (state_ < USER_DICT_SCORE_DIRTY) - state_ = USER_DICT_SCORE_DIRTY; -#ifdef ___DEBUG_PERF___ - DEBUG_PERF_END; - LOGD_PERF("update_lemma"); -#endif -#ifdef ___SYNC_ENABLED___ - queue_lemma_for_sync(ids_[off]); -#endif - return ids_[off]; - } - return 0; -} - -size_t UserDict::get_total_lemma_count() { - return dict_info_.total_nfreq; -} - -void UserDict::set_total_lemma_count_of_others(size_t count) { - total_other_nfreq_ = count; -} - -LemmaIdType UserDict::append_a_lemma(char16 lemma_str[], uint16 splids[], - uint16 lemma_len, uint16 count, uint64 lmt) { - LemmaIdType id = get_max_lemma_id() + 1; - size_t offset = dict_info_.lemma_size; - if (offset > kUserDictOffsetMask) - return 0; - - lemmas_[offset] = 0; - lemmas_[offset + 1] = (uint8)lemma_len; - for (size_t i = 0; i < lemma_len; i++) { - *((uint16*)&lemmas_[offset + 2 + (i << 1)]) = splids[i]; - *((char16*)&lemmas_[offset + 2 + (lemma_len << 1) + (i << 1)]) - = lemma_str[i]; - } - uint32 off = dict_info_.lemma_count; - offsets_[off] = offset; - scores_[off] = build_score(lmt, count); - ids_[off] = id; -#ifdef ___PREDICT_ENABLED___ - predicts_[off] = offset; -#endif - - offsets_by_id_[id - start_id_] = offset; - - dict_info_.lemma_count++; - dict_info_.lemma_size += (2 + (lemma_len << 2)); - lemma_count_left_--; - lemma_size_left_ -= (2 + (lemma_len << 2)); - - // Sort - - UserDictSearchable searchable; - prepare_locate(&searchable, splids, lemma_len); - - size_t i = 0; - while (i < off) { - offset = offsets_[i]; - uint32 nchar = get_lemma_nchar(offset); - uint16 * spl = get_lemma_spell_ids(offset); - - if (0 <= fuzzy_compare_spell_id(spl, nchar, &searchable)) - break; - i++; - } - if (i != off) { - uint32 temp = offsets_[off]; - memmove(offsets_ + i + 1, offsets_ + i, (off - i) << 2); - offsets_[i] = temp; - - temp = scores_[off]; - memmove(scores_ + i + 1, scores_ + i, (off - i) << 2); - scores_[i] = temp; - - temp = ids_[off]; - memmove(ids_ + i + 1, ids_ + i, (off - i) << 2); - ids_[i] = temp; - } - -#ifdef ___PREDICT_ENABLED___ - uint32 j = 0; - uint16 * words_new = get_lemma_word(predicts_[off]); - j = locate_where_to_insert_in_predicts(words_new, lemma_len); - if (j != off) { - uint32 temp = predicts_[off]; - memmove(predicts_ + j + 1, predicts_ + j, (off - j) << 2); - predicts_[j] = temp; - } -#endif - - if (state_ < USER_DICT_LEMMA_DIRTY) - state_ = USER_DICT_LEMMA_DIRTY; - -#ifdef ___CACHE_ENABLED___ - cache_init(); -#endif - - dict_info_.total_nfreq += count; - return id; -} -} diff --git a/PinyinIME/jni/share/utf16char.cpp b/PinyinIME/jni/share/utf16char.cpp deleted file mode 100644 index fadb6cf..0000000 --- a/PinyinIME/jni/share/utf16char.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "../include/utf16char.h" - -namespace ime_pinyin { - -#ifdef __cplusplus -extern "C" { -#endif - - char16* utf16_strtok(char16 *utf16_str, size_t *token_size, - char16 **utf16_str_next) { - if (NULL == utf16_str || NULL == token_size || NULL == utf16_str_next) { - return NULL; - } - - // Skip the splitters - size_t pos = 0; - while ((char16)' ' == utf16_str[pos] || (char16)'\n' == utf16_str[pos] - || (char16)'\t' == utf16_str[pos]) - pos++; - - utf16_str += pos; - pos = 0; - - while ((char16)'\0' != utf16_str[pos] && (char16)' ' != utf16_str[pos] - && (char16)'\n' != utf16_str[pos] - && (char16)'\t' != utf16_str[pos]) { - pos++; - } - - char16 *ret_val = utf16_str; - if ((char16)'\0' == utf16_str[pos]) { - *utf16_str_next = NULL; - if (0 == pos) - return NULL; - } else { - *utf16_str_next = utf16_str + pos + 1; - } - - utf16_str[pos] = (char16)'\0'; - *token_size = pos; - - return ret_val; - } - - int utf16_atoi(const char16 *utf16_str) { - if (NULL == utf16_str) - return 0; - - int value = 0; - int sign = 1; - size_t pos = 0; - - if ((char16)'-' == utf16_str[pos]) { - sign = -1; - pos++; - } - - while ((char16)'0' <= utf16_str[pos] && - (char16)'9' >= utf16_str[pos]) { - value = value * 10 + static_cast(utf16_str[pos] - (char16)'0'); - pos++; - } - - return value*sign; - } - - float utf16_atof(const char16 *utf16_str) { - // A temporary implemetation. - char char8[256]; - if (utf16_strlen(utf16_str) >= 256) return 0; - - utf16_strcpy_tochar(char8, utf16_str); - return atof(char8); - } - - size_t utf16_strlen(const char16 *utf16_str) { - if (NULL == utf16_str) - return 0; - - size_t size = 0; - while ((char16)'\0' != utf16_str[size]) - size++; - return size; - } - - int utf16_strcmp(const char16* str1, const char16* str2) { - size_t pos = 0; - while (str1[pos] == str2[pos] && (char16)'\0' != str1[pos]) - pos++; - - return static_cast(str1[pos]) - static_cast(str2[pos]); - } - - int utf16_strncmp(const char16 *str1, const char16 *str2, size_t size) { - size_t pos = 0; - while (pos < size && str1[pos] == str2[pos] && (char16)'\0' != str1[pos]) - pos++; - - if (pos == size) - return 0; - - return static_cast(str1[pos]) - static_cast(str2[pos]); - } - - // we do not consider overlapping - char16* utf16_strcpy(char16 *dst, const char16 *src) { - if (NULL == src || NULL == dst) - return NULL; - - char16* cp = dst; - - while ((char16)'\0' != *src) { - *cp = *src; - cp++; - src++; - } - - *cp = *src; - - return dst; - } - - char16* utf16_strncpy(char16 *dst, const char16 *src, size_t size) { - if (NULL == src || NULL == dst || 0 == size) - return NULL; - - if (src == dst) - return dst; - - char16* cp = dst; - - if (dst < src || (dst > src && dst >= src + size)) { - while (size-- && (*cp++ = *src++)) - ; - } else { - cp += size - 1; - src += size - 1; - while (size-- && (*cp-- == *src--)) - ; - } - return dst; - } - - // We do not handle complicated cases like overlapping, because in this - // codebase, it is not necessary. - char* utf16_strcpy_tochar(char *dst, const char16 *src) { - if (NULL == src || NULL == dst) - return NULL; - - char* cp = dst; - - while ((char16)'\0' != *src) { - *cp = static_cast(*src); - cp++; - src++; - } - *cp = *src; - - return dst; - } - -#ifdef __cplusplus -} -#endif -} // namespace ime_pinyin diff --git a/PinyinIME/jni/share/utf16reader.cpp b/PinyinIME/jni/share/utf16reader.cpp deleted file mode 100644 index d8e5de5..0000000 --- a/PinyinIME/jni/share/utf16reader.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../include/utf16reader.h" - -namespace ime_pinyin { - -#define MIN_BUF_LEN 128 -#define MAX_BUF_LEN 65535 - -Utf16Reader::Utf16Reader() { - fp_ = NULL; - buffer_ = NULL; - buffer_total_len_ = 0; - buffer_next_pos_ = 0; - buffer_valid_len_ = 0; -} - -Utf16Reader::~Utf16Reader() { - if (NULL != fp_) - fclose(fp_); - - if (NULL != buffer_) - delete [] buffer_; -} - - -bool Utf16Reader::open(const char* filename, size_t buffer_len) { - if (filename == NULL) - return false; - - if (buffer_len < MIN_BUF_LEN) - buffer_len = MIN_BUF_LEN; - else if (buffer_len > MAX_BUF_LEN) - buffer_len = MAX_BUF_LEN; - - buffer_total_len_ = buffer_len; - - if (NULL != buffer_) - delete [] buffer_; - buffer_ = new char16[buffer_total_len_]; - if (NULL == buffer_) - return false; - - if ((fp_ = fopen(filename, "rb")) == NULL) - return false; - - // the UTF16 file header, skip - char16 header; - if (fread(&header, sizeof(header), 1, fp_) != 1 || header != 0xfeff) { - fclose(fp_); - fp_ = NULL; - return false; - } - - return true; -} - -char16* Utf16Reader::readline(char16* read_buf, size_t max_len) { - if (NULL == fp_ || NULL == read_buf || 0 == max_len) - return NULL; - - size_t ret_len = 0; - - do { - if (buffer_valid_len_ == 0) { - buffer_next_pos_ = 0; - buffer_valid_len_ = fread(buffer_, sizeof(char16), - buffer_total_len_, fp_); - if (buffer_valid_len_ == 0) { - if (0 == ret_len) - return NULL; - read_buf[ret_len] = (char16)'\0'; - return read_buf; - } - } - - for (size_t i = 0; i < buffer_valid_len_; i++) { - if (i == max_len - 1 || - buffer_[buffer_next_pos_ + i] == (char16)'\n') { - if (ret_len + i > 0 && read_buf[ret_len + i - 1] == (char16)'\r') { - read_buf[ret_len + i - 1] = (char16)'\0'; - } else { - read_buf[ret_len + i] = (char16)'\0'; - } - - i++; - buffer_next_pos_ += i; - buffer_valid_len_ -= i; - if (buffer_next_pos_ == buffer_total_len_) { - buffer_next_pos_ = 0; - buffer_valid_len_ = 0; - } - return read_buf; - } else { - read_buf[ret_len + i] = buffer_[buffer_next_pos_ + i]; - } - } - - ret_len += buffer_valid_len_; - buffer_valid_len_ = 0; - } while (true); - - // Never reach here - return NULL; -} - -bool Utf16Reader::close() { - if (NULL != fp_) - fclose(fp_); - fp_ = NULL; - - if (NULL != buffer_) - delete [] buffer_; - buffer_ = NULL; - return true; -} -} // namespace ime_pinyin diff --git a/PinyinIME/lib/Android.mk b/PinyinIME/lib/Android.mk deleted file mode 100644 index f41193e..0000000 --- a/PinyinIME/lib/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - $(call all-subdir-java-files) \ - com/android/inputmethod/pinyin/IPinyinDecoderService.aidl - -LOCAL_MODULE := com.android.inputmethod.pinyin.lib - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/PinyinIME/lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl b/PinyinIME/lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl deleted file mode 100644 index 59dd6cc..0000000 --- a/PinyinIME/lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl +++ /dev/null @@ -1,51 +0,0 @@ -/* //com/andriod/inputmethod/pinyin/IPinyinDecoderService.aidl - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.inputmethod.pinyin; - -interface IPinyinDecoderService { - int getInt(); - void setMaxLens(int maxSpsLen, int maxHzsLen); - int imSearch(in byte[] pyBuf, int pyLen); - int imDelSearch(int pos, boolean is_pos_in_splid, boolean clear_fixed_this_step); - void imResetSearch(); - int imAddLetter(byte ch); - String imGetPyStr(boolean decoded); - int imGetPyStrLen(boolean decoded); - int[] imGetSplStart(); - String imGetChoice(int choiceId); - String imGetChoices(int choicesNum); - List imGetChoiceList(int choicesStart, int choicesNum, int sentFixedLen); - int imChoose(int choiceId); - int imCancelLastChoice(); - int imGetFixedLen(); - boolean imCancelInput(); - void imFlushCache(); - int imGetPredictsNum(in String fixedStr); - List imGetPredictList(int predictsStart, int predictsNum); - String imGetPredictItem(int predictNo); - - String syncUserDict(in String tomerge); - boolean syncBegin(); - void syncFinish(); - int syncPutLemmas(in String tomerge); - String syncGetLemmas(); - int syncGetLastCount(); - int syncGetTotalCount(); - void syncClearLastGot(); - int imSyncGetCapacity(); -} diff --git a/PinyinIME/res/drawable/app_icon.png b/PinyinIME/res/drawable/app_icon.png deleted file mode 100644 index 0c6eff3..0000000 Binary files a/PinyinIME/res/drawable/app_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/arrow_bg.xml b/PinyinIME/res/drawable/arrow_bg.xml deleted file mode 100644 index 9125232..0000000 --- a/PinyinIME/res/drawable/arrow_bg.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - diff --git a/PinyinIME/res/drawable/arrow_left.png b/PinyinIME/res/drawable/arrow_left.png deleted file mode 100644 index 75b8daa..0000000 Binary files a/PinyinIME/res/drawable/arrow_left.png and /dev/null differ diff --git a/PinyinIME/res/drawable/arrow_right.png b/PinyinIME/res/drawable/arrow_right.png deleted file mode 100644 index e4e6cd7..0000000 Binary files a/PinyinIME/res/drawable/arrow_right.png and /dev/null differ diff --git a/PinyinIME/res/drawable/candidate_balloon_bg.9.png b/PinyinIME/res/drawable/candidate_balloon_bg.9.png deleted file mode 100644 index ddc26f6..0000000 Binary files a/PinyinIME/res/drawable/candidate_balloon_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/candidate_hl_bg.9.png b/PinyinIME/res/drawable/candidate_hl_bg.9.png deleted file mode 100644 index 83d40a0..0000000 Binary files a/PinyinIME/res/drawable/candidate_hl_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/candidates_area_bg.9.png b/PinyinIME/res/drawable/candidates_area_bg.9.png deleted file mode 100644 index 3ff63f4..0000000 Binary files a/PinyinIME/res/drawable/candidates_area_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/candidates_vertical_line.png b/PinyinIME/res/drawable/candidates_vertical_line.png deleted file mode 100644 index 1edd335..0000000 Binary files a/PinyinIME/res/drawable/candidates_vertical_line.png and /dev/null differ diff --git a/PinyinIME/res/drawable/cands_container_bg.9.png b/PinyinIME/res/drawable/cands_container_bg.9.png deleted file mode 100644 index 837ba9b..0000000 Binary files a/PinyinIME/res/drawable/cands_container_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/comma_full_icon.png b/PinyinIME/res/drawable/comma_full_icon.png deleted file mode 100644 index 142fe1e..0000000 Binary files a/PinyinIME/res/drawable/comma_full_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/comma_full_popup_icon.png b/PinyinIME/res/drawable/comma_full_popup_icon.png deleted file mode 100644 index f63a5b5..0000000 Binary files a/PinyinIME/res/drawable/comma_full_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/composing_area_bg.9.png b/PinyinIME/res/drawable/composing_area_bg.9.png deleted file mode 100644 index 31f6580..0000000 Binary files a/PinyinIME/res/drawable/composing_area_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/composing_area_cursor.png b/PinyinIME/res/drawable/composing_area_cursor.png deleted file mode 100644 index ea3bbca..0000000 Binary files a/PinyinIME/res/drawable/composing_area_cursor.png and /dev/null differ diff --git a/PinyinIME/res/drawable/composing_hl_bg.9.png b/PinyinIME/res/drawable/composing_hl_bg.9.png deleted file mode 100644 index b84719e..0000000 Binary files a/PinyinIME/res/drawable/composing_hl_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/delete_icon.png b/PinyinIME/res/drawable/delete_icon.png deleted file mode 100644 index f1f7c58..0000000 Binary files a/PinyinIME/res/drawable/delete_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/delete_popup_icon.png b/PinyinIME/res/drawable/delete_popup_icon.png deleted file mode 100644 index 3c90839..0000000 Binary files a/PinyinIME/res/drawable/delete_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/dun_icon.png b/PinyinIME/res/drawable/dun_icon.png deleted file mode 100644 index eaa6aa1..0000000 Binary files a/PinyinIME/res/drawable/dun_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/dun_popup_icon.png b/PinyinIME/res/drawable/dun_popup_icon.png deleted file mode 100644 index 5b7eb6a..0000000 Binary files a/PinyinIME/res/drawable/dun_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_00.png b/PinyinIME/res/drawable/emotion_icon_00.png deleted file mode 100644 index 33f2945..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_00.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_00_popup.png b/PinyinIME/res/drawable/emotion_icon_00_popup.png deleted file mode 100644 index 1aaebdd..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_00_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_01.png b/PinyinIME/res/drawable/emotion_icon_01.png deleted file mode 100644 index 9165e2a..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_01.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_01_popup.png b/PinyinIME/res/drawable/emotion_icon_01_popup.png deleted file mode 100644 index baeebdc..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_01_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_02.png b/PinyinIME/res/drawable/emotion_icon_02.png deleted file mode 100644 index e967603..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_02.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_02_popup.png b/PinyinIME/res/drawable/emotion_icon_02_popup.png deleted file mode 100644 index 3a27346..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_02_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_03.png b/PinyinIME/res/drawable/emotion_icon_03.png deleted file mode 100644 index 3fc2585..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_03.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_03_popup.png b/PinyinIME/res/drawable/emotion_icon_03_popup.png deleted file mode 100644 index 2567632..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_03_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_04.png b/PinyinIME/res/drawable/emotion_icon_04.png deleted file mode 100644 index 0e2d459..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_04.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_04_popup.png b/PinyinIME/res/drawable/emotion_icon_04_popup.png deleted file mode 100644 index 61f2d21..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_04_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_05.png b/PinyinIME/res/drawable/emotion_icon_05.png deleted file mode 100644 index 29ac101..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_05.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_05_popup.png b/PinyinIME/res/drawable/emotion_icon_05_popup.png deleted file mode 100644 index 48fd0bc..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_05_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_06.png b/PinyinIME/res/drawable/emotion_icon_06.png deleted file mode 100644 index 02f47c6..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_06.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_06_popup.png b/PinyinIME/res/drawable/emotion_icon_06_popup.png deleted file mode 100644 index 5508cd2..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_06_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_10.png b/PinyinIME/res/drawable/emotion_icon_10.png deleted file mode 100644 index d9a0582..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_10.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_10_popup.png b/PinyinIME/res/drawable/emotion_icon_10_popup.png deleted file mode 100644 index 32dd180..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_10_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_11.png b/PinyinIME/res/drawable/emotion_icon_11.png deleted file mode 100644 index d1ca0a8..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_11.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_11_popup.png b/PinyinIME/res/drawable/emotion_icon_11_popup.png deleted file mode 100644 index 5e42d97..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_11_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_12.png b/PinyinIME/res/drawable/emotion_icon_12.png deleted file mode 100644 index ee88ef8..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_12.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_12_popup.png b/PinyinIME/res/drawable/emotion_icon_12_popup.png deleted file mode 100644 index 195600d..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_12_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_13.png b/PinyinIME/res/drawable/emotion_icon_13.png deleted file mode 100644 index 70b7334..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_13.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_13_popup.png b/PinyinIME/res/drawable/emotion_icon_13_popup.png deleted file mode 100644 index 1df73ec..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_13_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_14.png b/PinyinIME/res/drawable/emotion_icon_14.png deleted file mode 100644 index 852b7c1..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_14.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_14_popup.png b/PinyinIME/res/drawable/emotion_icon_14_popup.png deleted file mode 100644 index 800a240..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_14_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_15.png b/PinyinIME/res/drawable/emotion_icon_15.png deleted file mode 100644 index cfbbe16..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_15.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_15_popup.png b/PinyinIME/res/drawable/emotion_icon_15_popup.png deleted file mode 100644 index 7f175f5..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_15_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_16.png b/PinyinIME/res/drawable/emotion_icon_16.png deleted file mode 100644 index 52368c0..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_16.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_16_popup.png b/PinyinIME/res/drawable/emotion_icon_16_popup.png deleted file mode 100644 index 2a5b815..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_16_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_20.png b/PinyinIME/res/drawable/emotion_icon_20.png deleted file mode 100644 index 2681132..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_20.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_20_popup.png b/PinyinIME/res/drawable/emotion_icon_20_popup.png deleted file mode 100644 index 8887619..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_20_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_21.png b/PinyinIME/res/drawable/emotion_icon_21.png deleted file mode 100644 index ed425d8..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_21.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_21_popup.png b/PinyinIME/res/drawable/emotion_icon_21_popup.png deleted file mode 100644 index 48afe8a..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_21_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_22.png b/PinyinIME/res/drawable/emotion_icon_22.png deleted file mode 100644 index 88d37ec..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_22.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_22_popup.png b/PinyinIME/res/drawable/emotion_icon_22_popup.png deleted file mode 100644 index cb08d1b..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_22_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_23.png b/PinyinIME/res/drawable/emotion_icon_23.png deleted file mode 100644 index 75f592b..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_23.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_23_popup.png b/PinyinIME/res/drawable/emotion_icon_23_popup.png deleted file mode 100644 index e3ecfc3..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_23_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_24.png b/PinyinIME/res/drawable/emotion_icon_24.png deleted file mode 100644 index fdadf91..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_24.png and /dev/null differ diff --git a/PinyinIME/res/drawable/emotion_icon_24_popup.png b/PinyinIME/res/drawable/emotion_icon_24_popup.png deleted file mode 100644 index 1766a5d..0000000 Binary files a/PinyinIME/res/drawable/emotion_icon_24_popup.png and /dev/null differ diff --git a/PinyinIME/res/drawable/enter_icon.png b/PinyinIME/res/drawable/enter_icon.png deleted file mode 100644 index 17f2574..0000000 Binary files a/PinyinIME/res/drawable/enter_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/enter_popup_icon.png b/PinyinIME/res/drawable/enter_popup_icon.png deleted file mode 100644 index 03d9c9b..0000000 Binary files a/PinyinIME/res/drawable/enter_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/ime_en.png b/PinyinIME/res/drawable/ime_en.png deleted file mode 100644 index 652d7d7..0000000 Binary files a/PinyinIME/res/drawable/ime_en.png and /dev/null differ diff --git a/PinyinIME/res/drawable/ime_pinyin.png b/PinyinIME/res/drawable/ime_pinyin.png deleted file mode 100644 index b398d89..0000000 Binary files a/PinyinIME/res/drawable/ime_pinyin.png and /dev/null differ diff --git a/PinyinIME/res/drawable/key_balloon_bg.9.png b/PinyinIME/res/drawable/key_balloon_bg.9.png deleted file mode 100644 index 1789b34..0000000 Binary files a/PinyinIME/res/drawable/key_balloon_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/light_key_bg.9.png b/PinyinIME/res/drawable/light_key_bg.9.png deleted file mode 100644 index bda9b83..0000000 Binary files a/PinyinIME/res/drawable/light_key_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/light_key_hl_bg.9.png b/PinyinIME/res/drawable/light_key_hl_bg.9.png deleted file mode 100644 index bdcf06e..0000000 Binary files a/PinyinIME/res/drawable/light_key_hl_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/light_key_up_bg.9.png b/PinyinIME/res/drawable/light_key_up_bg.9.png deleted file mode 100644 index 0c16ed5..0000000 Binary files a/PinyinIME/res/drawable/light_key_up_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/light_key_up_hl_bg.9.png b/PinyinIME/res/drawable/light_key_up_hl_bg.9.png deleted file mode 100644 index 79621a9..0000000 Binary files a/PinyinIME/res/drawable/light_key_up_hl_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/miniskb_bg.9.png b/PinyinIME/res/drawable/miniskb_bg.9.png deleted file mode 100644 index 74fddc8..0000000 Binary files a/PinyinIME/res/drawable/miniskb_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/normal_key_bg.9.png b/PinyinIME/res/drawable/normal_key_bg.9.png deleted file mode 100644 index 7ba18dd..0000000 Binary files a/PinyinIME/res/drawable/normal_key_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/normal_key_hl_bg.9.png b/PinyinIME/res/drawable/normal_key_hl_bg.9.png deleted file mode 100644 index 39b9314..0000000 Binary files a/PinyinIME/res/drawable/normal_key_hl_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num0.png b/PinyinIME/res/drawable/num0.png deleted file mode 100644 index e7007c8..0000000 Binary files a/PinyinIME/res/drawable/num0.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num1.png b/PinyinIME/res/drawable/num1.png deleted file mode 100644 index aaac11b..0000000 Binary files a/PinyinIME/res/drawable/num1.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num2.png b/PinyinIME/res/drawable/num2.png deleted file mode 100644 index 4372eb8..0000000 Binary files a/PinyinIME/res/drawable/num2.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num3.png b/PinyinIME/res/drawable/num3.png deleted file mode 100644 index 6f54c85..0000000 Binary files a/PinyinIME/res/drawable/num3.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num4.png b/PinyinIME/res/drawable/num4.png deleted file mode 100644 index 3e50bb9..0000000 Binary files a/PinyinIME/res/drawable/num4.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num5.png b/PinyinIME/res/drawable/num5.png deleted file mode 100644 index c39ef44..0000000 Binary files a/PinyinIME/res/drawable/num5.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num6.png b/PinyinIME/res/drawable/num6.png deleted file mode 100644 index ea88ceb..0000000 Binary files a/PinyinIME/res/drawable/num6.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num7.png b/PinyinIME/res/drawable/num7.png deleted file mode 100644 index 4d75583..0000000 Binary files a/PinyinIME/res/drawable/num7.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num8.png b/PinyinIME/res/drawable/num8.png deleted file mode 100644 index 1a8ff94..0000000 Binary files a/PinyinIME/res/drawable/num8.png and /dev/null differ diff --git a/PinyinIME/res/drawable/num9.png b/PinyinIME/res/drawable/num9.png deleted file mode 100644 index 8b344c0..0000000 Binary files a/PinyinIME/res/drawable/num9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/numalt.png b/PinyinIME/res/drawable/numalt.png deleted file mode 100644 index 32a2cf3..0000000 Binary files a/PinyinIME/res/drawable/numalt.png and /dev/null differ diff --git a/PinyinIME/res/drawable/numpound.png b/PinyinIME/res/drawable/numpound.png deleted file mode 100644 index b2419d9..0000000 Binary files a/PinyinIME/res/drawable/numpound.png and /dev/null differ diff --git a/PinyinIME/res/drawable/numstar.png b/PinyinIME/res/drawable/numstar.png deleted file mode 100644 index cb66f96..0000000 Binary files a/PinyinIME/res/drawable/numstar.png and /dev/null differ diff --git a/PinyinIME/res/drawable/period_full_icon.png b/PinyinIME/res/drawable/period_full_icon.png deleted file mode 100644 index 1d7c6c7..0000000 Binary files a/PinyinIME/res/drawable/period_full_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/period_full_popup_icon.png b/PinyinIME/res/drawable/period_full_popup_icon.png deleted file mode 100644 index 07ee288..0000000 Binary files a/PinyinIME/res/drawable/period_full_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/period_icon.png b/PinyinIME/res/drawable/period_icon.png deleted file mode 100644 index 6a8bd86..0000000 Binary files a/PinyinIME/res/drawable/period_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/period_popup_icon.png b/PinyinIME/res/drawable/period_popup_icon.png deleted file mode 100644 index 3e18e03..0000000 Binary files a/PinyinIME/res/drawable/period_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/search_icon.png b/PinyinIME/res/drawable/search_icon.png deleted file mode 100644 index 127755d..0000000 Binary files a/PinyinIME/res/drawable/search_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/search_popup_icon.png b/PinyinIME/res/drawable/search_popup_icon.png deleted file mode 100644 index f4af341..0000000 Binary files a/PinyinIME/res/drawable/search_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/shift_off_icon.png b/PinyinIME/res/drawable/shift_off_icon.png deleted file mode 100644 index 0566e5a..0000000 Binary files a/PinyinIME/res/drawable/shift_off_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/shift_off_popup_icon.png b/PinyinIME/res/drawable/shift_off_popup_icon.png deleted file mode 100644 index 97f4661..0000000 Binary files a/PinyinIME/res/drawable/shift_off_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/shift_on_icon.png b/PinyinIME/res/drawable/shift_on_icon.png deleted file mode 100644 index ccaf05d..0000000 Binary files a/PinyinIME/res/drawable/shift_on_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/shift_on_popup_icon.png b/PinyinIME/res/drawable/shift_on_popup_icon.png deleted file mode 100644 index 7194b30..0000000 Binary files a/PinyinIME/res/drawable/shift_on_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/skb_bg.png b/PinyinIME/res/drawable/skb_bg.png deleted file mode 100644 index f582462..0000000 Binary files a/PinyinIME/res/drawable/skb_bg.png and /dev/null differ diff --git a/PinyinIME/res/drawable/skb_container_bg.9.png b/PinyinIME/res/drawable/skb_container_bg.9.png deleted file mode 100644 index d6d7537..0000000 Binary files a/PinyinIME/res/drawable/skb_container_bg.9.png and /dev/null differ diff --git a/PinyinIME/res/drawable/smiley_icon.png b/PinyinIME/res/drawable/smiley_icon.png deleted file mode 100644 index daa2dc1..0000000 Binary files a/PinyinIME/res/drawable/smiley_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/smiley_popup_icon.png b/PinyinIME/res/drawable/smiley_popup_icon.png deleted file mode 100644 index 5d50370..0000000 Binary files a/PinyinIME/res/drawable/smiley_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/space_icon.png b/PinyinIME/res/drawable/space_icon.png deleted file mode 100644 index 4e6273b..0000000 Binary files a/PinyinIME/res/drawable/space_icon.png and /dev/null differ diff --git a/PinyinIME/res/drawable/space_popup_icon.png b/PinyinIME/res/drawable/space_popup_icon.png deleted file mode 100644 index 739db68..0000000 Binary files a/PinyinIME/res/drawable/space_popup_icon.png and /dev/null differ diff --git a/PinyinIME/res/layout/candidates_container.xml b/PinyinIME/res/layout/candidates_container.xml deleted file mode 100644 index eda7e28..0000000 --- a/PinyinIME/res/layout/candidates_container.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - diff --git a/PinyinIME/res/layout/floating_container.xml b/PinyinIME/res/layout/floating_container.xml deleted file mode 100644 index ad9f3fd..0000000 --- a/PinyinIME/res/layout/floating_container.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - diff --git a/PinyinIME/res/layout/skb_container.xml b/PinyinIME/res/layout/skb_container.xml deleted file mode 100644 index 5a20aab..0000000 --- a/PinyinIME/res/layout/skb_container.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - diff --git a/PinyinIME/res/raw/dict_pinyin.dat b/PinyinIME/res/raw/dict_pinyin.dat deleted file mode 100644 index 1be3f9c..0000000 Binary files a/PinyinIME/res/raw/dict_pinyin.dat and /dev/null differ diff --git a/PinyinIME/res/values-land/dimens.xml b/PinyinIME/res/values-land/dimens.xml deleted file mode 100644 index 3ee7751..0000000 --- a/PinyinIME/res/values-land/dimens.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - 10dip - 16dip - diff --git a/PinyinIME/res/values-port/dimens.xml b/PinyinIME/res/values-port/dimens.xml deleted file mode 100644 index 47a3a53..0000000 --- a/PinyinIME/res/values-port/dimens.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - 3dip - 18dip - diff --git a/PinyinIME/res/values-zh/bools.xml b/PinyinIME/res/values-zh/bools.xml deleted file mode 100644 index c3291be..0000000 --- a/PinyinIME/res/values-zh/bools.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - true - diff --git a/PinyinIME/res/values/colors.xml b/PinyinIME/res/values/colors.xml deleted file mode 100644 index e66a1ce..0000000 --- a/PinyinIME/res/values/colors.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - #ffffffff - #ffffffff - #ff000000 - #ff000000 - #ff000000 - #ffe35900 - #ff343233 - #ff000000 - #ffffffff - #ff777777 - diff --git a/PinyinIME/res/values/dimens.xml b/PinyinIME/res/values/dimens.xml deleted file mode 100644 index 0c8519e..0000000 --- a/PinyinIME/res/values/dimens.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - 0dip - diff --git a/PinyinIME/res/values/strings.xml b/PinyinIME/res/values/strings.xml deleted file mode 100644 index 09d64db..0000000 --- a/PinyinIME/res/values/strings.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - 谷歌拼音输入法 - 谷歌拼音输入法设置 - - 1.0.0 - - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 3 - - - setting_sound_key - setting_vibrate_key - setting_prediction_key - setting_switch_key - setting_advanced_key - - 谷歌拼音输入法设置 - 按键声音 - 按键震动 - 联想输入 - 中英文切换 - Shift-space - - 开启 - 关闭 - - 其它设置 - 词典同步等 - - diff --git a/PinyinIME/res/xml/method.xml b/PinyinIME/res/xml/method.xml deleted file mode 100644 index 2ff16b8..0000000 --- a/PinyinIME/res/xml/method.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/PinyinIME/res/xml/settings.xml b/PinyinIME/res/xml/settings.xml deleted file mode 100644 index b46e621..0000000 --- a/PinyinIME/res/xml/settings.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/res/xml/skb_phone.xml b/PinyinIME/res/xml/skb_phone.xml deleted file mode 100644 index 80a44fb..0000000 --- a/PinyinIME/res/xml/skb_phone.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/res/xml/skb_qwerty.xml b/PinyinIME/res/xml/skb_qwerty.xml deleted file mode 100644 index 57b8838..0000000 --- a/PinyinIME/res/xml/skb_qwerty.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/res/xml/skb_smiley.xml b/PinyinIME/res/xml/skb_smiley.xml deleted file mode 100644 index ec48b12..0000000 --- a/PinyinIME/res/xml/skb_smiley.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/res/xml/skb_sym1.xml b/PinyinIME/res/xml/skb_sym1.xml deleted file mode 100644 index 918fd33..0000000 --- a/PinyinIME/res/xml/skb_sym1.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/res/xml/skb_sym2.xml b/PinyinIME/res/xml/skb_sym2.xml deleted file mode 100644 index a55f91e..0000000 --- a/PinyinIME/res/xml/skb_sym2.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/res/xml/skb_template1.xml b/PinyinIME/res/xml/skb_template1.xml deleted file mode 100644 index 16338eb..0000000 --- a/PinyinIME/res/xml/skb_template1.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/BalloonHint.java b/PinyinIME/src/com/android/inputmethod/pinyin/BalloonHint.java deleted file mode 100644 index 919dbf1..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/BalloonHint.java +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Paint.FontMetricsInt; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.view.Gravity; -import android.view.View; -import android.view.View.MeasureSpec; -import android.widget.PopupWindow; - -/** - * Subclass of PopupWindow used as the feedback when user presses on a soft key - * or a candidate. - */ -public class BalloonHint extends PopupWindow { - /** - * Delayed time to show the balloon hint. - */ - public static final int TIME_DELAY_SHOW = 0; - - /** - * Delayed time to dismiss the balloon hint. - */ - public static final int TIME_DELAY_DISMISS = 200; - - /** - * The padding information of the balloon. Because PopupWindow's background - * can not be changed unless it is dismissed and shown again, we set the - * real background drawable to the content view, and make the PopupWindow's - * background transparent. So actually this padding information is for the - * content view. - */ - private Rect mPaddingRect = new Rect(); - - /** - * The context used to create this balloon hint object. - */ - private Context mContext; - - /** - * Parent used to show the balloon window. - */ - private View mParent; - - /** - * The content view of the balloon. - */ - BalloonView mBalloonView; - - /** - * The measuring specification used to determine its size. Key-press - * balloons and candidates balloons have different measuring specifications. - */ - private int mMeasureSpecMode; - - /** - * Used to indicate whether the balloon needs to be dismissed forcibly. - */ - private boolean mForceDismiss; - - /** - * Timer used to show/dismiss the balloon window with some time delay. - */ - private BalloonTimer mBalloonTimer; - - private int mParentLocationInWindow[] = new int[2]; - - public BalloonHint(Context context, View parent, int measureSpecMode) { - super(context); - mParent = parent; - mMeasureSpecMode = measureSpecMode; - - setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - setTouchable(false); - setBackgroundDrawable(new ColorDrawable(0)); - - mBalloonView = new BalloonView(context); - mBalloonView.setClickable(false); - setContentView(mBalloonView); - - mBalloonTimer = new BalloonTimer(); - } - - public Context getContext() { - return mContext; - } - - public Rect getPadding() { - return mPaddingRect; - } - - public void setBalloonBackground(Drawable drawable) { - // We usually pick up a background from a soft keyboard template, - // and the object may has been set to this balloon before. - if (mBalloonView.getBackground() == drawable) return; - mBalloonView.setBackgroundDrawable(drawable); - - if (null != drawable) { - drawable.getPadding(mPaddingRect); - } else { - mPaddingRect.set(0, 0, 0, 0); - } - } - - /** - * Set configurations to show text label in this balloon. - * - * @param label The text label to show in the balloon. - * @param textSize The text size used to show label. - * @param textBold Used to indicate whether the label should be bold. - * @param textColor The text color used to show label. - * @param width The desired width of the balloon. The real width is - * determined by the desired width and balloon's measuring - * specification. - * @param height The desired width of the balloon. The real width is - * determined by the desired width and balloon's measuring - * specification. - */ - public void setBalloonConfig(String label, float textSize, - boolean textBold, int textColor, int width, int height) { - mBalloonView.setTextConfig(label, textSize, textBold, textColor); - setBalloonSize(width, height); - } - - /** - * Set configurations to show text label in this balloon. - * - * @param icon The icon used to shown in this balloon. - * @param width The desired width of the balloon. The real width is - * determined by the desired width and balloon's measuring - * specification. - * @param height The desired width of the balloon. The real width is - * determined by the desired width and balloon's measuring - * specification. - */ - public void setBalloonConfig(Drawable icon, int width, int height) { - mBalloonView.setIcon(icon); - setBalloonSize(width, height); - } - - - public boolean needForceDismiss() { - return mForceDismiss; - } - - public int getPaddingLeft() { - return mPaddingRect.left; - } - - public int getPaddingTop() { - return mPaddingRect.top; - } - - public int getPaddingRight() { - return mPaddingRect.right; - } - - public int getPaddingBottom() { - return mPaddingRect.bottom; - } - - public void delayedShow(long delay, int locationInParent[]) { - if (mBalloonTimer.isPending()) { - mBalloonTimer.removeTimer(); - } - if (delay <= 0) { - mParent.getLocationInWindow(mParentLocationInWindow); - showAtLocation(mParent, Gravity.LEFT | Gravity.TOP, - locationInParent[0], locationInParent[1] - + mParentLocationInWindow[1]); - } else { - mBalloonTimer.startTimer(delay, BalloonTimer.ACTION_SHOW, - locationInParent, -1, -1); - } - } - - public void delayedUpdate(long delay, int locationInParent[], - int width, int height) { - mBalloonView.invalidate(); - if (mBalloonTimer.isPending()) { - mBalloonTimer.removeTimer(); - } - if (delay <= 0) { - mParent.getLocationInWindow(mParentLocationInWindow); - update(locationInParent[0], locationInParent[1] - + mParentLocationInWindow[1], width, height); - } else { - mBalloonTimer.startTimer(delay, BalloonTimer.ACTION_UPDATE, - locationInParent, width, height); - } - } - - public void delayedDismiss(long delay) { - if (mBalloonTimer.isPending()) { - mBalloonTimer.removeTimer(); - int pendingAction = mBalloonTimer.getAction(); - if (0 != delay && BalloonTimer.ACTION_HIDE != pendingAction) { - mBalloonTimer.run(); - } - } - if (delay <= 0) { - dismiss(); - } else { - mBalloonTimer.startTimer(delay, BalloonTimer.ACTION_HIDE, null, -1, - -1); - } - } - - public void removeTimer() { - if (mBalloonTimer.isPending()) { - mBalloonTimer.removeTimer(); - } - } - - private void setBalloonSize(int width, int height) { - int widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, - mMeasureSpecMode); - int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, - mMeasureSpecMode); - mBalloonView.measure(widthMeasureSpec, heightMeasureSpec); - - int oldWidth = getWidth(); - int oldHeight = getHeight(); - int newWidth = mBalloonView.getMeasuredWidth() + getPaddingLeft() - + getPaddingRight(); - int newHeight = mBalloonView.getMeasuredHeight() + getPaddingTop() - + getPaddingBottom(); - setWidth(newWidth); - setHeight(newHeight); - - // If update() is called to update both size and position, the system - // will first MOVE the PopupWindow to the new position, and then - // perform a size-updating operation, so there will be a flash in - // PopupWindow if user presses a key and moves finger to next one whose - // size is different. - // PopupWindow will handle the updating issue in one go in the future, - // but before that, if we find the size is changed, a mandatory dismiss - // operation is required. In our UI design, normal QWERTY keys' width - // can be different in 1-pixel, and we do not dismiss the balloon when - // user move between QWERTY keys. - mForceDismiss = false; - if (isShowing()) { - mForceDismiss = oldWidth - newWidth > 1 || newWidth - oldWidth > 1; - } - } - - - private class BalloonTimer extends Handler implements Runnable { - public static final int ACTION_SHOW = 1; - public static final int ACTION_HIDE = 2; - public static final int ACTION_UPDATE = 3; - - /** - * The pending action. - */ - private int mAction; - - private int mPositionInParent[] = new int[2]; - private int mWidth; - private int mHeight; - - private boolean mTimerPending = false; - - public void startTimer(long time, int action, int positionInParent[], - int width, int height) { - mAction = action; - if (ACTION_HIDE != action) { - mPositionInParent[0] = positionInParent[0]; - mPositionInParent[1] = positionInParent[1]; - } - mWidth = width; - mHeight = height; - postDelayed(this, time); - mTimerPending = true; - } - - public boolean isPending() { - return mTimerPending; - } - - public boolean removeTimer() { - if (mTimerPending) { - mTimerPending = false; - removeCallbacks(this); - return true; - } - - return false; - } - - public int getAction() { - return mAction; - } - - public void run() { - switch (mAction) { - case ACTION_SHOW: - mParent.getLocationInWindow(mParentLocationInWindow); - showAtLocation(mParent, Gravity.LEFT | Gravity.TOP, - mPositionInParent[0], mPositionInParent[1] - + mParentLocationInWindow[1]); - break; - case ACTION_HIDE: - dismiss(); - break; - case ACTION_UPDATE: - mParent.getLocationInWindow(mParentLocationInWindow); - update(mPositionInParent[0], mPositionInParent[1] - + mParentLocationInWindow[1], mWidth, mHeight); - } - mTimerPending = false; - } - } - - private class BalloonView extends View { - /** - * Suspension points used to display long items. - */ - private static final String SUSPENSION_POINTS = "..."; - - /** - * The icon to be shown. If it is not null, {@link #mLabel} will be - * ignored. - */ - private Drawable mIcon; - - /** - * The label to be shown. It is enabled only if {@link #mIcon} is null. - */ - private String mLabel; - - private int mLabeColor = 0xff000000; - private Paint mPaintLabel; - private FontMetricsInt mFmi; - - /** - * The width to show suspension points. - */ - private float mSuspensionPointsWidth; - - - public BalloonView(Context context) { - super(context); - mPaintLabel = new Paint(); - mPaintLabel.setColor(mLabeColor); - mPaintLabel.setAntiAlias(true); - mPaintLabel.setFakeBoldText(true); - mFmi = mPaintLabel.getFontMetricsInt(); - } - - public void setIcon(Drawable icon) { - mIcon = icon; - } - - public void setTextConfig(String label, float fontSize, - boolean textBold, int textColor) { - // Icon should be cleared so that the label will be enabled. - mIcon = null; - mLabel = label; - mPaintLabel.setTextSize(fontSize); - mPaintLabel.setFakeBoldText(textBold); - mPaintLabel.setColor(textColor); - mFmi = mPaintLabel.getFontMetricsInt(); - mSuspensionPointsWidth = mPaintLabel.measureText(SUSPENSION_POINTS); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - final int widthSize = MeasureSpec.getSize(widthMeasureSpec); - final int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthMode == MeasureSpec.EXACTLY) { - setMeasuredDimension(widthSize, heightSize); - return; - } - - int measuredWidth = mPaddingLeft + mPaddingRight; - int measuredHeight = mPaddingTop + mPaddingBottom; - if (null != mIcon) { - measuredWidth += mIcon.getIntrinsicWidth(); - measuredHeight += mIcon.getIntrinsicHeight(); - } else if (null != mLabel) { - measuredWidth += (int) (mPaintLabel.measureText(mLabel)); - measuredHeight += mFmi.bottom - mFmi.top; - } - if (widthSize > measuredWidth || widthMode == MeasureSpec.AT_MOST) { - measuredWidth = widthSize; - } - - if (heightSize > measuredHeight - || heightMode == MeasureSpec.AT_MOST) { - measuredHeight = heightSize; - } - - int maxWidth = Environment.getInstance().getScreenWidth() - - mPaddingLeft - mPaddingRight; - if (measuredWidth > maxWidth) { - measuredWidth = maxWidth; - } - setMeasuredDimension(measuredWidth, measuredHeight); - } - - @Override - protected void onDraw(Canvas canvas) { - int width = getWidth(); - int height = getHeight(); - if (null != mIcon) { - int marginLeft = (width - mIcon.getIntrinsicWidth()) / 2; - int marginRight = width - mIcon.getIntrinsicWidth() - - marginLeft; - int marginTop = (height - mIcon.getIntrinsicHeight()) / 2; - int marginBottom = height - mIcon.getIntrinsicHeight() - - marginTop; - mIcon.setBounds(marginLeft, marginTop, width - marginRight, - height - marginBottom); - mIcon.draw(canvas); - } else if (null != mLabel) { - float labelMeasuredWidth = mPaintLabel.measureText(mLabel); - float x = mPaddingLeft; - x += (width - labelMeasuredWidth - mPaddingLeft - mPaddingRight) / 2.0f; - String labelToDraw = mLabel; - if (x < mPaddingLeft) { - x = mPaddingLeft; - labelToDraw = getLimitedLabelForDrawing(mLabel, - width - mPaddingLeft - mPaddingRight); - } - - int fontHeight = mFmi.bottom - mFmi.top; - float marginY = (height - fontHeight) / 2.0f; - float y = marginY - mFmi.top; - canvas.drawText(labelToDraw, x, y, mPaintLabel); - } - } - - private String getLimitedLabelForDrawing(String rawLabel, - float widthToDraw) { - int subLen = rawLabel.length(); - if (subLen <= 1) return rawLabel; - do { - subLen--; - float width = mPaintLabel.measureText(rawLabel, 0, subLen); - if (width + mSuspensionPointsWidth <= widthToDraw || 1 >= subLen) { - return rawLabel.substring(0, subLen) + - SUSPENSION_POINTS; - } - } while (true); - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/CandidateView.java b/PinyinIME/src/com/android/inputmethod/pinyin/CandidateView.java deleted file mode 100644 index 8dc1bf1..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/CandidateView.java +++ /dev/null @@ -1,760 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import com.android.inputmethod.pinyin.PinyinIME.DecodingInfo; - -import java.util.Vector; - -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.graphics.Paint.FontMetricsInt; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; - -/** - * View to show candidate list. There two candidate view instances which are - * used to show animation when user navigates between pages. - */ -public class CandidateView extends View { - /** - * The minimum width to show a item. - */ - private static final float MIN_ITEM_WIDTH = 22; - - /** - * Suspension points used to display long items. - */ - private static final String SUSPENSION_POINTS = "..."; - - /** - * The width to draw candidates. - */ - private int mContentWidth; - - /** - * The height to draw candidate content. - */ - private int mContentHeight; - - /** - * Whether footnotes are displayed. Footnote is shown when hardware keyboard - * is available. - */ - private boolean mShowFootnote = true; - - /** - * Balloon hint for candidate press/release. - */ - private BalloonHint mBalloonHint; - - /** - * Desired position of the balloon to the input view. - */ - private int mHintPositionToInputView[] = new int[2]; - - /** - * Decoding result to show. - */ - private DecodingInfo mDecInfo; - - /** - * Listener used to notify IME that user clicks a candidate, or navigate - * between them. - */ - private CandidateViewListener mCvListener; - - /** - * Used to notify the container to update the status of forward/backward - * arrows. - */ - private ArrowUpdater mArrowUpdater; - - /** - * If true, update the arrow status when drawing candidates. - */ - private boolean mUpdateArrowStatusWhenDraw = false; - - /** - * Page number of the page displayed in this view. - */ - private int mPageNo; - - /** - * Active candidate position in this page. - */ - private int mActiveCandInPage; - - /** - * Used to decided whether the active candidate should be highlighted or - * not. If user changes focus to composing view (The view to show Pinyin - * string), the highlight in candidate view should be removed. - */ - private boolean mEnableActiveHighlight = true; - - /** - * The page which is just calculated. - */ - private int mPageNoCalculated = -1; - - /** - * The Drawable used to display as the background of the high-lighted item. - */ - private Drawable mActiveCellDrawable; - - /** - * The Drawable used to display as separators between candidates. - */ - private Drawable mSeparatorDrawable; - - /** - * Color to draw normal candidates generated by IME. - */ - private int mImeCandidateColor; - - /** - * Color to draw normal candidates Recommended by application. - */ - private int mRecommendedCandidateColor; - - /** - * Color to draw the normal(not highlighted) candidates, it can be one of - * {@link #mImeCandidateColor} or {@link #mRecommendedCandidateColor}. - */ - private int mNormalCandidateColor; - - /** - * Color to draw the active(highlighted) candidates, including candidates - * from IME and candidates from application. - */ - private int mActiveCandidateColor; - - /** - * Text size to draw candidates generated by IME. - */ - private int mImeCandidateTextSize; - - /** - * Text size to draw candidates recommended by application. - */ - private int mRecommendedCandidateTextSize; - - /** - * The current text size to draw candidates. It can be one of - * {@link #mImeCandidateTextSize} or {@link #mRecommendedCandidateTextSize}. - */ - private int mCandidateTextSize; - - /** - * Paint used to draw candidates. - */ - private Paint mCandidatesPaint; - - /** - * Used to draw footnote. - */ - private Paint mFootnotePaint; - - /** - * The width to show suspension points. - */ - private float mSuspensionPointsWidth; - - /** - * Rectangle used to draw the active candidate. - */ - private RectF mActiveCellRect; - - /** - * Left and right margins for a candidate. It is specified in xml, and is - * the minimum margin for a candidate. The actual gap between two candidates - * is 2 * {@link #mCandidateMargin} + {@link #mSeparatorDrawable}. - * getIntrinsicWidth(). Because length of candidate is not fixed, there can - * be some extra space after the last candidate in the current page. In - * order to achieve best look-and-feel, this extra space will be divided and - * allocated to each candidates. - */ - private float mCandidateMargin; - - /** - * Left and right extra margins for a candidate. - */ - private float mCandidateMarginExtra; - - /** - * Rectangles for the candidates in this page. - **/ - private Vector mCandRects; - - /** - * FontMetricsInt used to measure the size of candidates. - */ - private FontMetricsInt mFmiCandidates; - - /** - * FontMetricsInt used to measure the size of footnotes. - */ - private FontMetricsInt mFmiFootnote; - - private PressTimer mTimer = new PressTimer(); - - private GestureDetector mGestureDetector; - - private int mLocationTmp[] = new int[2]; - - public CandidateView(Context context, AttributeSet attrs) { - super(context, attrs); - - Resources r = context.getResources(); - - Configuration conf = r.getConfiguration(); - if (conf.keyboard == Configuration.KEYBOARD_NOKEYS - || conf.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { - mShowFootnote = false; - } - - mActiveCellDrawable = r.getDrawable(R.drawable.candidate_hl_bg); - mSeparatorDrawable = r.getDrawable(R.drawable.candidates_vertical_line); - mCandidateMargin = r.getDimension(R.dimen.candidate_margin_left_right); - - mImeCandidateColor = r.getColor(R.color.candidate_color); - mRecommendedCandidateColor = r.getColor(R.color.recommended_candidate_color); - mNormalCandidateColor = mImeCandidateColor; - mActiveCandidateColor = r.getColor(R.color.active_candidate_color); - - mCandidatesPaint = new Paint(); - mCandidatesPaint.setAntiAlias(true); - - mFootnotePaint = new Paint(); - mFootnotePaint.setAntiAlias(true); - mFootnotePaint.setColor(r.getColor(R.color.footnote_color)); - mActiveCellRect = new RectF(); - - mCandRects = new Vector(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int mOldWidth = mMeasuredWidth; - int mOldHeight = mMeasuredHeight; - - setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), - widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), - heightMeasureSpec)); - - if (mOldWidth != mMeasuredWidth || mOldHeight != mMeasuredHeight) { - onSizeChanged(); - } - } - - public void initialize(ArrowUpdater arrowUpdater, BalloonHint balloonHint, - GestureDetector gestureDetector, CandidateViewListener cvListener) { - mArrowUpdater = arrowUpdater; - mBalloonHint = balloonHint; - mGestureDetector = gestureDetector; - mCvListener = cvListener; - } - - public void setDecodingInfo(DecodingInfo decInfo) { - if (null == decInfo) return; - mDecInfo = decInfo; - mPageNoCalculated = -1; - - if (mDecInfo.candidatesFromApp()) { - mNormalCandidateColor = mRecommendedCandidateColor; - mCandidateTextSize = mRecommendedCandidateTextSize; - } else { - mNormalCandidateColor = mImeCandidateColor; - mCandidateTextSize = mImeCandidateTextSize; - } - if (mCandidatesPaint.getTextSize() != mCandidateTextSize) { - mCandidatesPaint.setTextSize(mCandidateTextSize); - mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); - mSuspensionPointsWidth = - mCandidatesPaint.measureText(SUSPENSION_POINTS); - } - - // Remove any pending timer for the previous list. - mTimer.removeTimer(); - } - - public int getActiveCandiatePosInPage() { - return mActiveCandInPage; - } - - public int getActiveCandiatePosGlobal() { - return mDecInfo.mPageStart.get(mPageNo) + mActiveCandInPage; - } - - /** - * Show a page in the decoding result set previously. - * - * @param pageNo Which page to show. - * @param activeCandInPage Which candidate should be set as active item. - * @param enableActiveHighlight When false, active item will not be - * highlighted. - */ - public void showPage(int pageNo, int activeCandInPage, - boolean enableActiveHighlight) { - if (null == mDecInfo) return; - mPageNo = pageNo; - mActiveCandInPage = activeCandInPage; - if (mEnableActiveHighlight != enableActiveHighlight) { - mEnableActiveHighlight = enableActiveHighlight; - } - - if (!calculatePage(mPageNo)) { - mUpdateArrowStatusWhenDraw = true; - } else { - mUpdateArrowStatusWhenDraw = false; - } - - invalidate(); - } - - public void enableActiveHighlight(boolean enableActiveHighlight) { - if (enableActiveHighlight == mEnableActiveHighlight) return; - - mEnableActiveHighlight = enableActiveHighlight; - invalidate(); - } - - public boolean activeCursorForward() { - if (!mDecInfo.pageReady(mPageNo)) return false; - int pageSize = mDecInfo.mPageStart.get(mPageNo + 1) - - mDecInfo.mPageStart.get(mPageNo); - if (mActiveCandInPage + 1 < pageSize) { - showPage(mPageNo, mActiveCandInPage + 1, true); - return true; - } - return false; - } - - public boolean activeCurseBackward() { - if (mActiveCandInPage > 0) { - showPage(mPageNo, mActiveCandInPage - 1, true); - return true; - } - return false; - } - - private void onSizeChanged() { - mContentWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; - mContentHeight = (int) ((mMeasuredHeight - mPaddingTop - mPaddingBottom) * 0.95f); - /** - * How to decide the font size if the height for display is given? - * Now it is implemented in a stupid way. - */ - int textSize = 1; - mCandidatesPaint.setTextSize(textSize); - mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); - while (mFmiCandidates.bottom - mFmiCandidates.top < mContentHeight) { - textSize++; - mCandidatesPaint.setTextSize(textSize); - mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); - } - - mImeCandidateTextSize = textSize; - mRecommendedCandidateTextSize = textSize * 3 / 4; - if (null == mDecInfo) { - mCandidateTextSize = mImeCandidateTextSize; - mCandidatesPaint.setTextSize(mCandidateTextSize); - mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); - mSuspensionPointsWidth = - mCandidatesPaint.measureText(SUSPENSION_POINTS); - } else { - // Reset the decoding information to update members for painting. - setDecodingInfo(mDecInfo); - } - - textSize = 1; - mFootnotePaint.setTextSize(textSize); - mFmiFootnote = mFootnotePaint.getFontMetricsInt(); - while (mFmiFootnote.bottom - mFmiFootnote.top < mContentHeight / 2) { - textSize++; - mFootnotePaint.setTextSize(textSize); - mFmiFootnote = mFootnotePaint.getFontMetricsInt(); - } - textSize--; - mFootnotePaint.setTextSize(textSize); - mFmiFootnote = mFootnotePaint.getFontMetricsInt(); - - // When the size is changed, the first page will be displayed. - mPageNo = 0; - mActiveCandInPage = 0; - } - - private boolean calculatePage(int pageNo) { - if (pageNo == mPageNoCalculated) return true; - - mContentWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; - mContentHeight = (int) ((mMeasuredHeight - mPaddingTop - mPaddingBottom) * 0.95f); - - if (mContentWidth <= 0 || mContentHeight <= 0) return false; - - int candSize = mDecInfo.mCandidatesList.size(); - - // If the size of page exists, only calculate the extra margin. - boolean onlyExtraMargin = false; - int fromPage = mDecInfo.mPageStart.size() - 1; - if (mDecInfo.mPageStart.size() > pageNo + 1) { - onlyExtraMargin = true; - fromPage = pageNo; - } - - // If the previous pages have no information, calculate them first. - for (int p = fromPage; p <= pageNo; p++) { - int pStart = mDecInfo.mPageStart.get(p); - int pSize = 0; - int charNum = 0; - float lastItemWidth = 0; - - float xPos; - xPos = 0; - xPos += mSeparatorDrawable.getIntrinsicWidth(); - while (xPos < mContentWidth && pStart + pSize < candSize) { - int itemPos = pStart + pSize; - String itemStr = mDecInfo.mCandidatesList.get(itemPos); - float itemWidth = mCandidatesPaint.measureText(itemStr); - if (itemWidth < MIN_ITEM_WIDTH) itemWidth = MIN_ITEM_WIDTH; - - itemWidth += mCandidateMargin * 2; - itemWidth += mSeparatorDrawable.getIntrinsicWidth(); - if (xPos + itemWidth < mContentWidth || 0 == pSize) { - xPos += itemWidth; - lastItemWidth = itemWidth; - pSize++; - charNum += itemStr.length(); - } else { - break; - } - } - if (!onlyExtraMargin) { - mDecInfo.mPageStart.add(pStart + pSize); - mDecInfo.mCnToPage.add(mDecInfo.mCnToPage.get(p) + charNum); - } - - float marginExtra = (mContentWidth - xPos) / pSize / 2; - - if (mContentWidth - xPos > lastItemWidth) { - // Must be the last page, because if there are more items, - // the next item's width must be less than lastItemWidth. - // In this case, if the last margin is less than the current - // one, the last margin can be used, so that the - // look-and-feeling will be the same as the previous page. - if (mCandidateMarginExtra <= marginExtra) { - marginExtra = mCandidateMarginExtra; - } - } else if (pSize == 1) { - marginExtra = 0; - } - mCandidateMarginExtra = marginExtra; - } - mPageNoCalculated = pageNo; - return true; - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - // The invisible candidate view(the one which is not in foreground) can - // also be called to drawn, but its decoding result and candidate list - // may be empty. - if (null == mDecInfo || mDecInfo.isCandidatesListEmpty()) return; - - // Calculate page. If the paging information is ready, the function will - // return at once. - calculatePage(mPageNo); - - int pStart = mDecInfo.mPageStart.get(mPageNo); - int pSize = mDecInfo.mPageStart.get(mPageNo + 1) - pStart; - float candMargin = mCandidateMargin + mCandidateMarginExtra; - if (mActiveCandInPage > pSize - 1) { - mActiveCandInPage = pSize - 1; - } - - mCandRects.removeAllElements(); - - float xPos = mPaddingLeft; - int yPos = (getMeasuredHeight() - - (mFmiCandidates.bottom - mFmiCandidates.top)) / 2 - - mFmiCandidates.top; - xPos += drawVerticalSeparator(canvas, xPos); - for (int i = 0; i < pSize; i++) { - float footnoteSize = 0; - String footnote = null; - if (mShowFootnote) { - footnote = Integer.toString(i + 1); - footnoteSize = mFootnotePaint.measureText(footnote); - assert (footnoteSize < candMargin); - } - String cand = mDecInfo.mCandidatesList.get(pStart + i); - float candidateWidth = mCandidatesPaint.measureText(cand); - float centerOffset = 0; - if (candidateWidth < MIN_ITEM_WIDTH) { - centerOffset = (MIN_ITEM_WIDTH - candidateWidth) / 2; - candidateWidth = MIN_ITEM_WIDTH; - } - - float itemTotalWidth = candidateWidth + 2 * candMargin; - - if (mActiveCandInPage == i && mEnableActiveHighlight) { - mActiveCellRect.set(xPos, mPaddingTop + 1, xPos - + itemTotalWidth, getHeight() - mPaddingBottom - 1); - mActiveCellDrawable.setBounds((int) mActiveCellRect.left, - (int) mActiveCellRect.top, (int) mActiveCellRect.right, - (int) mActiveCellRect.bottom); - mActiveCellDrawable.draw(canvas); - } - - if (mCandRects.size() < pSize) mCandRects.add(new RectF()); - mCandRects.elementAt(i).set(xPos - 1, yPos + mFmiCandidates.top, - xPos + itemTotalWidth + 1, yPos + mFmiCandidates.bottom); - - // Draw footnote - if (mShowFootnote) { - canvas.drawText(footnote, xPos + (candMargin - footnoteSize) - / 2, yPos, mFootnotePaint); - } - - // Left margin - xPos += candMargin; - if (candidateWidth > mContentWidth - xPos - centerOffset) { - cand = getLimitedCandidateForDrawing(cand, - mContentWidth - xPos - centerOffset); - } - if (mActiveCandInPage == i && mEnableActiveHighlight) { - mCandidatesPaint.setColor(mActiveCandidateColor); - } else { - mCandidatesPaint.setColor(mNormalCandidateColor); - } - canvas.drawText(cand, xPos + centerOffset, yPos, - mCandidatesPaint); - - // Candidate and right margin - xPos += candidateWidth + candMargin; - - // Draw the separator between candidates. - xPos += drawVerticalSeparator(canvas, xPos); - } - - // Update the arrow status of the container. - if (null != mArrowUpdater && mUpdateArrowStatusWhenDraw) { - mArrowUpdater.updateArrowStatus(); - mUpdateArrowStatusWhenDraw = false; - } - } - - private String getLimitedCandidateForDrawing(String rawCandidate, - float widthToDraw) { - int subLen = rawCandidate.length(); - if (subLen <= 1) return rawCandidate; - do { - subLen--; - float width = mCandidatesPaint.measureText(rawCandidate, 0, subLen); - if (width + mSuspensionPointsWidth <= widthToDraw || 1 >= subLen) { - return rawCandidate.substring(0, subLen) + - SUSPENSION_POINTS; - } - } while (true); - } - - private float drawVerticalSeparator(Canvas canvas, float xPos) { - mSeparatorDrawable.setBounds((int) xPos, mPaddingTop, (int) xPos - + mSeparatorDrawable.getIntrinsicWidth(), getMeasuredHeight() - - mPaddingBottom); - mSeparatorDrawable.draw(canvas); - return mSeparatorDrawable.getIntrinsicWidth(); - } - - private int mapToItemInPage(int x, int y) { - // mCandRects.size() == 0 happens when the page is set, but - // touch events occur before onDraw(). It usually happens with - // monkey test. - if (!mDecInfo.pageReady(mPageNo) || mPageNoCalculated != mPageNo - || mCandRects.size() == 0) { - return -1; - } - - int pageStart = mDecInfo.mPageStart.get(mPageNo); - int pageSize = mDecInfo.mPageStart.get(mPageNo + 1) - pageStart; - if (mCandRects.size() < pageSize) { - return -1; - } - - // If not found, try to find the nearest one. - float nearestDis = Float.MAX_VALUE; - int nearest = -1; - for (int i = 0; i < pageSize; i++) { - RectF r = mCandRects.elementAt(i); - if (r.left < x && r.right > x && r.top < y && r.bottom > y) { - return i; - } - float disx = (r.left + r.right) / 2 - x; - float disy = (r.top + r.bottom) / 2 - y; - float dis = disx * disx + disy * disy; - if (dis < nearestDis) { - nearestDis = dis; - nearest = i; - } - } - - return nearest; - } - - // Because the candidate view under the current focused one may also get - // touching events. Here we just bypass the event to the container and let - // it decide which view should handle the event. - @Override - public boolean onTouchEvent(MotionEvent event) { - return super.onTouchEvent(event); - } - - public boolean onTouchEventReal(MotionEvent event) { - // The page in the background can also be touched. - if (null == mDecInfo || !mDecInfo.pageReady(mPageNo) - || mPageNoCalculated != mPageNo) return true; - - int x, y; - x = (int) event.getX(); - y = (int) event.getY(); - - if (mGestureDetector.onTouchEvent(event)) { - mTimer.removeTimer(); - mBalloonHint.delayedDismiss(0); - return true; - } - - int clickedItemInPage = -1; - - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - clickedItemInPage = mapToItemInPage(x, y); - if (clickedItemInPage >= 0) { - invalidate(); - mCvListener.onClickChoice(clickedItemInPage - + mDecInfo.mPageStart.get(mPageNo)); - } - mBalloonHint.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS); - break; - - case MotionEvent.ACTION_DOWN: - clickedItemInPage = mapToItemInPage(x, y); - if (clickedItemInPage >= 0) { - showBalloon(clickedItemInPage, true); - mTimer.startTimer(BalloonHint.TIME_DELAY_SHOW, mPageNo, - clickedItemInPage); - } - break; - - case MotionEvent.ACTION_CANCEL: - break; - - case MotionEvent.ACTION_MOVE: - clickedItemInPage = mapToItemInPage(x, y); - if (clickedItemInPage >= 0 - && (clickedItemInPage != mTimer.getActiveCandOfPageToShow() || mPageNo != mTimer - .getPageToShow())) { - showBalloon(clickedItemInPage, true); - mTimer.startTimer(BalloonHint.TIME_DELAY_SHOW, mPageNo, - clickedItemInPage); - } - } - return true; - } - - private void showBalloon(int candPos, boolean delayedShow) { - mBalloonHint.removeTimer(); - - RectF r = mCandRects.elementAt(candPos); - int desired_width = (int) (r.right - r.left); - int desired_height = (int) (r.bottom - r.top); - mBalloonHint.setBalloonConfig(mDecInfo.mCandidatesList - .get(mDecInfo.mPageStart.get(mPageNo) + candPos), 44, true, - mImeCandidateColor, desired_width, desired_height); - - getLocationOnScreen(mLocationTmp); - mHintPositionToInputView[0] = mLocationTmp[0] - + (int) (r.left - (mBalloonHint.getWidth() - desired_width) / 2); - mHintPositionToInputView[1] = -mBalloonHint.getHeight(); - - long delay = BalloonHint.TIME_DELAY_SHOW; - if (!delayedShow) delay = 0; - mBalloonHint.dismiss(); - if (!mBalloonHint.isShowing()) { - mBalloonHint.delayedShow(delay, mHintPositionToInputView); - } else { - mBalloonHint.delayedUpdate(0, mHintPositionToInputView, -1, -1); - } - } - - private class PressTimer extends Handler implements Runnable { - private boolean mTimerPending = false; - private int mPageNoToShow; - private int mActiveCandOfPage; - - public PressTimer() { - super(); - } - - public void startTimer(long afterMillis, int pageNo, int activeInPage) { - mTimer.removeTimer(); - postDelayed(this, afterMillis); - mTimerPending = true; - mPageNoToShow = pageNo; - mActiveCandOfPage = activeInPage; - } - - public int getPageToShow() { - return mPageNoToShow; - } - - public int getActiveCandOfPageToShow() { - return mActiveCandOfPage; - } - - public boolean removeTimer() { - if (mTimerPending) { - mTimerPending = false; - removeCallbacks(this); - return true; - } - return false; - } - - public boolean isPending() { - return mTimerPending; - } - - public void run() { - if (mPageNoToShow >= 0 && mActiveCandOfPage >= 0) { - // Always enable to highlight the clicked one. - showPage(mPageNoToShow, mActiveCandOfPage, true); - invalidate(); - } - mTimerPending = false; - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/CandidateViewListener.java b/PinyinIME/src/com/android/inputmethod/pinyin/CandidateViewListener.java deleted file mode 100644 index 795d119..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/CandidateViewListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -/** - * Interface to notify the input method when the user clicks a candidate or - * makes a direction-gesture on candidate view. - */ -public interface CandidateViewListener { - public void onClickChoice(int choiceId); - - public void onToLeftGesture(); - - public void onToRightGesture(); - - public void onToTopGesture(); - - public void onToBottomGesture(); -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/CandidatesContainer.java b/PinyinIME/src/com/android/inputmethod/pinyin/CandidatesContainer.java deleted file mode 100644 index 5b2a999..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/CandidatesContainer.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import com.android.inputmethod.pinyin.PinyinIME.DecodingInfo; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.AnimationSet; -import android.view.animation.TranslateAnimation; -import android.view.animation.Animation.AnimationListener; -import android.widget.ImageButton; -import android.widget.RelativeLayout; -import android.widget.ViewFlipper; - -interface ArrowUpdater { - void updateArrowStatus(); -} - - -/** - * Container used to host the two candidate views. When user drags on candidate - * view, animation is used to dismiss the current candidate view and show a new - * one. These two candidate views and their parent are hosted by this container. - *

- * Besides the candidate views, there are two arrow views to show the page - * forward/backward arrows. - *

- */ -public class CandidatesContainer extends RelativeLayout implements - OnTouchListener, AnimationListener, ArrowUpdater { - /** - * Alpha value to show an enabled arrow. - */ - private static int ARROW_ALPHA_ENABLED = 0xff; - - /** - * Alpha value to show an disabled arrow. - */ - private static int ARROW_ALPHA_DISABLED = 0x40; - - /** - * Animation time to show a new candidate view and dismiss the old one. - */ - private static int ANIMATION_TIME = 200; - - /** - * Listener used to notify IME that user clicks a candidate, or navigate - * between them. - */ - private CandidateViewListener mCvListener; - - /** - * The left arrow button used to show previous page. - */ - private ImageButton mLeftArrowBtn; - - /** - * The right arrow button used to show next page. - */ - private ImageButton mRightArrowBtn; - - /** - * Decoding result to show. - */ - private DecodingInfo mDecInfo; - - /** - * The animation view used to show candidates. It contains two views. - * Normally, the candidates are shown one of them. When user navigates to - * another page, animation effect will be performed. - */ - private ViewFlipper mFlipper; - - /** - * The x offset of the flipper in this container. - */ - private int xOffsetForFlipper; - - /** - * Animation used by the incoming view when the user navigates to a left - * page. - */ - private Animation mInAnimPushLeft; - - /** - * Animation used by the incoming view when the user navigates to a right - * page. - */ - private Animation mInAnimPushRight; - - /** - * Animation used by the incoming view when the user navigates to a page - * above. If the page navigation is triggered by DOWN key, this animation is - * used. - */ - private Animation mInAnimPushUp; - - /** - * Animation used by the incoming view when the user navigates to a page - * below. If the page navigation is triggered by UP key, this animation is - * used. - */ - private Animation mInAnimPushDown; - - /** - * Animation used by the outgoing view when the user navigates to a left - * page. - */ - private Animation mOutAnimPushLeft; - - /** - * Animation used by the outgoing view when the user navigates to a right - * page. - */ - private Animation mOutAnimPushRight; - - /** - * Animation used by the outgoing view when the user navigates to a page - * above. If the page navigation is triggered by DOWN key, this animation is - * used. - */ - private Animation mOutAnimPushUp; - - /** - * Animation used by the incoming view when the user navigates to a page - * below. If the page navigation is triggered by UP key, this animation is - * used. - */ - private Animation mOutAnimPushDown; - - /** - * Animation object which is used for the incoming view currently. - */ - private Animation mInAnimInUse; - - /** - * Animation object which is used for the outgoing view currently. - */ - private Animation mOutAnimInUse; - - /** - * Current page number in display. - */ - private int mCurrentPage = -1; - - public CandidatesContainer(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public void initialize(CandidateViewListener cvListener, - BalloonHint balloonHint, GestureDetector gestureDetector) { - mCvListener = cvListener; - - mLeftArrowBtn = (ImageButton) findViewById(R.id.arrow_left_btn); - mRightArrowBtn = (ImageButton) findViewById(R.id.arrow_right_btn); - mLeftArrowBtn.setOnTouchListener(this); - mRightArrowBtn.setOnTouchListener(this); - - mFlipper = (ViewFlipper) findViewById(R.id.candidate_flipper); - mFlipper.setMeasureAllChildren(true); - - invalidate(); - requestLayout(); - - for (int i = 0; i < mFlipper.getChildCount(); i++) { - CandidateView cv = (CandidateView) mFlipper.getChildAt(i); - cv.initialize(this, balloonHint, gestureDetector, mCvListener); - } - } - - public void showCandidates(PinyinIME.DecodingInfo decInfo, - boolean enableActiveHighlight) { - if (null == decInfo) return; - mDecInfo = decInfo; - mCurrentPage = 0; - - if (decInfo.isCandidatesListEmpty()) { - showArrow(mLeftArrowBtn, false); - showArrow(mRightArrowBtn, false); - } else { - showArrow(mLeftArrowBtn, true); - showArrow(mRightArrowBtn, true); - } - - for (int i = 0; i < mFlipper.getChildCount(); i++) { - CandidateView cv = (CandidateView) mFlipper.getChildAt(i); - cv.setDecodingInfo(mDecInfo); - } - stopAnimation(); - - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - cv.showPage(mCurrentPage, 0, enableActiveHighlight); - - updateArrowStatus(); - invalidate(); - } - - public int getCurrentPage() { - return mCurrentPage; - } - - public void enableActiveHighlight(boolean enableActiveHighlight) { - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - cv.enableActiveHighlight(enableActiveHighlight); - invalidate(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - Environment env = Environment.getInstance(); - int measuredWidth = env.getScreenWidth(); - int measuredHeight = getPaddingTop(); - measuredHeight += env.getHeightForCandidates(); - widthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth, - MeasureSpec.EXACTLY); - heightMeasureSpec = MeasureSpec.makeMeasureSpec(measuredHeight, - MeasureSpec.EXACTLY); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (null != mLeftArrowBtn) { - xOffsetForFlipper = mLeftArrowBtn.getMeasuredWidth(); - } - } - - public boolean activeCurseBackward() { - if (mFlipper.isFlipping() || null == mDecInfo) { - return false; - } - - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - - if (cv.activeCurseBackward()) { - cv.invalidate(); - return true; - } else { - return pageBackward(true, true); - } - } - - public boolean activeCurseForward() { - if (mFlipper.isFlipping() || null == mDecInfo) { - return false; - } - - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - - if (cv.activeCursorForward()) { - cv.invalidate(); - return true; - } else { - return pageForward(true, true); - } - } - - public boolean pageBackward(boolean animLeftRight, - boolean enableActiveHighlight) { - if (null == mDecInfo) return false; - - if (mFlipper.isFlipping() || 0 == mCurrentPage) return false; - - int child = mFlipper.getDisplayedChild(); - int childNext = (child + 1) % 2; - CandidateView cv = (CandidateView) mFlipper.getChildAt(child); - CandidateView cvNext = (CandidateView) mFlipper.getChildAt(childNext); - - mCurrentPage--; - int activeCandInPage = cv.getActiveCandiatePosInPage(); - if (animLeftRight) - activeCandInPage = mDecInfo.mPageStart.elementAt(mCurrentPage + 1) - - mDecInfo.mPageStart.elementAt(mCurrentPage) - 1; - - cvNext.showPage(mCurrentPage, activeCandInPage, enableActiveHighlight); - loadAnimation(animLeftRight, false); - startAnimation(); - - updateArrowStatus(); - return true; - } - - public boolean pageForward(boolean animLeftRight, - boolean enableActiveHighlight) { - if (null == mDecInfo) return false; - - if (mFlipper.isFlipping() || !mDecInfo.preparePage(mCurrentPage + 1)) { - return false; - } - - int child = mFlipper.getDisplayedChild(); - int childNext = (child + 1) % 2; - CandidateView cv = (CandidateView) mFlipper.getChildAt(child); - int activeCandInPage = cv.getActiveCandiatePosInPage(); - cv.enableActiveHighlight(enableActiveHighlight); - - CandidateView cvNext = (CandidateView) mFlipper.getChildAt(childNext); - mCurrentPage++; - if (animLeftRight) activeCandInPage = 0; - - cvNext.showPage(mCurrentPage, activeCandInPage, enableActiveHighlight); - loadAnimation(animLeftRight, true); - startAnimation(); - - updateArrowStatus(); - return true; - } - - public int getActiveCandiatePos() { - if (null == mDecInfo) return -1; - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - return cv.getActiveCandiatePosGlobal(); - } - - public void updateArrowStatus() { - if (mCurrentPage < 0) return; - boolean forwardEnabled = mDecInfo.pageForwardable(mCurrentPage); - boolean backwardEnabled = mDecInfo.pageBackwardable(mCurrentPage); - - if (backwardEnabled) { - enableArrow(mLeftArrowBtn, true); - } else { - enableArrow(mLeftArrowBtn, false); - } - if (forwardEnabled) { - enableArrow(mRightArrowBtn, true); - } else { - enableArrow(mRightArrowBtn, false); - } - } - - private void enableArrow(ImageButton arrowBtn, boolean enabled) { - arrowBtn.setEnabled(enabled); - if (enabled) - arrowBtn.setAlpha(ARROW_ALPHA_ENABLED); - else - arrowBtn.setAlpha(ARROW_ALPHA_DISABLED); - } - - private void showArrow(ImageButton arrowBtn, boolean show) { - if (show) - arrowBtn.setVisibility(View.VISIBLE); - else - arrowBtn.setVisibility(View.INVISIBLE); - } - - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (v == mLeftArrowBtn) { - mCvListener.onToRightGesture(); - } else if (v == mRightArrowBtn) { - mCvListener.onToLeftGesture(); - } - } else if (event.getAction() == MotionEvent.ACTION_UP) { - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - cv.enableActiveHighlight(true); - } - - return false; - } - - // The reason why we handle candiate view's touch events here is because - // that the view under the focused view may get touch events instead of the - // focused one. - @Override - public boolean onTouchEvent(MotionEvent event) { - event.offsetLocation(-xOffsetForFlipper, 0); - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - cv.onTouchEventReal(event); - return true; - } - - public void loadAnimation(boolean animLeftRight, boolean forward) { - if (animLeftRight) { - if (forward) { - if (null == mInAnimPushLeft) { - mInAnimPushLeft = createAnimation(1.0f, 0, 0, 0, 0, 1.0f, - ANIMATION_TIME); - mOutAnimPushLeft = createAnimation(0, -1.0f, 0, 0, 1.0f, 0, - ANIMATION_TIME); - } - mInAnimInUse = mInAnimPushLeft; - mOutAnimInUse = mOutAnimPushLeft; - } else { - if (null == mInAnimPushRight) { - mInAnimPushRight = createAnimation(-1.0f, 0, 0, 0, 0, 1.0f, - ANIMATION_TIME); - mOutAnimPushRight = createAnimation(0, 1.0f, 0, 0, 1.0f, 0, - ANIMATION_TIME); - } - mInAnimInUse = mInAnimPushRight; - mOutAnimInUse = mOutAnimPushRight; - } - } else { - if (forward) { - if (null == mInAnimPushUp) { - mInAnimPushUp = createAnimation(0, 0, 1.0f, 0, 0, 1.0f, - ANIMATION_TIME); - mOutAnimPushUp = createAnimation(0, 0, 0, -1.0f, 1.0f, 0, - ANIMATION_TIME); - } - mInAnimInUse = mInAnimPushUp; - mOutAnimInUse = mOutAnimPushUp; - } else { - if (null == mInAnimPushDown) { - mInAnimPushDown = createAnimation(0, 0, -1.0f, 0, 0, 1.0f, - ANIMATION_TIME); - mOutAnimPushDown = createAnimation(0, 0, 0, 1.0f, 1.0f, 0, - ANIMATION_TIME); - } - mInAnimInUse = mInAnimPushDown; - mOutAnimInUse = mOutAnimPushDown; - } - } - - mInAnimInUse.setAnimationListener(this); - - mFlipper.setInAnimation(mInAnimInUse); - mFlipper.setOutAnimation(mOutAnimInUse); - } - - private Animation createAnimation(float xFrom, float xTo, float yFrom, - float yTo, float alphaFrom, float alphaTo, long duration) { - AnimationSet animSet = new AnimationSet(getContext(), null); - Animation trans = new TranslateAnimation(Animation.RELATIVE_TO_SELF, - xFrom, Animation.RELATIVE_TO_SELF, xTo, - Animation.RELATIVE_TO_SELF, yFrom, Animation.RELATIVE_TO_SELF, - yTo); - Animation alpha = new AlphaAnimation(alphaFrom, alphaTo); - animSet.addAnimation(trans); - animSet.addAnimation(alpha); - animSet.setDuration(duration); - return animSet; - } - - private void startAnimation() { - mFlipper.showNext(); - } - - private void stopAnimation() { - mFlipper.stopFlipping(); - } - - public void onAnimationEnd(Animation animation) { - if (!mLeftArrowBtn.isPressed() && !mRightArrowBtn.isPressed()) { - CandidateView cv = (CandidateView) mFlipper.getCurrentView(); - cv.enableActiveHighlight(true); - } - } - - public void onAnimationRepeat(Animation animation) { - } - - public void onAnimationStart(Animation animation) { - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/ComposingView.java b/PinyinIME/src/com/android/inputmethod/pinyin/ComposingView.java deleted file mode 100644 index b781cda..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/ComposingView.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.FontMetricsInt; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup.LayoutParams; - -/** - * View used to show composing string (The Pinyin string for the unselected - * syllables and the Chinese string for the selected syllables.) - */ -public class ComposingView extends View { - /** - *

- * There are three statuses for the composing view. - *

- * - *

- * {@link #SHOW_PINYIN} is used to show the current Pinyin string without - * highlighted effect. When user inputs Pinyin characters one by one, the - * Pinyin string will be shown in this mode. - *

- *

- * {@link #SHOW_STRING_LOWERCASE} is used to show the Pinyin string in - * lowercase with highlighted effect. When user presses UP key and there is - * no fixed Chinese characters, composing view will switch from - * {@link #SHOW_PINYIN} to this mode, and in this mode, user can press - * confirm key to input the lower-case string, so that user can input - * English letter in Chinese mode. - *

- *

- * {@link #EDIT_PINYIN} is used to edit the Pinyin string (shown with - * highlighted effect). When current status is {@link #SHOW_PINYIN} and user - * presses UP key, if there are fixed Characters, the input method will - * switch to {@link #EDIT_PINYIN} thus user can modify some characters in - * the middle of the Pinyin string. If the current status is - * {@link #SHOW_STRING_LOWERCASE} and user presses LEFT and RIGHT key, it - * will also switch to {@link #EDIT_PINYIN}. - *

- *

- * Whenever user presses down key, the status switches to - * {@link #SHOW_PINYIN}. - *

- *

- * When composing view's status is {@link #SHOW_PINYIN}, the IME's status is - * {@link PinyinIME.ImeState#STATE_INPUT}, otherwise, the IME's status - * should be {@link PinyinIME.ImeState#STATE_COMPOSING}. - *

- */ - public enum ComposingStatus { - SHOW_PINYIN, SHOW_STRING_LOWERCASE, EDIT_PINYIN, - } - - private static final int LEFT_RIGHT_MARGIN = 5; - - /** - * Used to draw composing string. When drawing the active and idle part of - * the spelling(Pinyin) string, the color may be changed. - */ - private Paint mPaint; - - /** - * Drawable used to draw highlight effect. - */ - private Drawable mHlDrawable; - - /** - * Drawable used to draw cursor for editing mode. - */ - private Drawable mCursor; - - /** - * Used to estimate dimensions to show the string . - */ - private FontMetricsInt mFmi; - - private int mStrColor; - private int mStrColorHl; - private int mStrColorIdle; - - private int mFontSize; - - private ComposingStatus mComposingStatus; - - PinyinIME.DecodingInfo mDecInfo; - - public ComposingView(Context context, AttributeSet attrs) { - super(context, attrs); - - Resources r = context.getResources(); - mHlDrawable = r.getDrawable(R.drawable.composing_hl_bg); - mCursor = r.getDrawable(R.drawable.composing_area_cursor); - - mStrColor = r.getColor(R.color.composing_color); - mStrColorHl = r.getColor(R.color.composing_color_hl); - mStrColorIdle = r.getColor(R.color.composing_color_idle); - - mFontSize = r.getDimensionPixelSize(R.dimen.composing_height); - - mPaint = new Paint(); - mPaint.setColor(mStrColor); - mPaint.setAntiAlias(true); - mPaint.setTextSize(mFontSize); - - mFmi = mPaint.getFontMetricsInt(); - } - - public void reset() { - mComposingStatus = ComposingStatus.SHOW_PINYIN; - } - - /** - * Set the composing string to show. If the IME status is - * {@link PinyinIME.ImeState#STATE_INPUT}, the composing view's status will - * be set to {@link ComposingStatus#SHOW_PINYIN}, otherwise the composing - * view will set its status to {@link ComposingStatus#SHOW_STRING_LOWERCASE} - * or {@link ComposingStatus#EDIT_PINYIN} automatically. - */ - public void setDecodingInfo(PinyinIME.DecodingInfo decInfo, - PinyinIME.ImeState imeStatus) { - mDecInfo = decInfo; - - if (PinyinIME.ImeState.STATE_INPUT == imeStatus) { - mComposingStatus = ComposingStatus.SHOW_PINYIN; - mDecInfo.moveCursorToEdge(false); - } else { - if (decInfo.getFixedLen() != 0 - || ComposingStatus.EDIT_PINYIN == mComposingStatus) { - mComposingStatus = ComposingStatus.EDIT_PINYIN; - } else { - mComposingStatus = ComposingStatus.SHOW_STRING_LOWERCASE; - } - mDecInfo.moveCursor(0); - } - - measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - requestLayout(); - invalidate(); - } - - public boolean moveCursor(int keyCode) { - if (keyCode != KeyEvent.KEYCODE_DPAD_LEFT - && keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) return false; - - if (ComposingStatus.EDIT_PINYIN == mComposingStatus) { - int offset = 0; - if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) - offset = -1; - else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) offset = 1; - mDecInfo.moveCursor(offset); - } else if (ComposingStatus.SHOW_STRING_LOWERCASE == mComposingStatus) { - if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT - || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - mComposingStatus = ComposingStatus.EDIT_PINYIN; - - measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - requestLayout(); - } - - } - invalidate(); - return true; - } - - public ComposingStatus getComposingStatus() { - return mComposingStatus; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - float width; - int height; - height = mFmi.bottom - mFmi.top + mPaddingTop + mPaddingBottom; - - if (null == mDecInfo) { - width = 0; - } else { - width = mPaddingLeft + mPaddingRight + LEFT_RIGHT_MARGIN * 2; - - String str; - if (ComposingStatus.SHOW_STRING_LOWERCASE == mComposingStatus) { - str = mDecInfo.getOrigianlSplStr().toString(); - } else { - str = mDecInfo.getComposingStrForDisplay(); - } - width += mPaint.measureText(str, 0, str.length()); - } - setMeasuredDimension((int) (width + 0.5f), height); - } - - @Override - protected void onDraw(Canvas canvas) { - if (ComposingStatus.EDIT_PINYIN == mComposingStatus - || ComposingStatus.SHOW_PINYIN == mComposingStatus) { - drawForPinyin(canvas); - return; - } - - float x, y; - x = mPaddingLeft + LEFT_RIGHT_MARGIN; - y = -mFmi.top + mPaddingTop; - - mPaint.setColor(mStrColorHl); - mHlDrawable.setBounds(mPaddingLeft, mPaddingTop, getWidth() - - mPaddingRight, getHeight() - mPaddingBottom); - mHlDrawable.draw(canvas); - - String splStr = mDecInfo.getOrigianlSplStr().toString(); - canvas.drawText(splStr, 0, splStr.length(), x, y, mPaint); - } - - private void drawCursor(Canvas canvas, float x) { - mCursor.setBounds((int) x, mPaddingTop, (int) x - + mCursor.getIntrinsicWidth(), getHeight() - mPaddingBottom); - mCursor.draw(canvas); - } - - private void drawForPinyin(Canvas canvas) { - float x, y; - x = mPaddingLeft + LEFT_RIGHT_MARGIN; - y = -mFmi.top + mPaddingTop; - - mPaint.setColor(mStrColor); - - int cursorPos = mDecInfo.getCursorPosInCmpsDisplay(); - int cmpsPos = cursorPos; - String cmpsStr = mDecInfo.getComposingStrForDisplay(); - int activeCmpsLen = mDecInfo.getActiveCmpsDisplayLen(); - if (cursorPos > activeCmpsLen) cmpsPos = activeCmpsLen; - canvas.drawText(cmpsStr, 0, cmpsPos, x, y, mPaint); - x += mPaint.measureText(cmpsStr, 0, cmpsPos); - if (cursorPos <= activeCmpsLen) { - if (ComposingStatus.EDIT_PINYIN == mComposingStatus) { - drawCursor(canvas, x); - } - canvas.drawText(cmpsStr, cmpsPos, activeCmpsLen, x, y, mPaint); - } - - x += mPaint.measureText(cmpsStr, cmpsPos, activeCmpsLen); - - if (cmpsStr.length() > activeCmpsLen) { - mPaint.setColor(mStrColorIdle); - int oriPos = activeCmpsLen; - if (cursorPos > activeCmpsLen) { - canvas.drawText(cmpsStr, oriPos, cursorPos, x, y, mPaint); - x += mPaint.measureText(cmpsStr, oriPos, cursorPos); - - if (ComposingStatus.EDIT_PINYIN == mComposingStatus) { - drawCursor(canvas, x); - } - - oriPos = cursorPos; - } - canvas.drawText(cmpsStr, oriPos, cmpsStr.length(), x, y, mPaint); - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/EnglishInputProcessor.java b/PinyinIME/src/com/android/inputmethod/pinyin/EnglishInputProcessor.java deleted file mode 100644 index 6d61119..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/EnglishInputProcessor.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.view.KeyEvent; -import android.view.inputmethod.InputConnection; - -/** - * Class to handle English input. - */ -public class EnglishInputProcessor { - - private int mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN; - - public boolean processKey(InputConnection inputContext, KeyEvent event, - boolean upperCase, boolean realAction) { - if (null == inputContext || null == event) return false; - - int keyCode = event.getKeyCode(); - - CharSequence prefix = null; - prefix = inputContext.getTextBeforeCursor(2, 0); - - int keyChar; - keyChar = 0; - if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) { - keyChar = keyCode - KeyEvent.KEYCODE_A + 'a'; - if (upperCase) { - keyChar = keyChar + 'A' - 'a'; - } - } else if (keyCode >= KeyEvent.KEYCODE_0 - && keyCode <= KeyEvent.KEYCODE_9) - keyChar = keyCode - KeyEvent.KEYCODE_0 + '0'; - else if (keyCode == KeyEvent.KEYCODE_COMMA) - keyChar = ','; - else if (keyCode == KeyEvent.KEYCODE_PERIOD) - keyChar = '.'; - else if (keyCode == KeyEvent.KEYCODE_APOSTROPHE) - keyChar = '\''; - else if (keyCode == KeyEvent.KEYCODE_AT) - keyChar = '@'; - else if (keyCode == KeyEvent.KEYCODE_SLASH) keyChar = '/'; - - if (0 == keyChar) { - mLastKeyCode = keyCode; - - String insert = null; - if (KeyEvent.KEYCODE_DEL == keyCode) { - if (realAction) { - inputContext.deleteSurroundingText(1, 0); - } - } else if (KeyEvent.KEYCODE_ENTER == keyCode) { - insert = "\n"; - } else if (KeyEvent.KEYCODE_SPACE == keyCode) { - insert = " "; - } else { - return false; - } - - if (null != insert && realAction) - inputContext.commitText(insert, insert.length()); - - return true; - } - - if (!realAction) - return true; - - if (KeyEvent.KEYCODE_SHIFT_LEFT == mLastKeyCode - || KeyEvent.KEYCODE_SHIFT_LEFT == mLastKeyCode) { - if (keyChar >= 'a' && keyChar <= 'z') - keyChar = keyChar - 'a' + 'A'; - } else if (KeyEvent.KEYCODE_ALT_LEFT == mLastKeyCode) { - } - - String result = String.valueOf((char) keyChar); - inputContext.commitText(result, result.length()); - mLastKeyCode = keyCode; - return true; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/Environment.java b/PinyinIME/src/com/android/inputmethod/pinyin/Environment.java deleted file mode 100644 index 8869294..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/Environment.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.content.Context; -import android.content.res.Configuration; -import android.view.Display; -import android.view.WindowManager; - -/** - * Global environment configurations for showing soft keyboard and candidate - * view. All original dimension values are defined in float, and the real size - * is calculated from the float values of and screen size. In this way, this - * input method can work even when screen size is changed. - */ -public class Environment { - /** - * The key height for portrait mode. It is relative to the screen height. - */ - private static final float KEY_HEIGHT_RATIO_PORTRAIT = 0.105f; - - /** - * The key height for landscape mode. It is relative to the screen height. - */ - private static final float KEY_HEIGHT_RATIO_LANDSCAPE = 0.147f; - - /** - * The height of the candidates area for portrait mode. It is relative to - * screen height. - */ - private static final float CANDIDATES_AREA_HEIGHT_RATIO_PORTRAIT = 0.084f; - - /** - * The height of the candidates area for portrait mode. It is relative to - * screen height. - */ - private static final float CANDIDATES_AREA_HEIGHT_RATIO_LANDSCAPE = 0.125f; - - /** - * How much should the balloon width be larger than width of the real key. - * It is relative to the smaller one of screen width and height. - */ - private static final float KEY_BALLOON_WIDTH_PLUS_RATIO = 0.08f; - - /** - * How much should the balloon height be larger than that of the real key. - * It is relative to the smaller one of screen width and height. - */ - private static final float KEY_BALLOON_HEIGHT_PLUS_RATIO = 0.07f; - - /** - * The text size for normal keys. It is relative to the smaller one of - * screen width and height. - */ - private static final float NORMAL_KEY_TEXT_SIZE_RATIO = 0.075f; - - /** - * The text size for function keys. It is relative to the smaller one of - * screen width and height. - */ - private static final float FUNCTION_KEY_TEXT_SIZE_RATIO = 0.055f; - - /** - * The text size balloons of normal keys. It is relative to the smaller one - * of screen width and height. - */ - private static final float NORMAL_BALLOON_TEXT_SIZE_RATIO = 0.14f; - - /** - * The text size balloons of function keys. It is relative to the smaller - * one of screen width and height. - */ - private static final float FUNCTION_BALLOON_TEXT_SIZE_RATIO = 0.085f; - - /** - * The configurations are managed in a singleton. - */ - private static Environment mInstance; - - private int mScreenWidth; - private int mScreenHeight; - private int mKeyHeight; - private int mCandidatesAreaHeight; - private int mKeyBalloonWidthPlus; - private int mKeyBalloonHeightPlus; - private int mNormalKeyTextSize; - private int mFunctionKeyTextSize; - private int mNormalBalloonTextSize; - private int mFunctionBalloonTextSize; - private Configuration mConfig = new Configuration(); - private boolean mDebug = false; - - private Environment() { - } - - public static Environment getInstance() { - if (null == mInstance) { - mInstance = new Environment(); - } - return mInstance; - } - - public void onConfigurationChanged(Configuration newConfig, Context context) { - if (mConfig.orientation != newConfig.orientation) { - WindowManager wm = (WindowManager) context - .getSystemService(Context.WINDOW_SERVICE); - Display d = wm.getDefaultDisplay(); - mScreenWidth = d.getWidth(); - mScreenHeight = d.getHeight(); - - int scale; - if (mScreenHeight > mScreenWidth) { - mKeyHeight = (int) (mScreenHeight * KEY_HEIGHT_RATIO_PORTRAIT); - mCandidatesAreaHeight = (int) (mScreenHeight * CANDIDATES_AREA_HEIGHT_RATIO_PORTRAIT); - scale = mScreenWidth; - } else { - mKeyHeight = (int) (mScreenHeight * KEY_HEIGHT_RATIO_LANDSCAPE); - mCandidatesAreaHeight = (int) (mScreenHeight * CANDIDATES_AREA_HEIGHT_RATIO_LANDSCAPE); - scale = mScreenHeight; - } - mNormalKeyTextSize = (int) (scale * NORMAL_KEY_TEXT_SIZE_RATIO); - mFunctionKeyTextSize = (int) (scale * FUNCTION_KEY_TEXT_SIZE_RATIO); - mNormalBalloonTextSize = (int) (scale * NORMAL_BALLOON_TEXT_SIZE_RATIO); - mFunctionBalloonTextSize = (int) (scale * FUNCTION_BALLOON_TEXT_SIZE_RATIO); - mKeyBalloonWidthPlus = (int) (scale * KEY_BALLOON_WIDTH_PLUS_RATIO); - mKeyBalloonHeightPlus = (int) (scale * KEY_BALLOON_HEIGHT_PLUS_RATIO); - } - - mConfig.updateFrom(newConfig); - } - - public Configuration getConfiguration() { - return mConfig; - } - - public int getScreenWidth() { - return mScreenWidth; - } - - public int getScreenHeight() { - return mScreenHeight; - } - - public int getHeightForCandidates() { - return mCandidatesAreaHeight; - } - - public float getKeyXMarginFactor() { - return 1.0f; - } - - public float getKeyYMarginFactor() { - if (Configuration.ORIENTATION_LANDSCAPE == mConfig.orientation) { - return 0.7f; - } - return 1.0f; - } - - public int getKeyHeight() { - return mKeyHeight; - } - - public int getKeyBalloonWidthPlus() { - return mKeyBalloonWidthPlus; - } - - public int getKeyBalloonHeightPlus() { - return mKeyBalloonHeightPlus; - } - - public int getSkbHeight() { - if (Configuration.ORIENTATION_PORTRAIT == mConfig.orientation) { - return mKeyHeight * 4; - } else if (Configuration.ORIENTATION_LANDSCAPE == mConfig.orientation) { - return mKeyHeight * 4; - } - return 0; - } - - public int getKeyTextSize(boolean isFunctionKey) { - if (isFunctionKey) { - return mFunctionKeyTextSize; - } else { - return mNormalKeyTextSize; - } - } - - public int getBalloonTextSize(boolean isFunctionKey) { - if (isFunctionKey) { - return mFunctionBalloonTextSize; - } else { - return mNormalBalloonTextSize; - } - } - - public boolean hasHardKeyboard() { - if (mConfig.keyboard == Configuration.KEYBOARD_NOKEYS - || mConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { - return false; - } - return true; - } - - public boolean needDebug() { - return mDebug; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/InputModeSwitcher.java b/PinyinIME/src/com/android/inputmethod/pinyin/InputModeSwitcher.java deleted file mode 100644 index 96b59c3..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/InputModeSwitcher.java +++ /dev/null @@ -1,821 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow; - -import android.content.res.Resources; -import android.view.inputmethod.EditorInfo; - -/** - * Switcher used to switching input mode between Chinese, English, symbol,etc. - */ -public class InputModeSwitcher { - /** - * User defined key code, used by soft keyboard. - */ - private static final int USERDEF_KEYCODE_SHIFT_1 = -1; - - /** - * User defined key code, used by soft keyboard. - */ - private static final int USERDEF_KEYCODE_LANG_2 = -2; - - /** - * User defined key code, used by soft keyboard. - */ - private static final int USERDEF_KEYCODE_SYM_3 = -3; - - /** - * User defined key code, used by soft keyboard. - */ - public static final int USERDEF_KEYCODE_PHONE_SYM_4 = -4; - - /** - * User defined key code, used by soft keyboard. - */ - private static final int USERDEF_KEYCODE_MORE_SYM_5 = -5; - - /** - * User defined key code, used by soft keyboard. - */ - private static final int USERDEF_KEYCODE_SMILEY_6 = -6; - - - /** - * Bits used to indicate soft keyboard layout. If none bit is set, the - * current input mode does not require a soft keyboard. - **/ - private static final int MASK_SKB_LAYOUT = 0xf0000000; - - /** - * A kind of soft keyboard layout. An input mode should be anded with - * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. - */ - private static final int MASK_SKB_LAYOUT_QWERTY = 0x10000000; - - /** - * A kind of soft keyboard layout. An input mode should be anded with - * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. - */ - private static final int MASK_SKB_LAYOUT_SYMBOL1 = 0x20000000; - - /** - * A kind of soft keyboard layout. An input mode should be anded with - * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. - */ - private static final int MASK_SKB_LAYOUT_SYMBOL2 = 0x30000000; - - /** - * A kind of soft keyboard layout. An input mode should be anded with - * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. - */ - private static final int MASK_SKB_LAYOUT_SMILEY = 0x40000000; - - /** - * A kind of soft keyboard layout. An input mode should be anded with - * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. - */ - private static final int MASK_SKB_LAYOUT_PHONE = 0x50000000; - - /** - * Used to indicate which language the current input mode is in. If the - * current input mode works with a none-QWERTY soft keyboard, these bits are - * also used to get language information. For example, a Chinese symbol soft - * keyboard and an English one are different in an icon which is used to - * tell user the language information. BTW, the smiley soft keyboard mode - * should be set with {@link #MASK_LANGUAGE_CN} because it can only be - * launched from Chinese QWERTY soft keyboard, and it has Chinese icon on - * soft keyboard. - */ - private static final int MASK_LANGUAGE = 0x0f000000; - - /** - * Used to indicate the current language. An input mode should be anded with - * {@link #MASK_LANGUAGE} to get this information. - */ - private static final int MASK_LANGUAGE_CN = 0x01000000; - - /** - * Used to indicate the current language. An input mode should be anded with - * {@link #MASK_LANGUAGE} to get this information. - */ - private static final int MASK_LANGUAGE_EN = 0x02000000; - - /** - * Used to indicate which case the current input mode is in. For example, - * English QWERTY has lowercase and uppercase. For the Chinese QWERTY, these - * bits are ignored. For phone keyboard layout, these bits can be - * {@link #MASK_CASE_UPPER} to request symbol page for phone soft keyboard. - */ - private static final int MASK_CASE = 0x00f00000; - - /** - * Used to indicate the current case information. An input mode should be - * anded with {@link #MASK_CASE} to get this information. - */ - private static final int MASK_CASE_LOWER = 0x00100000; - - /** - * Used to indicate the current case information. An input mode should be - * anded with {@link #MASK_CASE} to get this information. - */ - private static final int MASK_CASE_UPPER = 0x00200000; - - /** - * Mode for inputing Chinese with soft keyboard. - */ - public static final int MODE_SKB_CHINESE = (MASK_SKB_LAYOUT_QWERTY | MASK_LANGUAGE_CN); - - /** - * Mode for inputing basic symbols for Chinese mode with soft keyboard. - */ - public static final int MODE_SKB_SYMBOL1_CN = (MASK_SKB_LAYOUT_SYMBOL1 | MASK_LANGUAGE_CN); - - /** - * Mode for inputing more symbols for Chinese mode with soft keyboard. - */ - public static final int MODE_SKB_SYMBOL2_CN = (MASK_SKB_LAYOUT_SYMBOL2 | MASK_LANGUAGE_CN); - - /** - * Mode for inputing English lower characters with soft keyboard. - */ - public static final int MODE_SKB_ENGLISH_LOWER = (MASK_SKB_LAYOUT_QWERTY - | MASK_LANGUAGE_EN | MASK_CASE_LOWER); - - /** - * Mode for inputing English upper characters with soft keyboard. - */ - public static final int MODE_SKB_ENGLISH_UPPER = (MASK_SKB_LAYOUT_QWERTY - | MASK_LANGUAGE_EN | MASK_CASE_UPPER); - - /** - * Mode for inputing basic symbols for English mode with soft keyboard. - */ - public static final int MODE_SKB_SYMBOL1_EN = (MASK_SKB_LAYOUT_SYMBOL1 | MASK_LANGUAGE_EN); - - /** - * Mode for inputing more symbols for English mode with soft keyboard. - */ - public static final int MODE_SKB_SYMBOL2_EN = (MASK_SKB_LAYOUT_SYMBOL2 | MASK_LANGUAGE_EN); - - /** - * Mode for inputing smileys with soft keyboard. - */ - public static final int MODE_SKB_SMILEY = (MASK_SKB_LAYOUT_SMILEY | MASK_LANGUAGE_CN); - - /** - * Mode for inputing phone numbers. - */ - public static final int MODE_SKB_PHONE_NUM = (MASK_SKB_LAYOUT_PHONE); - - /** - * Mode for inputing phone numbers. - */ - public static final int MODE_SKB_PHONE_SYM = (MASK_SKB_LAYOUT_PHONE | MASK_CASE_UPPER); - - /** - * Mode for inputing Chinese with a hardware keyboard. - */ - public static final int MODE_HKB_CHINESE = (MASK_LANGUAGE_CN); - - /** - * Mode for inputing English with a hardware keyboard - */ - public static final int MODE_HKB_ENGLISH = (MASK_LANGUAGE_EN); - - /** - * Unset mode. - */ - public static final int MODE_UNSET = 0; - - /** - * Maximum toggle states for a soft keyboard. - */ - public static final int MAX_TOGGLE_STATES = 4; - - /** - * The input mode for the current edit box. - */ - private int mInputMode = MODE_UNSET; - - /** - * Used to remember previous input mode. When user enters an edit field, the - * previous input mode will be tried. If the previous mode can not be used - * for the current situation (For example, previous mode is a soft keyboard - * mode to input symbols, and we have a hardware keyboard for the current - * situation), {@link #mRecentLauageInputMode} will be tried. - **/ - private int mPreviousInputMode = MODE_SKB_CHINESE; - - /** - * Used to remember recent mode to input language. - */ - private int mRecentLauageInputMode = MODE_SKB_CHINESE; - - /** - * Editor information of the current edit box. - */ - private EditorInfo mEditorInfo; - - /** - * Used to indicate required toggling operations. - */ - private ToggleStates mToggleStates = new ToggleStates(); - - /** - * The current field is a short message field? - */ - private boolean mShortMessageField; - - /** - * Is return key in normal state? - */ - private boolean mEnterKeyNormal = true; - - /** - * Current icon. 0 for none icon. - */ - int mInputIcon = R.drawable.ime_pinyin; - - /** - * IME service. - */ - private PinyinIME mImeService; - - /** - * Key toggling state for Chinese mode. - */ - private int mToggleStateCn; - - /** - * Key toggling state for Chinese mode with candidates. - */ - private int mToggleStateCnCand; - - /** - * Key toggling state for English lowwercase mode. - */ - private int mToggleStateEnLower; - - /** - * Key toggling state for English upppercase mode. - */ - private int mToggleStateEnUpper; - - /** - * Key toggling state for English symbol mode for the first page. - */ - private int mToggleStateEnSym1; - - /** - * Key toggling state for English symbol mode for the second page. - */ - private int mToggleStateEnSym2; - - /** - * Key toggling state for smiley mode. - */ - private int mToggleStateSmiley; - - /** - * Key toggling state for phone symbol mode. - */ - private int mToggleStatePhoneSym; - - /** - * Key toggling state for GO action of ENTER key. - */ - private int mToggleStateGo; - - /** - * Key toggling state for SEARCH action of ENTER key. - */ - private int mToggleStateSearch; - - /** - * Key toggling state for SEND action of ENTER key. - */ - private int mToggleStateSend; - - /** - * Key toggling state for NEXT action of ENTER key. - */ - private int mToggleStateNext; - - /** - * Key toggling state for SEND action of ENTER key. - */ - private int mToggleStateDone; - - /** - * QWERTY row toggling state for Chinese input. - */ - private int mToggleRowCn; - - /** - * QWERTY row toggling state for English input. - */ - private int mToggleRowEn; - - /** - * QWERTY row toggling state for URI input. - */ - private int mToggleRowUri; - - /** - * QWERTY row toggling state for email address input. - */ - private int mToggleRowEmailAddress; - - class ToggleStates { - /** - * If it is true, this soft keyboard is a QWERTY one. - */ - boolean mQwerty; - - /** - * If {@link #mQwerty} is true, this variable is used to decide the - * letter case of the QWERTY keyboard. - */ - boolean mQwertyUpperCase; - - /** - * The id of enabled row in the soft keyboard. Refer to - * {@link com.android.inputmethod.pinyin.SoftKeyboard.KeyRow} for - * details. - */ - public int mRowIdToEnable; - - /** - * Used to store all other toggle states for the current input mode. - */ - public int mKeyStates[] = new int[MAX_TOGGLE_STATES]; - - /** - * Number of states to toggle. - */ - public int mKeyStatesNum; - } - - public InputModeSwitcher(PinyinIME imeService) { - mImeService = imeService; - Resources r = mImeService.getResources(); - mToggleStateCn = Integer.parseInt(r.getString(R.string.toggle_cn)); - mToggleStateCnCand = Integer.parseInt(r - .getString(R.string.toggle_cn_cand)); - mToggleStateEnLower = Integer.parseInt(r - .getString(R.string.toggle_en_lower)); - mToggleStateEnUpper = Integer.parseInt(r - .getString(R.string.toggle_en_upper)); - mToggleStateEnSym1 = Integer.parseInt(r - .getString(R.string.toggle_en_sym1)); - mToggleStateEnSym2 = Integer.parseInt(r - .getString(R.string.toggle_en_sym2)); - mToggleStateSmiley = Integer.parseInt(r - .getString(R.string.toggle_smiley)); - mToggleStatePhoneSym = Integer.parseInt(r - .getString(R.string.toggle_phone_sym)); - - mToggleStateGo = Integer - .parseInt(r.getString(R.string.toggle_enter_go)); - mToggleStateSearch = Integer.parseInt(r - .getString(R.string.toggle_enter_search)); - mToggleStateSend = Integer.parseInt(r - .getString(R.string.toggle_enter_send)); - mToggleStateNext = Integer.parseInt(r - .getString(R.string.toggle_enter_next)); - mToggleStateDone = Integer.parseInt(r - .getString(R.string.toggle_enter_done)); - - mToggleRowCn = Integer.parseInt(r.getString(R.string.toggle_row_cn)); - mToggleRowEn = Integer.parseInt(r.getString(R.string.toggle_row_en)); - mToggleRowUri = Integer.parseInt(r.getString(R.string.toggle_row_uri)); - mToggleRowEmailAddress = Integer.parseInt(r - .getString(R.string.toggle_row_emailaddress)); - } - - public int getInputMode() { - return mInputMode; - } - - public ToggleStates getToggleStates() { - return mToggleStates; - } - - public int getSkbLayout() { - int layout = (mInputMode & MASK_SKB_LAYOUT); - - switch (layout) { - case MASK_SKB_LAYOUT_QWERTY: - return R.xml.skb_qwerty; - case MASK_SKB_LAYOUT_SYMBOL1: - return R.xml.skb_sym1; - case MASK_SKB_LAYOUT_SYMBOL2: - return R.xml.skb_sym2; - case MASK_SKB_LAYOUT_SMILEY: - return R.xml.skb_smiley; - case MASK_SKB_LAYOUT_PHONE: - return R.xml.skb_phone; - } - return 0; - } - - // Return the icon to update. - public int switchLanguageWithHkb() { - int newInputMode = MODE_HKB_CHINESE; - mInputIcon = R.drawable.ime_pinyin; - - if (MODE_HKB_CHINESE == mInputMode) { - newInputMode = MODE_HKB_ENGLISH; - mInputIcon = R.drawable.ime_en; - } - - saveInputMode(newInputMode); - return mInputIcon; - } - - // Return the icon to update. - public int switchModeForUserKey(int userKey) { - int newInputMode = MODE_UNSET; - - if (USERDEF_KEYCODE_LANG_2 == userKey) { - if (MODE_SKB_CHINESE == mInputMode) { - newInputMode = MODE_SKB_ENGLISH_LOWER; - } else if (MODE_SKB_ENGLISH_LOWER == mInputMode - || MODE_SKB_ENGLISH_UPPER == mInputMode) { - newInputMode = MODE_SKB_CHINESE; - } else if (MODE_SKB_SYMBOL1_CN == mInputMode) { - newInputMode = MODE_SKB_SYMBOL1_EN; - } else if (MODE_SKB_SYMBOL1_EN == mInputMode) { - newInputMode = MODE_SKB_SYMBOL1_CN; - } else if (MODE_SKB_SYMBOL2_CN == mInputMode) { - newInputMode = MODE_SKB_SYMBOL2_EN; - } else if (MODE_SKB_SYMBOL2_EN == mInputMode) { - newInputMode = MODE_SKB_SYMBOL2_CN; - } else if (MODE_SKB_SMILEY == mInputMode) { - newInputMode = MODE_SKB_CHINESE; - } - } else if (USERDEF_KEYCODE_SYM_3 == userKey) { - if (MODE_SKB_CHINESE == mInputMode) { - newInputMode = MODE_SKB_SYMBOL1_CN; - } else if (MODE_SKB_ENGLISH_UPPER == mInputMode - || MODE_SKB_ENGLISH_LOWER == mInputMode) { - newInputMode = MODE_SKB_SYMBOL1_EN; - } else if (MODE_SKB_SYMBOL1_EN == mInputMode - || MODE_SKB_SYMBOL2_EN == mInputMode) { - newInputMode = MODE_SKB_ENGLISH_LOWER; - } else if (MODE_SKB_SYMBOL1_CN == mInputMode - || MODE_SKB_SYMBOL2_CN == mInputMode) { - newInputMode = MODE_SKB_CHINESE; - } else if (MODE_SKB_SMILEY == mInputMode) { - newInputMode = MODE_SKB_SYMBOL1_CN; - } - } else if (USERDEF_KEYCODE_SHIFT_1 == userKey) { - if (MODE_SKB_ENGLISH_LOWER == mInputMode) { - newInputMode = MODE_SKB_ENGLISH_UPPER; - } else if (MODE_SKB_ENGLISH_UPPER == mInputMode) { - newInputMode = MODE_SKB_ENGLISH_LOWER; - } - } else if (USERDEF_KEYCODE_MORE_SYM_5 == userKey) { - int sym = (MASK_SKB_LAYOUT & mInputMode); - if (MASK_SKB_LAYOUT_SYMBOL1 == sym) { - sym = MASK_SKB_LAYOUT_SYMBOL2; - } else { - sym = MASK_SKB_LAYOUT_SYMBOL1; - } - newInputMode = ((mInputMode & (~MASK_SKB_LAYOUT)) | sym); - } else if (USERDEF_KEYCODE_SMILEY_6 == userKey) { - if (MODE_SKB_CHINESE == mInputMode) { - newInputMode = MODE_SKB_SMILEY; - } else { - newInputMode = MODE_SKB_CHINESE; - } - } else if (USERDEF_KEYCODE_PHONE_SYM_4 == userKey) { - if (MODE_SKB_PHONE_NUM == mInputMode) { - newInputMode = MODE_SKB_PHONE_SYM; - } else { - newInputMode = MODE_SKB_PHONE_NUM; - } - } - - if (newInputMode == mInputMode || MODE_UNSET == newInputMode) { - return mInputIcon; - } - - saveInputMode(newInputMode); - prepareToggleStates(true); - return mInputIcon; - } - - // Return the icon to update. - public int requestInputWithHkb(EditorInfo editorInfo) { - mShortMessageField = false; - boolean english = false; - int newInputMode = MODE_HKB_CHINESE; - - switch (editorInfo.inputType & EditorInfo.TYPE_MASK_CLASS) { - case EditorInfo.TYPE_CLASS_NUMBER: - case EditorInfo.TYPE_CLASS_PHONE: - case EditorInfo.TYPE_CLASS_DATETIME: - english = true; - break; - case EditorInfo.TYPE_CLASS_TEXT: - int v = editorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION; - if (v == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS - || v == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD - || v == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD - || v == EditorInfo.TYPE_TEXT_VARIATION_URI) { - english = true; - } else if (v == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { - mShortMessageField = true; - } else if ((editorInfo.imeOptions & - EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { - newInputMode = MODE_HKB_CHINESE; - } - break; - default: - } - - if (english) { - // If the application request English mode, we switch to it. - newInputMode = MODE_HKB_ENGLISH; - } else { - // If the application do not request English mode, we will - // try to keep the previous mode to input language text. - // Because there is not soft keyboard, we need discard all - // soft keyboard related information from the previous language - // mode. - if ((mRecentLauageInputMode & MASK_LANGUAGE) == MASK_LANGUAGE_CN) { - newInputMode = MODE_HKB_CHINESE; - } else { - newInputMode = MODE_HKB_ENGLISH; - } - } - mEditorInfo = editorInfo; - saveInputMode(newInputMode); - prepareToggleStates(false); - return mInputIcon; - } - - // Return the icon to update. - public int requestInputWithSkb(EditorInfo editorInfo) { - mShortMessageField = false; - - int newInputMode = MODE_SKB_CHINESE; - - switch (editorInfo.inputType & EditorInfo.TYPE_MASK_CLASS) { - case EditorInfo.TYPE_CLASS_NUMBER: - case EditorInfo.TYPE_CLASS_DATETIME: - newInputMode = MODE_SKB_SYMBOL1_EN; - break; - case EditorInfo.TYPE_CLASS_PHONE: - newInputMode = MODE_SKB_PHONE_NUM; - break; - case EditorInfo.TYPE_CLASS_TEXT: - int v = editorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION; - if (v == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS - || v == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD - || v == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD - || v == EditorInfo.TYPE_TEXT_VARIATION_URI) { - // If the application request English mode, we switch to it. - newInputMode = MODE_SKB_ENGLISH_LOWER; - } else if (v == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { - newInputMode = MODE_SKB_CHINESE; - mShortMessageField = true; - } else if ((editorInfo.imeOptions & - EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { - newInputMode = MODE_SKB_CHINESE; - } else { - // If the application do not request English mode, we will - // try to keep the previous mode. - int skbLayout = (mInputMode & MASK_SKB_LAYOUT); - newInputMode = mInputMode; - if (0 == skbLayout) { - if ((mInputMode & MASK_LANGUAGE) == MASK_LANGUAGE_CN) { - newInputMode = MODE_SKB_CHINESE; - } else { - newInputMode = MODE_SKB_ENGLISH_LOWER; - } - } - } - break; - default: - newInputMode = MODE_SKB_CHINESE; - } - - mEditorInfo = editorInfo; - saveInputMode(newInputMode); - prepareToggleStates(true); - return mInputIcon; - } - - // Return the icon to update. - public int requestBackToPreviousSkb() { - int layout = (mInputMode & MASK_SKB_LAYOUT); - int lastLayout = (mPreviousInputMode & MASK_SKB_LAYOUT); - if (0 != layout && 0 != lastLayout) { - mInputMode = mPreviousInputMode; - saveInputMode(mInputMode); - prepareToggleStates(true); - return mInputIcon; - } - return 0; - } - - public int getTooggleStateForCnCand() { - return mToggleStateCnCand; - } - - public boolean isEnglishWithHkb() { - return MODE_HKB_ENGLISH == mInputMode; - } - - public boolean isEnglishWithSkb() { - return MODE_SKB_ENGLISH_LOWER == mInputMode - || MODE_SKB_ENGLISH_UPPER == mInputMode; - } - - public boolean isEnglishUpperCaseWithSkb() { - return MODE_SKB_ENGLISH_UPPER == mInputMode; - } - - public boolean isChineseText() { - int skbLayout = (mInputMode & MASK_SKB_LAYOUT); - if (MASK_SKB_LAYOUT_QWERTY == skbLayout || 0 == skbLayout) { - int language = (mInputMode & MASK_LANGUAGE); - if (MASK_LANGUAGE_CN == language) return true; - } - return false; - } - - public boolean isChineseTextWithHkb() { - int skbLayout = (mInputMode & MASK_SKB_LAYOUT); - if (0 == skbLayout) { - int language = (mInputMode & MASK_LANGUAGE); - if (MASK_LANGUAGE_CN == language) return true; - } - return false; - } - - public boolean isChineseTextWithSkb() { - int skbLayout = (mInputMode & MASK_SKB_LAYOUT); - if (MASK_SKB_LAYOUT_QWERTY == skbLayout) { - int language = (mInputMode & MASK_LANGUAGE); - if (MASK_LANGUAGE_CN == language) return true; - } - return false; - } - - public boolean isSymbolWithSkb() { - int skbLayout = (mInputMode & MASK_SKB_LAYOUT); - if (MASK_SKB_LAYOUT_SYMBOL1 == skbLayout - || MASK_SKB_LAYOUT_SYMBOL2 == skbLayout) { - return true; - } - return false; - } - - public boolean isEnterNoramlState() { - return mEnterKeyNormal; - } - - public boolean tryHandleLongPressSwitch(int keyCode) { - if (USERDEF_KEYCODE_LANG_2 == keyCode - || USERDEF_KEYCODE_PHONE_SYM_4 == keyCode) { - mImeService.showOptionsMenu(); - return true; - } - return false; - } - - private void saveInputMode(int newInputMode) { - mPreviousInputMode = mInputMode; - mInputMode = newInputMode; - - int skbLayout = (mInputMode & MASK_SKB_LAYOUT); - if (MASK_SKB_LAYOUT_QWERTY == skbLayout) { - mRecentLauageInputMode = mInputMode; - } - - mInputIcon = R.drawable.ime_pinyin; - if (isEnglishWithHkb()) { - mInputIcon = R.drawable.ime_en; - } else if (isChineseTextWithHkb()) { - mInputIcon = R.drawable.ime_pinyin; - } - - if (!Environment.getInstance().hasHardKeyboard()) { - mInputIcon = 0; - } - } - - private void prepareToggleStates(boolean needSkb) { - mEnterKeyNormal = true; - if (!needSkb) return; - - mToggleStates.mQwerty = false; - mToggleStates.mKeyStatesNum = 0; - - int states[] = mToggleStates.mKeyStates; - int statesNum = 0; - // Toggle state for language. - int language = (mInputMode & MASK_LANGUAGE); - int layout = (mInputMode & MASK_SKB_LAYOUT); - int charcase = (mInputMode & MASK_CASE); - int variation = mEditorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION; - - if (MASK_SKB_LAYOUT_PHONE != layout) { - if (MASK_LANGUAGE_CN == language) { - // Chinese and Chinese symbol are always the default states, - // do not add a toggling operation. - if (MASK_SKB_LAYOUT_QWERTY == layout) { - mToggleStates.mQwerty = true; - mToggleStates.mQwertyUpperCase = true; - if (mShortMessageField) { - states[statesNum] = mToggleStateSmiley; - statesNum++; - } - } - } else if (MASK_LANGUAGE_EN == language) { - if (MASK_SKB_LAYOUT_QWERTY == layout) { - mToggleStates.mQwerty = true; - mToggleStates.mQwertyUpperCase = false; - states[statesNum] = mToggleStateEnLower; - if (MASK_CASE_UPPER == charcase) { - mToggleStates.mQwertyUpperCase = true; - states[statesNum] = mToggleStateEnUpper; - } - statesNum++; - } else if (MASK_SKB_LAYOUT_SYMBOL1 == layout) { - states[statesNum] = mToggleStateEnSym1; - statesNum++; - } else if (MASK_SKB_LAYOUT_SYMBOL2 == layout) { - states[statesNum] = mToggleStateEnSym2; - statesNum++; - } - } - - // Toggle rows for QWERTY. - mToggleStates.mRowIdToEnable = KeyRow.DEFAULT_ROW_ID; - if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) { - mToggleStates.mRowIdToEnable = mToggleRowEmailAddress; - } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) { - mToggleStates.mRowIdToEnable = mToggleRowUri; - } else if (MASK_LANGUAGE_CN == language) { - mToggleStates.mRowIdToEnable = mToggleRowCn; - } else if (MASK_LANGUAGE_EN == language) { - mToggleStates.mRowIdToEnable = mToggleRowEn; - } - } else { - if (MASK_CASE_UPPER == charcase) { - states[statesNum] = mToggleStatePhoneSym; - statesNum++; - } - } - - // Toggle state for enter key. - int action = mEditorInfo.imeOptions - & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); - - if (action == EditorInfo.IME_ACTION_GO) { - states[statesNum] = mToggleStateGo; - statesNum++; - mEnterKeyNormal = false; - } else if (action == EditorInfo.IME_ACTION_SEARCH) { - states[statesNum] = mToggleStateSearch; - statesNum++; - mEnterKeyNormal = false; - } else if (action == EditorInfo.IME_ACTION_SEND) { - states[statesNum] = mToggleStateSend; - statesNum++; - mEnterKeyNormal = false; - } else if (action == EditorInfo.IME_ACTION_NEXT) { - int f = mEditorInfo.inputType & EditorInfo.TYPE_MASK_FLAGS; - if (f != EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) { - states[statesNum] = mToggleStateNext; - statesNum++; - mEnterKeyNormal = false; - } - } else if (action == EditorInfo.IME_ACTION_DONE) { - states[statesNum] = mToggleStateDone; - statesNum++; - mEnterKeyNormal = false; - } - mToggleStates.mKeyStatesNum = statesNum; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/KeyMapDream.java b/PinyinIME/src/com/android/inputmethod/pinyin/KeyMapDream.java deleted file mode 100644 index 5a95c6f..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/KeyMapDream.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.view.KeyEvent; - -/** - * Class used to map the symbols on Dream's hardware keyboard to corresponding - * Chinese full-width symbols. - */ -public class KeyMapDream { - // Number of shift bits to store full-width symbols - private static final int SHIFT_FWCH = 8; - private static final int[] mKeyMap = { - KeyEvent.KEYCODE_UNKNOWN, - KeyEvent.KEYCODE_SOFT_LEFT, - KeyEvent.KEYCODE_SOFT_RIGHT, - KeyEvent.KEYCODE_HOME, - KeyEvent.KEYCODE_BACK, - KeyEvent.KEYCODE_CALL, - KeyEvent.KEYCODE_ENDCALL, - KeyEvent.KEYCODE_0 | ('\uff09' << SHIFT_FWCH), // ) - KeyEvent.KEYCODE_1 | ('\uff01' << SHIFT_FWCH), // ! - KeyEvent.KEYCODE_2 | ('\uff20' << SHIFT_FWCH), // @ - KeyEvent.KEYCODE_3 | ('\uff03' << SHIFT_FWCH), // # - KeyEvent.KEYCODE_4 | ('\uffe5' << SHIFT_FWCH), // $ - fullwidth Yuan - KeyEvent.KEYCODE_5 | ('\uff05' << SHIFT_FWCH), // % - KeyEvent.KEYCODE_6 | ('\u2026' << SHIFT_FWCH), // ^ - Apostrophe - KeyEvent.KEYCODE_7 | ('\uff06' << SHIFT_FWCH), // & - KeyEvent.KEYCODE_8 | ('\uff0a' << SHIFT_FWCH), // * - KeyEvent.KEYCODE_9 | ('\uff08' << SHIFT_FWCH), // ( - KeyEvent.KEYCODE_STAR, - KeyEvent.KEYCODE_POUND, - KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.KEYCODE_DPAD_DOWN, - KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_RIGHT, - KeyEvent.KEYCODE_DPAD_CENTER, - KeyEvent.KEYCODE_VOLUME_UP, - KeyEvent.KEYCODE_VOLUME_DOWN, - KeyEvent.KEYCODE_POWER, - KeyEvent.KEYCODE_CAMERA, - KeyEvent.KEYCODE_CLEAR, - KeyEvent.KEYCODE_A, - KeyEvent.KEYCODE_B | ('\uff3d' << SHIFT_FWCH), // ] - KeyEvent.KEYCODE_C | ('\u00a9' << SHIFT_FWCH), // copyright - KeyEvent.KEYCODE_D | ('\u3001' << SHIFT_FWCH), // \\ - KeyEvent.KEYCODE_E | ('_' << SHIFT_FWCH), // _ - KeyEvent.KEYCODE_F | ('\uff5b' << SHIFT_FWCH), // { - KeyEvent.KEYCODE_G | ('\uff5d' << SHIFT_FWCH), // } - KeyEvent.KEYCODE_H | ('\uff1a' << SHIFT_FWCH), // : - KeyEvent.KEYCODE_I | ('\uff0d' << SHIFT_FWCH), // - - KeyEvent.KEYCODE_J | ('\uff1b' << SHIFT_FWCH), // ; - KeyEvent.KEYCODE_K | ('\u201c' << SHIFT_FWCH), // " - KeyEvent.KEYCODE_L | ('\u2019' << SHIFT_FWCH), // ' - KeyEvent.KEYCODE_M | ('\u300b' << SHIFT_FWCH), // > - French quotes - KeyEvent.KEYCODE_N | ('\u300a' << SHIFT_FWCH), // < - French quotes - KeyEvent.KEYCODE_O | ('\uff0b' << SHIFT_FWCH), // + - KeyEvent.KEYCODE_P | ('\uff1d' << SHIFT_FWCH), // = - KeyEvent.KEYCODE_Q | ('\t' << SHIFT_FWCH), // \t - KeyEvent.KEYCODE_R | ('\u00ae' << SHIFT_FWCH), // trademark - KeyEvent.KEYCODE_S | ('\uff5c' << SHIFT_FWCH), // | - KeyEvent.KEYCODE_T | ('\u20ac' << SHIFT_FWCH), // - KeyEvent.KEYCODE_U | ('\u00d7' << SHIFT_FWCH), // multiplier - KeyEvent.KEYCODE_V | ('\uff3b' << SHIFT_FWCH), // [ - KeyEvent.KEYCODE_W | ('\uff40' << SHIFT_FWCH), // ` - KeyEvent.KEYCODE_X, KeyEvent.KEYCODE_Y | ('\u00f7' << SHIFT_FWCH), - KeyEvent.KEYCODE_Z, - KeyEvent.KEYCODE_COMMA | ('\uff1f' << SHIFT_FWCH), - KeyEvent.KEYCODE_PERIOD | ('\uff0f' << SHIFT_FWCH), - KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT, - KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT, - KeyEvent.KEYCODE_TAB, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_SYM, - KeyEvent.KEYCODE_EXPLORER, KeyEvent.KEYCODE_ENVELOPE, - KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_DEL, - KeyEvent.KEYCODE_GRAVE, KeyEvent.KEYCODE_MINUS, - KeyEvent.KEYCODE_EQUALS, KeyEvent.KEYCODE_LEFT_BRACKET, - KeyEvent.KEYCODE_RIGHT_BRACKET, KeyEvent.KEYCODE_BACKSLASH, - KeyEvent.KEYCODE_SEMICOLON, KeyEvent.KEYCODE_APOSTROPHE, - KeyEvent.KEYCODE_SLASH, - KeyEvent.KEYCODE_AT | ('\uff5e' << SHIFT_FWCH), - KeyEvent.KEYCODE_NUM, KeyEvent.KEYCODE_HEADSETHOOK, - KeyEvent.KEYCODE_FOCUS, KeyEvent.KEYCODE_PLUS, - KeyEvent.KEYCODE_MENU, KeyEvent.KEYCODE_NOTIFICATION, - KeyEvent.KEYCODE_SEARCH,}; - - static public char getChineseLabel(int keyCode) { - if (keyCode <= 0 || keyCode >= KeyEvent.getMaxKeyCode()) return 0; - assert ((mKeyMap[keyCode] & 0x000000ff) == keyCode); - return (char) (mKeyMap[keyCode] >> SHIFT_FWCH); - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/PinyinDecoderService.java b/PinyinIME/src/com/android/inputmethod/pinyin/PinyinDecoderService.java deleted file mode 100644 index a4a3ac4..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/PinyinDecoderService.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import com.android.inputmethod.pinyin.IPinyinDecoderService; - -import java.io.FileDescriptor; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.List; -import java.util.Vector; - -import android.app.Service; -import android.content.Intent; -import android.content.res.AssetFileDescriptor; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -/** - * This class is used to separate the input method kernel in an individual - * service so that both IME and IME-syncer can use it. - */ -public class PinyinDecoderService extends Service { - native static boolean nativeImOpenDecoder(byte fn_sys_dict[], - byte fn_usr_dict[]); - - native static boolean nativeImOpenDecoderFd(FileDescriptor fd, - long startOffset, long length, byte fn_usr_dict[]); - - native static void nativeImSetMaxLens(int maxSpsLen, int maxHzsLen); - - native static boolean nativeImCloseDecoder(); - - native static int nativeImSearch(byte pyBuf[], int pyLen); - - native static int nativeImDelSearch(int pos, boolean is_pos_in_splid, - boolean clear_fixed_this_step); - - native static void nativeImResetSearch(); - - native static int nativeImAddLetter(byte ch); - - native static String nativeImGetPyStr(boolean decoded); - - native static int nativeImGetPyStrLen(boolean decoded); - - native static int[] nativeImGetSplStart(); - - native static String nativeImGetChoice(int choiceId); - - native static int nativeImChoose(int choiceId); - - native static int nativeImCancelLastChoice(); - - native static int nativeImGetFixedLen(); - - native static boolean nativeImCancelInput(); - - native static boolean nativeImFlushCache(); - - native static int nativeImGetPredictsNum(String fixedStr); - - native static String nativeImGetPredictItem(int predictNo); - - // Sync related - native static String nativeSyncUserDict(byte[] user_dict, String tomerge); - - native static boolean nativeSyncBegin(byte[] user_dict); - - native static boolean nativeSyncFinish(); - - native static String nativeSyncGetLemmas(); - - native static int nativeSyncPutLemmas(String tomerge); - - native static int nativeSyncGetLastCount(); - - native static int nativeSyncGetTotalCount(); - - native static boolean nativeSyncClearLastGot(); - - native static int nativeSyncGetCapacity(); - - private final static int MAX_PATH_FILE_LENGTH = 100; - private static boolean inited = false; - - private String mUsr_dict_file; - - static { - try { - System.loadLibrary("jni_pinyinime"); - } catch (UnsatisfiedLinkError ule) { - Log.e("PinyinDecoderService", - "WARNING: Could not load jni_pinyinime natives"); - } - } - - // Get file name of the specified dictionary - private boolean getUsrDictFileName(byte usr_dict[]) { - if (null == usr_dict) { - return false; - } - - for (int i = 0; i < mUsr_dict_file.length(); i++) - usr_dict[i] = (byte) mUsr_dict_file.charAt(i); - usr_dict[mUsr_dict_file.length()] = 0; - - return true; - } - - private void initPinyinEngine() { - byte usr_dict[]; - usr_dict = new byte[MAX_PATH_FILE_LENGTH]; - - // Here is how we open a built-in dictionary for access through - // a file descriptor... - AssetFileDescriptor afd = getResources().openRawResourceFd( - R.raw.dict_pinyin); - if (Environment.getInstance().needDebug()) { - Log - .i("foo", "Dict: start=" + afd.getStartOffset() - + ", length=" + afd.getLength() + ", fd=" - + afd.getParcelFileDescriptor()); - } - if (getUsrDictFileName(usr_dict)) { - inited = nativeImOpenDecoderFd(afd.getFileDescriptor(), afd - .getStartOffset(), afd.getLength(), usr_dict); - } - try { - afd.close(); - } catch (IOException e) { - } - } - - @Override - public void onCreate() { - super.onCreate(); - mUsr_dict_file = getFileStreamPath("usr_dict.dat").getPath(); - // This is a hack to make sure our "files" directory has been - // created. - try { - openFileOutput("dummy", 0).close(); - } catch (FileNotFoundException e) { - } catch (IOException e) { - } - - initPinyinEngine(); - } - - @Override - public void onDestroy() { - nativeImCloseDecoder(); - inited = false; - super.onDestroy(); - } - - private final IPinyinDecoderService.Stub mBinder = new IPinyinDecoderService.Stub() { - public int getInt() { - return 12345; - } - - public void setMaxLens(int maxSpsLen, int maxHzsLen) { - nativeImSetMaxLens(maxSpsLen, maxHzsLen); - } - - public int imSearch(byte[] pyBuf, int pyLen) { - return nativeImSearch(pyBuf, pyLen); - } - - public int imDelSearch(int pos, boolean is_pos_in_splid, - boolean clear_fixed_this_step) { - return nativeImDelSearch(pos, is_pos_in_splid, - clear_fixed_this_step); - } - - public void imResetSearch() { - nativeImResetSearch(); - } - - public int imAddLetter(byte ch) { - return nativeImAddLetter(ch); - } - - public String imGetPyStr(boolean decoded) { - return nativeImGetPyStr(decoded); - } - - public int imGetPyStrLen(boolean decoded) { - return nativeImGetPyStrLen(decoded); - } - - public int[] imGetSplStart() { - return nativeImGetSplStart(); - } - - public String imGetChoice(int choiceId) { - return nativeImGetChoice(choiceId); - } - - public String imGetChoices(int choicesNum) { - String retStr = null; - for (int i = 0; i < choicesNum; i++) { - if (null == retStr) - retStr = nativeImGetChoice(i); - else - retStr += " " + nativeImGetChoice(i); - } - return retStr; - } - - public List imGetChoiceList(int choicesStart, int choicesNum, - int sentFixedLen) { - Vector choiceList = new Vector(); - for (int i = choicesStart; i < choicesStart + choicesNum; i++) { - String retStr = nativeImGetChoice(i); - if (0 == i) retStr = retStr.substring(sentFixedLen); - choiceList.add(retStr); - } - return choiceList; - } - - public int imChoose(int choiceId) { - return nativeImChoose(choiceId); - } - - public int imCancelLastChoice() { - return nativeImCancelLastChoice(); - } - - public int imGetFixedLen() { - return nativeImGetFixedLen(); - } - - public boolean imCancelInput() { - return nativeImCancelInput(); - } - - public void imFlushCache() { - nativeImFlushCache(); - } - - public int imGetPredictsNum(String fixedStr) { - return nativeImGetPredictsNum(fixedStr); - } - - public String imGetPredictItem(int predictNo) { - return nativeImGetPredictItem(predictNo); - } - - public List imGetPredictList(int predictsStart, int predictsNum) { - Vector predictList = new Vector(); - for (int i = predictsStart; i < predictsStart + predictsNum; i++) { - predictList.add(nativeImGetPredictItem(i)); - } - return predictList; - } - - public String syncUserDict(String tomerge) { - byte usr_dict[]; - usr_dict = new byte[MAX_PATH_FILE_LENGTH]; - - if (getUsrDictFileName(usr_dict)) { - return nativeSyncUserDict(usr_dict, tomerge); - } - return null; - } - - public boolean syncBegin() { - byte usr_dict[]; - usr_dict = new byte[MAX_PATH_FILE_LENGTH]; - - if (getUsrDictFileName(usr_dict)) { - return nativeSyncBegin(usr_dict); - } - return false; - } - - public void syncFinish() { - nativeSyncFinish(); - } - - public int syncPutLemmas(String tomerge) { - return nativeSyncPutLemmas(tomerge); - } - - public String syncGetLemmas() { - return nativeSyncGetLemmas(); - } - - public int syncGetLastCount() { - return nativeSyncGetLastCount(); - } - - public int syncGetTotalCount() { - return nativeSyncGetTotalCount(); - } - - public void syncClearLastGot() { - nativeSyncClearLastGot(); - } - - public int imSyncGetCapacity() { - return nativeSyncGetCapacity(); - } - }; - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/PinyinIME.java b/PinyinIME/src/com/android/inputmethod/pinyin/PinyinIME.java deleted file mode 100644 index 8d7dcce..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/PinyinIME.java +++ /dev/null @@ -1,2081 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.res.Configuration; -import android.inputmethodservice.InputMethodService; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.Gravity; -import android.view.GestureDetector; -import android.view.LayoutInflater; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.view.View.MeasureSpec; -import android.view.ViewGroup.LayoutParams; -import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.LinearLayout; -import android.widget.PopupWindow; - -import java.util.ArrayList; -import java.util.List; -import java.util.Vector; - -/** - * Main class of the Pinyin input method. - */ -public class PinyinIME extends InputMethodService { - /** - * TAG for debug. - */ - static final String TAG = "PinyinIME"; - - /** - * If is is true, IME will simulate key events for delete key, and send the - * events back to the application. - */ - private static final boolean SIMULATE_KEY_DELETE = true; - - /** - * Necessary environment configurations like screen size for this IME. - */ - private Environment mEnvironment; - - /** - * Used to switch input mode. - */ - private InputModeSwitcher mInputModeSwitcher; - - /** - * Soft keyboard container view to host real soft keyboard view. - */ - private SkbContainer mSkbContainer; - - /** - * The floating container which contains the composing view. If necessary, - * some other view like candiates container can also be put here. - */ - private LinearLayout mFloatingContainer; - - /** - * View to show the composing string. - */ - private ComposingView mComposingView; - - /** - * Window to show the composing string. - */ - private PopupWindow mFloatingWindow; - - /** - * Used to show the floating window. - */ - private PopupTimer mFloatingWindowTimer = new PopupTimer(); - - /** - * View to show candidates list. - */ - private CandidatesContainer mCandidatesContainer; - - /** - * Balloon used when user presses a candidate. - */ - private BalloonHint mCandidatesBalloon; - - /** - * Used to notify the input method when the user touch a candidate. - */ - private ChoiceNotifier mChoiceNotifier; - - /** - * Used to notify gestures from soft keyboard. - */ - private OnGestureListener mGestureListenerSkb; - - /** - * Used to notify gestures from candidates view. - */ - private OnGestureListener mGestureListenerCandidates; - - /** - * The on-screen movement gesture detector for soft keyboard. - */ - private GestureDetector mGestureDetectorSkb; - - /** - * The on-screen movement gesture detector for candidates view. - */ - private GestureDetector mGestureDetectorCandidates; - - /** - * Option dialog to choose settings and other IMEs. - */ - private AlertDialog mOptionsDialog; - - /** - * Connection used to bind the decoding service. - */ - private PinyinDecoderServiceConnection mPinyinDecoderServiceConnection; - - /** - * The current IME status. - * - * @see com.android.inputmethod.pinyin.PinyinIME.ImeState - */ - private ImeState mImeState = ImeState.STATE_IDLE; - - /** - * The decoding information, include spelling(Pinyin) string, decoding - * result, etc. - */ - private DecodingInfo mDecInfo = new DecodingInfo(); - - /** - * For English input. - */ - private EnglishInputProcessor mImEn; - - // receive ringer mode changes - private BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - SoundManager.getInstance(context).updateRingerMode(); - } - }; - - @Override - public void onCreate() { - mEnvironment = Environment.getInstance(); - if (mEnvironment.needDebug()) { - Log.d(TAG, "onCreate."); - } - super.onCreate(); - - startPinyinDecoderService(); - mImEn = new EnglishInputProcessor(); - Settings.getInstance(PreferenceManager - .getDefaultSharedPreferences(getApplicationContext())); - - mInputModeSwitcher = new InputModeSwitcher(this); - mChoiceNotifier = new ChoiceNotifier(this); - mGestureListenerSkb = new OnGestureListener(false); - mGestureListenerCandidates = new OnGestureListener(true); - mGestureDetectorSkb = new GestureDetector(this, mGestureListenerSkb); - mGestureDetectorCandidates = new GestureDetector(this, - mGestureListenerCandidates); - - mEnvironment.onConfigurationChanged(getResources().getConfiguration(), - this); - } - - @Override - public void onDestroy() { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onDestroy."); - } - unbindService(mPinyinDecoderServiceConnection); - Settings.releaseInstance(); - super.onDestroy(); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - Environment env = Environment.getInstance(); - if (mEnvironment.needDebug()) { - Log.d(TAG, "onConfigurationChanged"); - Log.d(TAG, "--last config: " + env.getConfiguration().toString()); - Log.d(TAG, "---new config: " + newConfig.toString()); - } - // We need to change the local environment first so that UI components - // can get the environment instance to handle size issues. When - // super.onConfigurationChanged() is called, onCreateCandidatesView() - // and onCreateInputView() will be executed if necessary. - env.onConfigurationChanged(newConfig, this); - - // Clear related UI of the previous configuration. - if (null != mSkbContainer) { - mSkbContainer.dismissPopups(); - } - if (null != mCandidatesBalloon) { - mCandidatesBalloon.dismiss(); - } - super.onConfigurationChanged(newConfig); - resetToIdleState(false); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - int count = event.getRepeatCount(); - if (0 == count) { - if (processKey(event, false)) return true; - } else { - boolean processed = false; - while (count > 0) { - processed |= processKey(event, true); - count--; - } - if (processed) return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (processKey(event, true)) return true; - return super.onKeyUp(keyCode, event); - } - - private boolean processKey(KeyEvent event, boolean realAction) { - if (ImeState.STATE_BYPASS == mImeState) return false; - - int keyCode = event.getKeyCode(); - // SHIFT-SPACE is used to switch between Chinese and English - // when HKB is on. - if (KeyEvent.KEYCODE_SPACE == keyCode && event.isShiftPressed()) { - if (!realAction) return true; - - updateIcon(mInputModeSwitcher.switchLanguageWithHkb()); - resetToIdleState(true); - - int allMetaState = KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON - | KeyEvent.META_ALT_RIGHT_ON | KeyEvent.META_SHIFT_ON - | KeyEvent.META_SHIFT_LEFT_ON - | KeyEvent.META_SHIFT_RIGHT_ON | KeyEvent.META_SYM_ON; - getCurrentInputConnection().clearMetaKeyStates(allMetaState); - return true; - } - - // If HKB is on to input English, by-pass the key event so that - // default key listener will handle it. - if (mInputModeSwitcher.isEnglishWithHkb()) { - return false; - } - - if (processFunctionKeys(keyCode, realAction)) { - return true; - } - - int keyChar = 0; - if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) { - keyChar = keyCode - KeyEvent.KEYCODE_A + 'a'; - } else if (keyCode >= KeyEvent.KEYCODE_0 - && keyCode <= KeyEvent.KEYCODE_9) { - keyChar = keyCode - KeyEvent.KEYCODE_0 + '0'; - } else if (keyCode == KeyEvent.KEYCODE_COMMA) { - keyChar = ','; - } else if (keyCode == KeyEvent.KEYCODE_PERIOD) { - keyChar = '.'; - } else if (keyCode == KeyEvent.KEYCODE_SPACE) { - keyChar = ' '; - } else if (keyCode == KeyEvent.KEYCODE_APOSTROPHE) { - keyChar = '\''; - } - - if (mInputModeSwitcher.isEnglishWithSkb()) { - return mImEn.processKey(getCurrentInputConnection(), event, - mInputModeSwitcher.isEnglishUpperCaseWithSkb(), realAction); - } else if (mInputModeSwitcher.isChineseText()) { - if (mImeState == ImeState.STATE_IDLE || - mImeState == ImeState.STATE_APP_COMPLETION) { - mImeState = ImeState.STATE_IDLE; - return processStateIdle(keyChar, keyCode, event, realAction); - } else if (mImeState == ImeState.STATE_INPUT) { - return processStateInput(keyChar, keyCode, event, realAction); - } else if (mImeState == ImeState.STATE_PREDICT) { - return processStatePredict(keyChar, keyCode, event, realAction); - } else if (mImeState == ImeState.STATE_COMPOSING) { - return processStateEditComposing(keyChar, keyCode, event, - realAction); - } - } else { - if (0 != keyChar && realAction) { - commitResultText(String.valueOf((char) keyChar)); - } - } - - return false; - } - - // keyCode can be from both hard key or soft key. - private boolean processFunctionKeys(int keyCode, boolean realAction) { - // Back key is used to dismiss all popup UI in a soft keyboard. - if (keyCode == KeyEvent.KEYCODE_BACK) { - if (isInputViewShown()) { - if (mSkbContainer.handleBack(realAction)) return true; - } - } - - // Chinese related input is handle separately. - if (mInputModeSwitcher.isChineseText()) { - return false; - } - - if (null != mCandidatesContainer && mCandidatesContainer.isShown() - && !mDecInfo.isCandidatesListEmpty()) { - if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { - if (!realAction) return true; - - chooseCandidate(-1); - return true; - } - - if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { - if (!realAction) return true; - mCandidatesContainer.activeCurseBackward(); - return true; - } - - if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - if (!realAction) return true; - mCandidatesContainer.activeCurseForward(); - return true; - } - - if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { - if (!realAction) return true; - mCandidatesContainer.pageBackward(false, true); - return true; - } - - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - if (!realAction) return true; - mCandidatesContainer.pageForward(false, true); - return true; - } - - if (keyCode == KeyEvent.KEYCODE_DEL && - ImeState.STATE_PREDICT == mImeState) { - if (!realAction) return true; - resetToIdleState(false); - return true; - } - } else { - if (keyCode == KeyEvent.KEYCODE_DEL) { - if (!realAction) return true; - if (SIMULATE_KEY_DELETE) { - simulateKeyEventDownUp(keyCode); - } else { - getCurrentInputConnection().deleteSurroundingText(1, 0); - } - return true; - } - if (keyCode == KeyEvent.KEYCODE_ENTER) { - if (!realAction) return true; - sendKeyChar('\n'); - return true; - } - if (keyCode == KeyEvent.KEYCODE_SPACE) { - if (!realAction) return true; - sendKeyChar(' '); - return true; - } - } - - return false; - } - - private boolean processStateIdle(int keyChar, int keyCode, KeyEvent event, - boolean realAction) { - // In this status, when user presses keys in [a..z], the status will - // change to input state. - if (keyChar >= 'a' && keyChar <= 'z' && !event.isAltPressed()) { - if (!realAction) return true; - mDecInfo.addSplChar((char) keyChar, true); - chooseAndUpdate(-1); - return true; - } else if (keyCode == KeyEvent.KEYCODE_DEL) { - if (!realAction) return true; - if (SIMULATE_KEY_DELETE) { - simulateKeyEventDownUp(keyCode); - } else { - getCurrentInputConnection().deleteSurroundingText(1, 0); - } - return true; - } else if (keyCode == KeyEvent.KEYCODE_ENTER) { - if (!realAction) return true; - sendKeyChar('\n'); - return true; - } else if (keyCode == KeyEvent.KEYCODE_ALT_LEFT - || keyCode == KeyEvent.KEYCODE_ALT_RIGHT - || keyCode == KeyEvent.KEYCODE_SHIFT_LEFT - || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { - return true; - } else if (event.isAltPressed()) { - char fullwidth_char = KeyMapDream.getChineseLabel(keyCode); - if (0 != fullwidth_char) { - if (realAction) { - String result = String.valueOf(fullwidth_char); - commitResultText(result); - } - return true; - } else { - if (keyCode >= KeyEvent.KEYCODE_A - && keyCode <= KeyEvent.KEYCODE_Z) { - return true; - } - } - } else if (keyChar != 0 && keyChar != '\t') { - if (realAction) { - if (keyChar == ',' || keyChar == '.') { - inputCommaPeriod("", keyChar, false, ImeState.STATE_IDLE); - } else { - if (0 != keyChar) { - String result = String.valueOf((char) keyChar); - commitResultText(result); - } - } - } - return true; - } - return false; - } - - private boolean processStateInput(int keyChar, int keyCode, KeyEvent event, - boolean realAction) { - if (keyChar >= 'a' && keyChar <= 'z' || keyChar == '\'' - && !mDecInfo.charBeforeCursorIsSeparator() - || keyCode == KeyEvent.KEYCODE_DEL) { - if (!realAction) return true; - return processSurfaceChange(keyChar, keyCode); - } else if (keyChar == ',' || keyChar == '.') { - if (!realAction) return true; - inputCommaPeriod(mDecInfo.getCurrentFullSent(mCandidatesContainer - .getActiveCandiatePos()), keyChar, true, - ImeState.STATE_IDLE); - return true; - } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP - || keyCode == KeyEvent.KEYCODE_DPAD_DOWN - || keyCode == KeyEvent.KEYCODE_DPAD_LEFT - || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - if (!realAction) return true; - - if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { - mCandidatesContainer.activeCurseBackward(); - } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - mCandidatesContainer.activeCurseForward(); - } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { - // If it has been the first page, a up key will shift - // the state to edit composing string. - if (!mCandidatesContainer.pageBackward(false, true)) { - mCandidatesContainer.enableActiveHighlight(false); - changeToStateComposing(true); - updateComposingText(true); - } - } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - mCandidatesContainer.pageForward(false, true); - } - return true; - } else if (keyCode >= KeyEvent.KEYCODE_1 - && keyCode <= KeyEvent.KEYCODE_9) { - if (!realAction) return true; - - int activePos = keyCode - KeyEvent.KEYCODE_1; - int currentPage = mCandidatesContainer.getCurrentPage(); - if (activePos < mDecInfo.getCurrentPageSize(currentPage)) { - activePos = activePos - + mDecInfo.getCurrentPageStart(currentPage); - if (activePos >= 0) { - chooseAndUpdate(activePos); - } - } - return true; - } else if (keyCode == KeyEvent.KEYCODE_ENTER) { - if (!realAction) return true; - if (mInputModeSwitcher.isEnterNoramlState()) { - commitResultText(mDecInfo.getOrigianlSplStr().toString()); - resetToIdleState(true); - } else { - commitResultText(mDecInfo - .getCurrentFullSent(mCandidatesContainer - .getActiveCandiatePos())); - sendKeyChar('\n'); - resetToIdleState(true); - } - return true; - } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER - || keyCode == KeyEvent.KEYCODE_SPACE) { - if (!realAction) return true; - chooseCandidate(-1); - return true; - } else if (keyCode == KeyEvent.KEYCODE_BACK) { - if (!realAction) return true; - resetToIdleState(true); - requestHideSelf(0); - return true; - } - return false; - } - - private boolean processStatePredict(int keyChar, int keyCode, - KeyEvent event, boolean realAction) { - if (!realAction) return true; - - // In this status, when user presses keys in [a..z], the status will - // change to input state. - if (keyChar >= 'a' && keyChar <= 'z') { - changeToStateInput(true); - mDecInfo.addSplChar((char) keyChar, true); - chooseAndUpdate(-1); - } else if (keyChar == ',' || keyChar == '.') { - inputCommaPeriod("", keyChar, true, ImeState.STATE_IDLE); - } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP - || keyCode == KeyEvent.KEYCODE_DPAD_DOWN - || keyCode == KeyEvent.KEYCODE_DPAD_LEFT - || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { - mCandidatesContainer.activeCurseBackward(); - } - if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - mCandidatesContainer.activeCurseForward(); - } - if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { - mCandidatesContainer.pageBackward(false, true); - } - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - mCandidatesContainer.pageForward(false, true); - } - } else if (keyCode == KeyEvent.KEYCODE_DEL) { - resetToIdleState(false); - } else if (keyCode == KeyEvent.KEYCODE_BACK) { - resetToIdleState(false); - requestHideSelf(0); - } else if (keyCode >= KeyEvent.KEYCODE_1 - && keyCode <= KeyEvent.KEYCODE_9) { - int activePos = keyCode - KeyEvent.KEYCODE_1; - int currentPage = mCandidatesContainer.getCurrentPage(); - if (activePos < mDecInfo.getCurrentPageSize(currentPage)) { - activePos = activePos - + mDecInfo.getCurrentPageStart(currentPage); - if (activePos >= 0) { - chooseAndUpdate(activePos); - } - } - } else if (keyCode == KeyEvent.KEYCODE_ENTER) { - sendKeyChar('\n'); - resetToIdleState(false); - } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER - || keyCode == KeyEvent.KEYCODE_SPACE) { - chooseCandidate(-1); - } - - return true; - } - - private boolean processStateEditComposing(int keyChar, int keyCode, - KeyEvent event, boolean realAction) { - if (!realAction) return true; - - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - if (!mDecInfo.selectionFinished()) { - changeToStateInput(true); - } - } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT - || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - mComposingView.moveCursor(keyCode); - } else if ((keyCode == KeyEvent.KEYCODE_ENTER && mInputModeSwitcher - .isEnterNoramlState()) - || keyCode == KeyEvent.KEYCODE_DPAD_CENTER - || keyCode == KeyEvent.KEYCODE_SPACE) { - ComposingView.ComposingStatus cmpsvStatus = mComposingView - .getComposingStatus(); - - if (ComposingView.ComposingStatus.SHOW_STRING_LOWERCASE == cmpsvStatus) { - String str = mDecInfo.getOrigianlSplStr().toString(); - if (!tryInputRawUnicode(str)) { - commitResultText(str); - } - } else if (ComposingView.ComposingStatus.EDIT_PINYIN == cmpsvStatus) { - String str = mDecInfo.getComposingStr(); - if (!tryInputRawUnicode(str)) { - commitResultText(str); - } - } else { - commitResultText(mDecInfo.getComposingStr()); - } - resetToIdleState(false); - } else if (keyCode == KeyEvent.KEYCODE_ENTER - && !mInputModeSwitcher.isEnterNoramlState()) { - String retStr; - if (!mDecInfo.isCandidatesListEmpty()) { - retStr = mDecInfo.getCurrentFullSent(mCandidatesContainer - .getActiveCandiatePos()); - } else { - retStr = mDecInfo.getComposingStr(); - } - commitResultText(retStr); - sendKeyChar('\n'); - resetToIdleState(false); - } else if (keyCode == KeyEvent.KEYCODE_BACK) { - resetToIdleState(false); - requestHideSelf(0); - return true; - } else { - return processSurfaceChange(keyChar, keyCode); - } - return true; - } - - private boolean tryInputRawUnicode(String str) { - if (str.length() > 7) { - if (str.substring(0, 7).compareTo("unicode") == 0) { - try { - String digitStr = str.substring(7); - int startPos = 0; - int radix = 10; - if (digitStr.length() > 2 && digitStr.charAt(0) == '0' - && digitStr.charAt(1) == 'x') { - startPos = 2; - radix = 16; - } - digitStr = digitStr.substring(startPos); - int unicode = Integer.parseInt(digitStr, radix); - if (unicode > 0) { - char low = (char) (unicode & 0x0000ffff); - char high = (char) ((unicode & 0xffff0000) >> 16); - commitResultText(String.valueOf(low)); - if (0 != high) { - commitResultText(String.valueOf(high)); - } - } - return true; - } catch (NumberFormatException e) { - return false; - } - } else if (str.substring(str.length() - 7, str.length()).compareTo( - "unicode") == 0) { - String resultStr = ""; - for (int pos = 0; pos < str.length() - 7; pos++) { - if (pos > 0) { - resultStr += " "; - } - - resultStr += "0x" + Integer.toHexString(str.charAt(pos)); - } - commitResultText(String.valueOf(resultStr)); - return true; - } - } - return false; - } - - private boolean processSurfaceChange(int keyChar, int keyCode) { - if (mDecInfo.isSplStrFull() && KeyEvent.KEYCODE_DEL != keyCode) { - return true; - } - - if ((keyChar >= 'a' && keyChar <= 'z') - || (keyChar == '\'' && !mDecInfo.charBeforeCursorIsSeparator()) - || (((keyChar >= '0' && keyChar <= '9') || keyChar == ' ') && ImeState.STATE_COMPOSING == mImeState)) { - mDecInfo.addSplChar((char) keyChar, false); - chooseAndUpdate(-1); - } else if (keyCode == KeyEvent.KEYCODE_DEL) { - mDecInfo.prepareDeleteBeforeCursor(); - chooseAndUpdate(-1); - } - return true; - } - - private void changeToStateComposing(boolean updateUi) { - mImeState = ImeState.STATE_COMPOSING; - if (!updateUi) return; - - if (null != mSkbContainer && mSkbContainer.isShown()) { - mSkbContainer.toggleCandidateMode(true); - } - } - - private void changeToStateInput(boolean updateUi) { - mImeState = ImeState.STATE_INPUT; - if (!updateUi) return; - - if (null != mSkbContainer && mSkbContainer.isShown()) { - mSkbContainer.toggleCandidateMode(true); - } - showCandidateWindow(true); - } - - private void simulateKeyEventDownUp(int keyCode) { - InputConnection ic = getCurrentInputConnection(); - if (null == ic) return; - - ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); - ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); - } - - private void commitResultText(String resultText) { - InputConnection ic = getCurrentInputConnection(); - if (null != ic) ic.commitText(resultText, resultText.length()); - if (null != mComposingView) { - mComposingView.setVisibility(View.INVISIBLE); - mComposingView.invalidate(); - } - } - - private void updateComposingText(boolean visible) { - if (!visible) { - mComposingView.setVisibility(View.INVISIBLE); - } else { - mComposingView.setDecodingInfo(mDecInfo, mImeState); - mComposingView.setVisibility(View.VISIBLE); - } - mComposingView.invalidate(); - } - - private void inputCommaPeriod(String preEdit, int keyChar, - boolean dismissCandWindow, ImeState nextState) { - if (keyChar == ',') - preEdit += '\uff0c'; - else if (keyChar == '.') - preEdit += '\u3002'; - else - return; - commitResultText(preEdit); - if (dismissCandWindow) resetCandidateWindow(); - mImeState = nextState; - } - - private void resetToIdleState(boolean resetInlineText) { - if (ImeState.STATE_IDLE == mImeState) return; - - mImeState = ImeState.STATE_IDLE; - mDecInfo.reset(); - - if (null != mComposingView) mComposingView.reset(); - if (resetInlineText) commitResultText(""); - resetCandidateWindow(); - } - - private void chooseAndUpdate(int candId) { - if (!mInputModeSwitcher.isChineseText()) { - String choice = mDecInfo.getCandidate(candId); - if (null != choice) { - commitResultText(choice); - } - resetToIdleState(false); - return; - } - - if (ImeState.STATE_PREDICT != mImeState) { - // Get result candidate list, if choice_id < 0, do a new decoding. - // If choice_id >=0, select the candidate, and get the new candidate - // list. - mDecInfo.chooseDecodingCandidate(candId); - } else { - // Choose a prediction item. - mDecInfo.choosePredictChoice(candId); - } - - if (mDecInfo.getComposingStr().length() > 0) { - String resultStr; - resultStr = mDecInfo.getComposingStrActivePart(); - - // choiceId >= 0 means user finishes a choice selection. - if (candId >= 0 && mDecInfo.canDoPrediction()) { - commitResultText(resultStr); - mImeState = ImeState.STATE_PREDICT; - if (null != mSkbContainer && mSkbContainer.isShown()) { - mSkbContainer.toggleCandidateMode(false); - } - // Try to get the prediction list. - if (Settings.getPrediction()) { - InputConnection ic = getCurrentInputConnection(); - if (null != ic) { - CharSequence cs = ic.getTextBeforeCursor(3, 0); - if (null != cs) { - mDecInfo.preparePredicts(cs); - } - } - } else { - mDecInfo.resetCandidates(); - } - - if (mDecInfo.mCandidatesList.size() > 0) { - showCandidateWindow(false); - } else { - resetToIdleState(false); - } - } else { - if (ImeState.STATE_IDLE == mImeState) { - if (mDecInfo.getSplStrDecodedLen() == 0) { - changeToStateComposing(true); - } else { - changeToStateInput(true); - } - } else { - if (mDecInfo.selectionFinished()) { - changeToStateComposing(true); - } - } - showCandidateWindow(true); - } - } else { - resetToIdleState(false); - } - } - - // If activeCandNo is less than 0, get the current active candidate number - // from candidate view, otherwise use activeCandNo. - private void chooseCandidate(int activeCandNo) { - if (activeCandNo < 0) { - activeCandNo = mCandidatesContainer.getActiveCandiatePos(); - } - if (activeCandNo >= 0) { - chooseAndUpdate(activeCandNo); - } - } - - private boolean startPinyinDecoderService() { - if (null == mDecInfo.mIPinyinDecoderService) { - Intent serviceIntent = new Intent(); - serviceIntent.setClass(this, PinyinDecoderService.class); - - if (null == mPinyinDecoderServiceConnection) { - mPinyinDecoderServiceConnection = new PinyinDecoderServiceConnection(); - } - - // Bind service - if (bindService(serviceIntent, mPinyinDecoderServiceConnection, - Context.BIND_AUTO_CREATE)) { - return true; - } else { - return false; - } - } - return true; - } - - @Override - public View onCreateCandidatesView() { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onCreateCandidatesView."); - } - - LayoutInflater inflater = getLayoutInflater(); - // Inflate the floating container view - mFloatingContainer = (LinearLayout) inflater.inflate( - R.layout.floating_container, null); - - // The first child is the composing view. - mComposingView = (ComposingView) mFloatingContainer.getChildAt(0); - - mCandidatesContainer = (CandidatesContainer) inflater.inflate( - R.layout.candidates_container, null); - - // Create balloon hint for candidates view. - mCandidatesBalloon = new BalloonHint(this, mCandidatesContainer, - MeasureSpec.UNSPECIFIED); - mCandidatesBalloon.setBalloonBackground(getResources().getDrawable( - R.drawable.candidate_balloon_bg)); - mCandidatesContainer.initialize(mChoiceNotifier, mCandidatesBalloon, - mGestureDetectorCandidates); - - // The floating window - if (null != mFloatingWindow && mFloatingWindow.isShowing()) { - mFloatingWindowTimer.cancelShowing(); - mFloatingWindow.dismiss(); - } - mFloatingWindow = new PopupWindow(this); - mFloatingWindow.setClippingEnabled(false); - mFloatingWindow.setBackgroundDrawable(null); - mFloatingWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - mFloatingWindow.setContentView(mFloatingContainer); - - setCandidatesViewShown(true); - return mCandidatesContainer; - } - - public void responseSoftKeyEvent(SoftKey sKey) { - if (null == sKey) return; - - InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; - - int keyCode = sKey.getKeyCode(); - // Process some general keys, including KEYCODE_DEL, KEYCODE_SPACE, - // KEYCODE_ENTER and KEYCODE_DPAD_CENTER. - if (sKey.isKeyCodeKey()) { - if (processFunctionKeys(keyCode, true)) return; - } - - if (sKey.isUserDefKey()) { - updateIcon(mInputModeSwitcher.switchModeForUserKey(keyCode)); - resetToIdleState(true); - mSkbContainer.updateInputMode(); - } else { - if (sKey.isKeyCodeKey()) { - KeyEvent eDown = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, - keyCode, 0, 0, 0, 0, KeyEvent.FLAG_SOFT_KEYBOARD); - KeyEvent eUp = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, - 0, 0, 0, 0, KeyEvent.FLAG_SOFT_KEYBOARD); - - onKeyDown(keyCode, eDown); - onKeyUp(keyCode, eUp); - } else if (sKey.isUniStrKey()) { - boolean kUsed = false; - String keyLabel = sKey.getKeyLabel(); - if (mInputModeSwitcher.isChineseTextWithSkb() - && (ImeState.STATE_INPUT == mImeState || ImeState.STATE_COMPOSING == mImeState)) { - if (mDecInfo.length() > 0 && keyLabel.length() == 1 - && keyLabel.charAt(0) == '\'') { - processSurfaceChange('\'', 0); - kUsed = true; - } - } - if (!kUsed) { - if (ImeState.STATE_INPUT == mImeState) { - commitResultText(mDecInfo - .getCurrentFullSent(mCandidatesContainer - .getActiveCandiatePos())); - } else if (ImeState.STATE_COMPOSING == mImeState) { - commitResultText(mDecInfo.getComposingStr()); - } - commitResultText(keyLabel); - resetToIdleState(false); - } - } - - // If the current soft keyboard is not sticky, IME needs to go - // back to the previous soft keyboard automatically. - if (!mSkbContainer.isCurrentSkbSticky()) { - updateIcon(mInputModeSwitcher.requestBackToPreviousSkb()); - resetToIdleState(true); - mSkbContainer.updateInputMode(); - } - } - } - - private void showCandidateWindow(boolean showComposingView) { - if (mEnvironment.needDebug()) { - Log.d(TAG, "Candidates window is shown. Parent = " - + mCandidatesContainer); - } - - setCandidatesViewShown(true); - - if (null != mSkbContainer) mSkbContainer.requestLayout(); - - if (null == mCandidatesContainer) { - resetToIdleState(false); - return; - } - - updateComposingText(showComposingView); - mCandidatesContainer.showCandidates(mDecInfo, - ImeState.STATE_COMPOSING != mImeState); - mFloatingWindowTimer.postShowFloatingWindow(); - } - - private void dismissCandidateWindow() { - if (mEnvironment.needDebug()) { - Log.d(TAG, "Candidates window is to be dismissed"); - } - if (null == mCandidatesContainer) return; - try { - mFloatingWindowTimer.cancelShowing(); - mFloatingWindow.dismiss(); - } catch (Exception e) { - Log.e(TAG, "Fail to show the PopupWindow."); - } - setCandidatesViewShown(false); - - if (null != mSkbContainer && mSkbContainer.isShown()) { - mSkbContainer.toggleCandidateMode(false); - } - } - - private void resetCandidateWindow() { - if (mEnvironment.needDebug()) { - Log.d(TAG, "Candidates window is to be reset"); - } - if (null == mCandidatesContainer) return; - try { - mFloatingWindowTimer.cancelShowing(); - mFloatingWindow.dismiss(); - } catch (Exception e) { - Log.e(TAG, "Fail to show the PopupWindow."); - } - - if (null != mSkbContainer && mSkbContainer.isShown()) { - mSkbContainer.toggleCandidateMode(false); - } - - mDecInfo.resetCandidates(); - - if (null != mCandidatesContainer && mCandidatesContainer.isShown()) { - showCandidateWindow(false); - } - } - - private void updateIcon(int iconId) { - if (iconId > 0) { - showStatusIcon(iconId); - } else { - hideStatusIcon(); - } - } - - @Override - public View onCreateInputView() { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onCreateInputView."); - } - LayoutInflater inflater = getLayoutInflater(); - mSkbContainer = (SkbContainer) inflater.inflate(R.layout.skb_container, - null); - mSkbContainer.setService(this); - mSkbContainer.setInputModeSwitcher(mInputModeSwitcher); - mSkbContainer.setGestureDetector(mGestureDetectorSkb); - return mSkbContainer; - } - - @Override - public void onStartInput(EditorInfo editorInfo, boolean restarting) { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onStartInput " + " ccontentType: " - + String.valueOf(editorInfo.inputType) + " Restarting:" - + String.valueOf(restarting)); - } - updateIcon(mInputModeSwitcher.requestInputWithHkb(editorInfo)); - resetToIdleState(false); - } - - @Override - public void onStartInputView(EditorInfo editorInfo, boolean restarting) { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onStartInputView " + " contentType: " - + String.valueOf(editorInfo.inputType) + " Restarting:" - + String.valueOf(restarting)); - } - updateIcon(mInputModeSwitcher.requestInputWithSkb(editorInfo)); - resetToIdleState(false); - mSkbContainer.updateInputMode(); - setCandidatesViewShown(false); - } - - @Override - public void onFinishInputView(boolean finishingInput) { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onFinishInputView."); - } - resetToIdleState(false); - super.onFinishInputView(finishingInput); - } - - @Override - public void onFinishInput() { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onFinishInput."); - } - resetToIdleState(false); - super.onFinishInput(); - } - - @Override - public void onFinishCandidatesView(boolean finishingInput) { - if (mEnvironment.needDebug()) { - Log.d(TAG, "onFinishCandidateView."); - } - resetToIdleState(false); - super.onFinishCandidatesView(finishingInput); - } - - @Override public void onDisplayCompletions(CompletionInfo[] completions) { - if (!isFullscreenMode()) return; - if (null == completions || completions.length <= 0) return; - if (null == mSkbContainer || !mSkbContainer.isShown()) return; - - if (!mInputModeSwitcher.isChineseText() || - ImeState.STATE_IDLE == mImeState || - ImeState.STATE_PREDICT == mImeState) { - mImeState = ImeState.STATE_APP_COMPLETION; - mDecInfo.prepareAppCompletions(completions); - showCandidateWindow(false); - } - } - - private void onChoiceTouched(int activeCandNo) { - if (mImeState == ImeState.STATE_COMPOSING) { - changeToStateInput(true); - } else if (mImeState == ImeState.STATE_INPUT - || mImeState == ImeState.STATE_PREDICT) { - chooseCandidate(activeCandNo); - } else if (mImeState == ImeState.STATE_APP_COMPLETION) { - if (null != mDecInfo.mAppCompletions && activeCandNo >= 0 && - activeCandNo < mDecInfo.mAppCompletions.length) { - CompletionInfo ci = mDecInfo.mAppCompletions[activeCandNo]; - if (null != ci) { - InputConnection ic = getCurrentInputConnection(); - ic.commitCompletion(ci); - } - } - resetToIdleState(false); - } - } - - @Override - public void requestHideSelf(int flags) { - if (mEnvironment.needDebug()) { - Log.d(TAG, "DimissSoftInput."); - } - dismissCandidateWindow(); - if (null != mSkbContainer && mSkbContainer.isShown()) { - mSkbContainer.dismissPopups(); - } - super.requestHideSelf(flags); - } - - public void showOptionsMenu() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setCancelable(true); - builder.setIcon(R.drawable.app_icon); - builder.setNegativeButton(android.R.string.cancel, null); - CharSequence itemSettings = getString(R.string.ime_settings_activity_name); - CharSequence itemInputMethod = getString(com.android.internal.R.string.inputMethod); - builder.setItems(new CharSequence[] {itemSettings, itemInputMethod}, - new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface di, int position) { - di.dismiss(); - switch (position) { - case 0: - launchSettings(); - break; - case 1: - InputMethodManager.getInstance(PinyinIME.this) - .showInputMethodPicker(); - break; - } - } - }); - builder.setTitle(getString(R.string.ime_name)); - mOptionsDialog = builder.create(); - Window window = mOptionsDialog.getWindow(); - WindowManager.LayoutParams lp = window.getAttributes(); - lp.token = mSkbContainer.getWindowToken(); - lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; - window.setAttributes(lp); - window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); - mOptionsDialog.show(); - } - - private void launchSettings() { - Intent intent = new Intent(); - intent.setClass(PinyinIME.this, SettingsActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - - private class PopupTimer extends Handler implements Runnable { - private int mParentLocation[] = new int[2]; - - void postShowFloatingWindow() { - mFloatingContainer.measure(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - mFloatingWindow.setWidth(mFloatingContainer.getMeasuredWidth()); - mFloatingWindow.setHeight(mFloatingContainer.getMeasuredHeight()); - post(this); - } - - void cancelShowing() { - if (mFloatingWindow.isShowing()) { - mFloatingWindow.dismiss(); - } - removeCallbacks(this); - } - - public void run() { - mCandidatesContainer.getLocationInWindow(mParentLocation); - - if (!mFloatingWindow.isShowing()) { - mFloatingWindow.showAtLocation(mCandidatesContainer, - Gravity.LEFT | Gravity.TOP, mParentLocation[0], - mParentLocation[1] -mFloatingWindow.getHeight()); - } else { - mFloatingWindow - .update(mParentLocation[0], - mParentLocation[1] - mFloatingWindow.getHeight(), - mFloatingWindow.getWidth(), - mFloatingWindow.getHeight()); - } - } - } - - /** - * Used to notify IME that the user selects a candidate or performs an - * gesture. - */ - public class ChoiceNotifier extends Handler implements - CandidateViewListener { - PinyinIME mIme; - - ChoiceNotifier(PinyinIME ime) { - mIme = ime; - } - - public void onClickChoice(int choiceId) { - if (choiceId >= 0) { - mIme.onChoiceTouched(choiceId); - } - } - - public void onToLeftGesture() { - if (ImeState.STATE_COMPOSING == mImeState) { - changeToStateInput(true); - } - mCandidatesContainer.pageForward(true, false); - } - - public void onToRightGesture() { - if (ImeState.STATE_COMPOSING == mImeState) { - changeToStateInput(true); - } - mCandidatesContainer.pageBackward(true, false); - } - - public void onToTopGesture() { - } - - public void onToBottomGesture() { - } - } - - public class OnGestureListener extends - GestureDetector.SimpleOnGestureListener { - /** - * When user presses and drags, the minimum x-distance to make a - * response to the drag event. - */ - private static final int MIN_X_FOR_DRAG = 60; - - /** - * When user presses and drags, the minimum y-distance to make a - * response to the drag event. - */ - private static final int MIN_Y_FOR_DRAG = 40; - - /** - * Velocity threshold for a screen-move gesture. If the minimum - * x-velocity is less than it, no gesture. - */ - static private final float VELOCITY_THRESHOLD_X1 = 0.3f; - - /** - * Velocity threshold for a screen-move gesture. If the maximum - * x-velocity is less than it, no gesture. - */ - static private final float VELOCITY_THRESHOLD_X2 = 0.7f; - - /** - * Velocity threshold for a screen-move gesture. If the minimum - * y-velocity is less than it, no gesture. - */ - static private final float VELOCITY_THRESHOLD_Y1 = 0.2f; - - /** - * Velocity threshold for a screen-move gesture. If the maximum - * y-velocity is less than it, no gesture. - */ - static private final float VELOCITY_THRESHOLD_Y2 = 0.45f; - - /** If it false, we will not response detected gestures. */ - private boolean mReponseGestures; - - /** The minimum X velocity observed in the gesture. */ - private float mMinVelocityX = Float.MAX_VALUE; - - /** The minimum Y velocity observed in the gesture. */ - private float mMinVelocityY = Float.MAX_VALUE; - - /** The first down time for the series of touch events for an action. */ - private long mTimeDown; - - /** The last time when onScroll() is called. */ - private long mTimeLastOnScroll; - - /** This flag used to indicate that this gesture is not a gesture. */ - private boolean mNotGesture; - - /** This flag used to indicate that this gesture has been recognized. */ - private boolean mGestureRecognized; - - public OnGestureListener(boolean reponseGestures) { - mReponseGestures = reponseGestures; - } - - @Override - public boolean onDown(MotionEvent e) { - mMinVelocityX = Integer.MAX_VALUE; - mMinVelocityY = Integer.MAX_VALUE; - mTimeDown = e.getEventTime(); - mTimeLastOnScroll = mTimeDown; - mNotGesture = false; - mGestureRecognized = false; - return false; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, - float distanceX, float distanceY) { - if (mNotGesture) return false; - if (mGestureRecognized) return true; - - if (Math.abs(e1.getX() - e2.getX()) < MIN_X_FOR_DRAG - && Math.abs(e1.getY() - e2.getY()) < MIN_Y_FOR_DRAG) - return false; - - long timeNow = e2.getEventTime(); - long spanTotal = timeNow - mTimeDown; - long spanThis = timeNow - mTimeLastOnScroll; - if (0 == spanTotal) spanTotal = 1; - if (0 == spanThis) spanThis = 1; - - float vXTotal = (e2.getX() - e1.getX()) / spanTotal; - float vYTotal = (e2.getY() - e1.getY()) / spanTotal; - - // The distances are from the current point to the previous one. - float vXThis = -distanceX / spanThis; - float vYThis = -distanceY / spanThis; - - float kX = vXTotal * vXThis; - float kY = vYTotal * vYThis; - float k1 = kX + kY; - float k2 = Math.abs(kX) + Math.abs(kY); - - if (k1 / k2 < 0.8) { - mNotGesture = true; - return false; - } - float absVXTotal = Math.abs(vXTotal); - float absVYTotal = Math.abs(vYTotal); - if (absVXTotal < mMinVelocityX) { - mMinVelocityX = absVXTotal; - } - if (absVYTotal < mMinVelocityY) { - mMinVelocityY = absVYTotal; - } - - if (mMinVelocityX < VELOCITY_THRESHOLD_X1 - && mMinVelocityY < VELOCITY_THRESHOLD_Y1) { - mNotGesture = true; - return false; - } - - if (vXTotal > VELOCITY_THRESHOLD_X2 - && absVYTotal < VELOCITY_THRESHOLD_Y2) { - if (mReponseGestures) onDirectionGesture(Gravity.RIGHT); - mGestureRecognized = true; - } else if (vXTotal < -VELOCITY_THRESHOLD_X2 - && absVYTotal < VELOCITY_THRESHOLD_Y2) { - if (mReponseGestures) onDirectionGesture(Gravity.LEFT); - mGestureRecognized = true; - } else if (vYTotal > VELOCITY_THRESHOLD_Y2 - && absVXTotal < VELOCITY_THRESHOLD_X2) { - if (mReponseGestures) onDirectionGesture(Gravity.BOTTOM); - mGestureRecognized = true; - } else if (vYTotal < -VELOCITY_THRESHOLD_Y2 - && absVXTotal < VELOCITY_THRESHOLD_X2) { - if (mReponseGestures) onDirectionGesture(Gravity.TOP); - mGestureRecognized = true; - } - - mTimeLastOnScroll = timeNow; - return mGestureRecognized; - } - - @Override - public boolean onFling(MotionEvent me1, MotionEvent me2, - float velocityX, float velocityY) { - return mGestureRecognized; - } - - public void onDirectionGesture(int gravity) { - if (Gravity.NO_GRAVITY == gravity) { - return; - } - - if (Gravity.LEFT == gravity || Gravity.RIGHT == gravity) { - if (mCandidatesContainer.isShown()) { - if (Gravity.LEFT == gravity) { - mCandidatesContainer.pageForward(true, true); - } else { - mCandidatesContainer.pageBackward(true, true); - } - return; - } - } - } - } - - /** - * Connection used for binding to the Pinyin decoding service. - */ - public class PinyinDecoderServiceConnection implements ServiceConnection { - public void onServiceConnected(ComponentName name, IBinder service) { - mDecInfo.mIPinyinDecoderService = IPinyinDecoderService.Stub - .asInterface(service); - } - - public void onServiceDisconnected(ComponentName name) { - } - } - - public enum ImeState { - STATE_BYPASS, STATE_IDLE, STATE_INPUT, STATE_COMPOSING, STATE_PREDICT, - STATE_APP_COMPLETION - } - - public class DecodingInfo { - /** - * Maximum length of the Pinyin string - */ - private static final int PY_STRING_MAX = 28; - - /** - * Maximum number of candidates to display in one page. - */ - private static final int MAX_PAGE_SIZE_DISPLAY = 10; - - /** - * Spelling (Pinyin) string. - */ - private StringBuffer mSurface; - - /** - * Byte buffer used as the Pinyin string parameter for native function - * call. - */ - private byte mPyBuf[]; - - /** - * The length of surface string successfully decoded by engine. - */ - private int mSurfaceDecodedLen; - - /** - * Composing string. - */ - private String mComposingStr; - - /** - * Length of the active composing string. - */ - private int mActiveCmpsLen; - - /** - * Composing string for display, it is copied from mComposingStr, and - * add spaces between spellings. - **/ - private String mComposingStrDisplay; - - /** - * Length of the active composing string for display. - */ - private int mActiveCmpsDisplayLen; - - /** - * The first full sentence choice. - */ - private String mFullSent; - - /** - * Number of characters which have been fixed. - */ - private int mFixedLen; - - /** - * If this flag is true, selection is finished. - */ - private boolean mFinishSelection; - - /** - * The starting position for each spelling. The first one is the number - * of the real starting position elements. - */ - private int mSplStart[]; - - /** - * Editing cursor in mSurface. - */ - private int mCursorPos; - - /** - * Remote Pinyin-to-Hanzi decoding engine service. - */ - private IPinyinDecoderService mIPinyinDecoderService; - - /** - * The complication information suggested by application. - */ - private CompletionInfo[] mAppCompletions; - - /** - * The total number of choices for display. The list may only contains - * the first part. If user tries to navigate to next page which is not - * in the result list, we need to get these items. - **/ - public int mTotalChoicesNum; - - /** - * Candidate list. The first one is the full-sentence candidate. - */ - public List mCandidatesList = new Vector(); - - /** - * Element i stores the starting position of page i. - */ - public Vector mPageStart = new Vector(); - - /** - * Element i stores the number of characters to page i. - */ - public Vector mCnToPage = new Vector(); - - /** - * The position to delete in Pinyin string. If it is less than 0, IME - * will do an incremental search, otherwise IME will do a deletion - * operation. if {@link #mIsPosInSpl} is true, IME will delete the whole - * string for mPosDelSpl-th spelling, otherwise it will only delete - * mPosDelSpl-th character in the Pinyin string. - */ - public int mPosDelSpl = -1; - - /** - * If {@link #mPosDelSpl} is big than or equal to 0, this member is used - * to indicate that whether the postion is counted in spelling id or - * character. - */ - public boolean mIsPosInSpl; - - public DecodingInfo() { - mSurface = new StringBuffer(); - mSurfaceDecodedLen = 0; - } - - public void reset() { - mSurface.delete(0, mSurface.length()); - mSurfaceDecodedLen = 0; - mCursorPos = 0; - mFullSent = ""; - mFixedLen = 0; - mFinishSelection = false; - mComposingStr = ""; - mComposingStrDisplay = ""; - mActiveCmpsLen = 0; - mActiveCmpsDisplayLen = 0; - - resetCandidates(); - } - - public boolean isCandidatesListEmpty() { - return mCandidatesList.size() == 0; - } - - public boolean isSplStrFull() { - if (mSurface.length() >= PY_STRING_MAX - 1) return true; - return false; - } - - public void addSplChar(char ch, boolean reset) { - if (reset) { - mSurface.delete(0, mSurface.length()); - mSurfaceDecodedLen = 0; - mCursorPos = 0; - try { - mIPinyinDecoderService.imResetSearch(); - } catch (RemoteException e) { - } - } - mSurface.insert(mCursorPos, ch); - mCursorPos++; - } - - // Prepare to delete before cursor. We may delete a spelling char if - // the cursor is in the range of unfixed part, delete a whole spelling - // if the cursor in inside the range of the fixed part. - // This function only marks the position used to delete. - public void prepareDeleteBeforeCursor() { - if (mCursorPos > 0) { - int pos; - for (pos = 0; pos < mFixedLen; pos++) { - if (mSplStart[pos + 2] >= mCursorPos - && mSplStart[pos + 1] < mCursorPos) { - mPosDelSpl = pos; - mCursorPos = mSplStart[pos + 1]; - mIsPosInSpl = true; - break; - } - } - if (mPosDelSpl < 0) { - mPosDelSpl = mCursorPos - 1; - mCursorPos--; - mIsPosInSpl = false; - } - } - } - - public int length() { - return mSurface.length(); - } - - public char charAt(int index) { - return mSurface.charAt(index); - } - - public StringBuffer getOrigianlSplStr() { - return mSurface; - } - - public int getSplStrDecodedLen() { - return mSurfaceDecodedLen; - } - - public int[] getSplStart() { - return mSplStart; - } - - public String getComposingStr() { - return mComposingStr; - } - - public String getComposingStrActivePart() { - assert (mActiveCmpsLen <= mComposingStr.length()); - return mComposingStr.substring(0, mActiveCmpsLen); - } - - public int getActiveCmpsLen() { - return mActiveCmpsLen; - } - - public String getComposingStrForDisplay() { - return mComposingStrDisplay; - } - - public int getActiveCmpsDisplayLen() { - return mActiveCmpsDisplayLen; - } - - public String getFullSent() { - return mFullSent; - } - - public String getCurrentFullSent(int activeCandPos) { - String retStr = mFullSent.substring(0, mFixedLen); - retStr += mCandidatesList.get(activeCandPos); - return retStr; - } - - public void resetCandidates() { - mCandidatesList.clear(); - mTotalChoicesNum = 0; - - mPageStart.clear(); - mPageStart.add(0); - mCnToPage.clear(); - mCnToPage.add(0); - } - - public boolean candidatesFromApp() { - return ImeState.STATE_APP_COMPLETION == mImeState; - } - - public boolean canDoPrediction() { - return mComposingStr.length() == mFixedLen; - } - - public boolean selectionFinished() { - return mFinishSelection; - } - - // After the user chooses a candidate, input method will do a - // re-decoding and give the new candidate list. - // If candidate id is less than 0, means user is inputting Pinyin, - // not selecting any choice. - private void chooseDecodingCandidate(int candId) { - if (mImeState != ImeState.STATE_PREDICT) { - resetCandidates(); - int totalChoicesNum = 0; - try { - if (candId < 0) { - if (length() == 0) { - totalChoicesNum = 0; - } else { - if (mPyBuf == null) - mPyBuf = new byte[PY_STRING_MAX]; - for (int i = 0; i < length(); i++) - mPyBuf[i] = (byte) charAt(i); - mPyBuf[length()] = 0; - - if (mPosDelSpl < 0) { - totalChoicesNum = mIPinyinDecoderService - .imSearch(mPyBuf, length()); - } else { - boolean clear_fixed_this_step = true; - if (ImeState.STATE_COMPOSING == mImeState) { - clear_fixed_this_step = false; - } - totalChoicesNum = mIPinyinDecoderService - .imDelSearch(mPosDelSpl, mIsPosInSpl, - clear_fixed_this_step); - mPosDelSpl = -1; - } - } - } else { - totalChoicesNum = mIPinyinDecoderService - .imChoose(candId); - } - } catch (RemoteException e) { - } - updateDecInfoForSearch(totalChoicesNum); - } - } - - private void updateDecInfoForSearch(int totalChoicesNum) { - mTotalChoicesNum = totalChoicesNum; - if (mTotalChoicesNum < 0) { - mTotalChoicesNum = 0; - return; - } - - try { - String pyStr; - - mSplStart = mIPinyinDecoderService.imGetSplStart(); - pyStr = mIPinyinDecoderService.imGetPyStr(false); - mSurfaceDecodedLen = mIPinyinDecoderService.imGetPyStrLen(true); - assert (mSurfaceDecodedLen <= pyStr.length()); - - mFullSent = mIPinyinDecoderService.imGetChoice(0); - mFixedLen = mIPinyinDecoderService.imGetFixedLen(); - - // Update the surface string to the one kept by engine. - mSurface.replace(0, mSurface.length(), pyStr); - - if (mCursorPos > mSurface.length()) - mCursorPos = mSurface.length(); - mComposingStr = mFullSent.substring(0, mFixedLen) - + mSurface.substring(mSplStart[mFixedLen + 1]); - - mActiveCmpsLen = mComposingStr.length(); - if (mSurfaceDecodedLen > 0) { - mActiveCmpsLen = mActiveCmpsLen - - (mSurface.length() - mSurfaceDecodedLen); - } - - // Prepare the display string. - if (0 == mSurfaceDecodedLen) { - mComposingStrDisplay = mComposingStr; - mActiveCmpsDisplayLen = mComposingStr.length(); - } else { - mComposingStrDisplay = mFullSent.substring(0, mFixedLen); - for (int pos = mFixedLen + 1; pos < mSplStart.length - 1; pos++) { - mComposingStrDisplay += mSurface.substring( - mSplStart[pos], mSplStart[pos + 1]); - if (mSplStart[pos + 1] < mSurfaceDecodedLen) { - mComposingStrDisplay += " "; - } - } - mActiveCmpsDisplayLen = mComposingStrDisplay.length(); - if (mSurfaceDecodedLen < mSurface.length()) { - mComposingStrDisplay += mSurface - .substring(mSurfaceDecodedLen); - } - } - - if (mSplStart.length == mFixedLen + 2) { - mFinishSelection = true; - } else { - mFinishSelection = false; - } - } catch (RemoteException e) { - Log.w(TAG, "PinyinDecoderService died", e); - } - // Prepare page 0. - if (!mFinishSelection) { - preparePage(0); - } - } - - private void choosePredictChoice(int choiceId) { - if (ImeState.STATE_PREDICT != mImeState || choiceId < 0 - || choiceId >= mTotalChoicesNum) { - return; - } - - String tmp = mCandidatesList.get(choiceId); - - resetCandidates(); - - mCandidatesList.add(tmp); - mTotalChoicesNum = 1; - - mSurface.replace(0, mSurface.length(), ""); - mCursorPos = 0; - mFullSent = tmp; - mFixedLen = tmp.length(); - mComposingStr = mFullSent; - mActiveCmpsLen = mFixedLen; - - mFinishSelection = true; - } - - public String getCandidate(int candId) { - // Only loaded items can be gotten, so we use mCandidatesList.size() - // instead mTotalChoiceNum. - if (candId < 0 || candId > mCandidatesList.size()) { - return null; - } - return mCandidatesList.get(candId); - } - - private void getCandiagtesForCache() { - int fetchStart = mCandidatesList.size(); - int fetchSize = mTotalChoicesNum - fetchStart; - if (fetchSize > MAX_PAGE_SIZE_DISPLAY) { - fetchSize = MAX_PAGE_SIZE_DISPLAY; - } - try { - List newList = null; - if (ImeState.STATE_INPUT == mImeState || - ImeState.STATE_IDLE == mImeState || - ImeState.STATE_COMPOSING == mImeState){ - newList = mIPinyinDecoderService.imGetChoiceList( - fetchStart, fetchSize, mFixedLen); - } else if (ImeState.STATE_PREDICT == mImeState) { - newList = mIPinyinDecoderService.imGetPredictList( - fetchStart, fetchSize); - } else if (ImeState.STATE_APP_COMPLETION == mImeState) { - newList = new ArrayList(); - if (null != mAppCompletions) { - for (int pos = fetchStart; pos < fetchSize; pos++) { - CompletionInfo ci = mAppCompletions[pos]; - if (null != ci) { - CharSequence s = ci.getText(); - if (null != s) newList.add(s.toString()); - } - } - } - } - mCandidatesList.addAll(newList); - } catch (RemoteException e) { - Log.w(TAG, "PinyinDecoderService died", e); - } - } - - public boolean pageReady(int pageNo) { - // If the page number is less than 0, return false - if (pageNo < 0) return false; - - // Page pageNo's ending information is not ready. - if (mPageStart.size() <= pageNo + 1) { - return false; - } - - return true; - } - - public boolean preparePage(int pageNo) { - // If the page number is less than 0, return false - if (pageNo < 0) return false; - - // Make sure the starting information for page pageNo is ready. - if (mPageStart.size() <= pageNo) { - return false; - } - - // Page pageNo's ending information is also ready. - if (mPageStart.size() > pageNo + 1) { - return true; - } - - // If cached items is enough for page pageNo. - if (mCandidatesList.size() - mPageStart.elementAt(pageNo) >= MAX_PAGE_SIZE_DISPLAY) { - return true; - } - - // Try to get more items from engine - getCandiagtesForCache(); - - // Try to find if there are available new items to display. - // If no new item, return false; - if (mPageStart.elementAt(pageNo) >= mCandidatesList.size()) { - return false; - } - - // If there are new items, return true; - return true; - } - - public void preparePredicts(CharSequence history) { - if (null == history) return; - - resetCandidates(); - - if (Settings.getPrediction()) { - String preEdit = history.toString(); - int predictNum = 0; - if (null != preEdit) { - try { - mTotalChoicesNum = mIPinyinDecoderService - .imGetPredictsNum(preEdit); - } catch (RemoteException e) { - return; - } - } - } - - preparePage(0); - mFinishSelection = false; - } - - private void prepareAppCompletions(CompletionInfo completions[]) { - resetCandidates(); - mAppCompletions = completions; - mTotalChoicesNum = completions.length; - preparePage(0); - mFinishSelection = false; - return; - } - - public int getCurrentPageSize(int currentPage) { - if (mPageStart.size() <= currentPage + 1) return 0; - return mPageStart.elementAt(currentPage + 1) - - mPageStart.elementAt(currentPage); - } - - public int getCurrentPageStart(int currentPage) { - if (mPageStart.size() < currentPage + 1) return mTotalChoicesNum; - return mPageStart.elementAt(currentPage); - } - - public boolean pageForwardable(int currentPage) { - if (mPageStart.size() <= currentPage + 1) return false; - if (mPageStart.elementAt(currentPage + 1) >= mTotalChoicesNum) { - return false; - } - return true; - } - - public boolean pageBackwardable(int currentPage) { - if (currentPage > 0) return true; - return false; - } - - public boolean charBeforeCursorIsSeparator() { - int len = mSurface.length(); - if (mCursorPos > len) return false; - if (mCursorPos > 0 && mSurface.charAt(mCursorPos - 1) == '\'') { - return true; - } - return false; - } - - public int getCursorPos() { - return mCursorPos; - } - - public int getCursorPosInCmps() { - int cursorPos = mCursorPos; - int fixedLen = 0; - - for (int hzPos = 0; hzPos < mFixedLen; hzPos++) { - if (mCursorPos >= mSplStart[hzPos + 2]) { - cursorPos -= mSplStart[hzPos + 2] - mSplStart[hzPos + 1]; - cursorPos += 1; - } - } - return cursorPos; - } - - public int getCursorPosInCmpsDisplay() { - int cursorPos = getCursorPosInCmps(); - // +2 is because: one for mSplStart[0], which is used for other - // purpose(The length of the segmentation string), and another - // for the first spelling which does not need a space before it. - for (int pos = mFixedLen + 2; pos < mSplStart.length - 1; pos++) { - if (mCursorPos <= mSplStart[pos]) { - break; - } else { - cursorPos++; - } - } - return cursorPos; - } - - public void moveCursorToEdge(boolean left) { - if (left) - mCursorPos = 0; - else - mCursorPos = mSurface.length(); - } - - // Move cursor. If offset is 0, this function can be used to adjust - // the cursor into the bounds of the string. - public void moveCursor(int offset) { - if (offset > 1 || offset < -1) return; - - if (offset != 0) { - int hzPos = 0; - for (hzPos = 0; hzPos <= mFixedLen; hzPos++) { - if (mCursorPos == mSplStart[hzPos + 1]) { - if (offset < 0) { - if (hzPos > 0) { - offset = mSplStart[hzPos] - - mSplStart[hzPos + 1]; - } - } else { - if (hzPos < mFixedLen) { - offset = mSplStart[hzPos + 2] - - mSplStart[hzPos + 1]; - } - } - break; - } - } - } - mCursorPos += offset; - if (mCursorPos < 0) { - mCursorPos = 0; - } else if (mCursorPos > mSurface.length()) { - mCursorPos = mSurface.length(); - } - } - - public int getSplNum() { - return mSplStart[0]; - } - - public int getFixedLen() { - return mFixedLen; - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/Settings.java b/PinyinIME/src/com/android/inputmethod/pinyin/Settings.java deleted file mode 100644 index c05f605..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/Settings.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; - -/** - * Class used to maintain settings. - */ -public class Settings { - private static final String ANDPY_CONFS_KEYSOUND_KEY = "Sound"; - private static final String ANDPY_CONFS_VIBRATE_KEY = "Vibrate"; - private static final String ANDPY_CONFS_PREDICTION_KEY = "Prediction"; - - private static boolean mKeySound; - private static boolean mVibrate; - private static boolean mPrediction; - - private static Settings mInstance = null; - - private static int mRefCount = 0; - - private static SharedPreferences mSharedPref = null; - - protected Settings(SharedPreferences pref) { - mSharedPref = pref; - initConfs(); - } - - public static Settings getInstance(SharedPreferences pref) { - if (mInstance == null) { - mInstance = new Settings(pref); - } - assert (pref == mSharedPref); - mRefCount++; - return mInstance; - } - - public static void writeBack() { - Editor editor = mSharedPref.edit(); - editor.putBoolean(ANDPY_CONFS_VIBRATE_KEY, mVibrate); - editor.putBoolean(ANDPY_CONFS_KEYSOUND_KEY, mKeySound); - editor.putBoolean(ANDPY_CONFS_PREDICTION_KEY, mPrediction); - editor.commit(); - } - - public static void releaseInstance() { - mRefCount--; - if (mRefCount == 0) { - mInstance = null; - } - } - - private void initConfs() { - mKeySound = mSharedPref.getBoolean(ANDPY_CONFS_KEYSOUND_KEY, true); - mVibrate = mSharedPref.getBoolean(ANDPY_CONFS_VIBRATE_KEY, false); - mPrediction = mSharedPref.getBoolean(ANDPY_CONFS_PREDICTION_KEY, true); - } - - public static boolean getKeySound() { - return mKeySound; - } - - public static void setKeySound(boolean v) { - if (mKeySound == v) return; - mKeySound = v; - } - - public static boolean getVibrate() { - return mVibrate; - } - - public static void setVibrate(boolean v) { - if (mVibrate == v) return; - mVibrate = v; - } - - public static boolean getPrediction() { - return mPrediction; - } - - public static void setPrediction(boolean v) { - if (mPrediction == v) return; - mPrediction = v; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SettingsActivity.java b/PinyinIME/src/com/android/inputmethod/pinyin/SettingsActivity.java deleted file mode 100644 index 7d23d8e..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SettingsActivity.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import java.util.List; - -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceGroup; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import com.android.inputmethod.pinyin.Settings; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; - -/** - * Setting activity of Pinyin IME. - */ -public class SettingsActivity extends PreferenceActivity implements - Preference.OnPreferenceChangeListener { - - private static String TAG = "SettingsActivity"; - - private CheckBoxPreference mKeySoundPref; - private CheckBoxPreference mVibratePref; - private CheckBoxPreference mPredictionPref; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.settings); - - PreferenceScreen prefSet = getPreferenceScreen(); - - mKeySoundPref = (CheckBoxPreference) prefSet - .findPreference(getString(R.string.setting_sound_key)); - mVibratePref = (CheckBoxPreference) prefSet - .findPreference(getString(R.string.setting_vibrate_key)); - mPredictionPref = (CheckBoxPreference) prefSet - .findPreference(getString(R.string.setting_prediction_key)); - - prefSet.setOnPreferenceChangeListener(this); - - Settings.getInstance(PreferenceManager - .getDefaultSharedPreferences(getApplicationContext())); - - updatePreference(prefSet, getString(R.string.setting_advanced_key)); - - updateWidgets(); - } - - @Override - protected void onResume() { - super.onResume(); - updateWidgets(); - } - - @Override - protected void onDestroy() { - Settings.releaseInstance(); - super.onDestroy(); - } - - @Override - protected void onPause() { - super.onPause(); - Settings.setKeySound(mKeySoundPref.isChecked()); - Settings.setVibrate(mVibratePref.isChecked()); - Settings.setPrediction(mPredictionPref.isChecked()); - - Settings.writeBack(); - } - - public boolean onPreferenceChange(Preference preference, Object newValue) { - return true; - } - - private void updateWidgets() { - mKeySoundPref.setChecked(Settings.getKeySound()); - mVibratePref.setChecked(Settings.getVibrate()); - mPredictionPref.setChecked(Settings.getPrediction()); - } - - public void updatePreference(PreferenceGroup parentPref, String prefKey) { - Preference preference = parentPref.findPreference(prefKey); - if (preference == null) { - return; - } - Intent intent = preference.getIntent(); - if (intent != null) { - PackageManager pm = getPackageManager(); - List list = pm.queryIntentActivities(intent, 0); - int listSize = list.size(); - if (listSize == 0) - parentPref.removePreference(preference); - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SkbContainer.java b/PinyinIME/src/com/android/inputmethod/pinyin/SkbContainer.java deleted file mode 100644 index 2294860..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SkbContainer.java +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.content.Context; -import android.content.res.Resources; -import android.inputmethodservice.InputMethodService; -import android.os.Handler; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.util.AttributeSet; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.widget.PopupWindow; -import android.widget.RelativeLayout; -import android.widget.ViewFlipper; - -/** - * The top container to host soft keyboard view(s). - */ -public class SkbContainer extends RelativeLayout implements OnTouchListener { - /** - * For finger touch, user tends to press the bottom part of the target key, - * or he/she even presses the area out of it, so it is necessary to make a - * simple bias correction. If the input method runs on emulator, no bias - * correction will be used. - */ - private static final int Y_BIAS_CORRECTION = -10; - - /** - * Used to skip these move events whose position is too close to the - * previous touch events. - */ - private static final int MOVE_TOLERANCE = 6; - - /** - * If this member is true, PopupWindow is used to show on-key highlight - * effect. - */ - private static boolean POPUPWINDOW_FOR_PRESSED_UI = false; - - /** - * The current soft keyboard layout. - * - * @see com.android.inputmethod.pinyin.InputModeSwitcher for detailed layout - * definitions. - */ - private int mSkbLayout = 0; - - /** - * The input method service. - */ - private InputMethodService mService; - - /** - * Input mode switcher used to switch between different modes like Chinese, - * English, etc. - */ - private InputModeSwitcher mInputModeSwitcher; - - /** - * The gesture detector. - */ - private GestureDetector mGestureDetector; - - private Environment mEnvironment; - - private ViewFlipper mSkbFlipper; - - /** - * The popup balloon hint for key press/release. - */ - private BalloonHint mBalloonPopup; - - /** - * The on-key balloon hint for key press/release. - */ - private BalloonHint mBalloonOnKey = null; - - /** The major sub soft keyboard. */ - private SoftKeyboardView mMajorView; - - /** - * The last parameter when function {@link #toggleCandidateMode(boolean)} - * was called. - */ - private boolean mLastCandidatesShowing; - - /** Used to indicate whether a popup soft keyboard is shown. */ - private boolean mPopupSkbShow = false; - - /** - * Used to indicate whether a popup soft keyboard is just shown, and waits - * for the touch event to release. After the release, the popup window can - * response to touch events. - **/ - private boolean mPopupSkbNoResponse = false; - - /** Popup sub keyboard. */ - private PopupWindow mPopupSkb; - - /** The view of the popup sub soft keyboard. */ - private SoftKeyboardView mPopupSkbView; - - private int mPopupX; - - private int mPopupY; - - /** - * When user presses a key, a timer is started, when it times out, it is - * necessary to detect whether user still holds the key. - */ - private volatile boolean mWaitForTouchUp = false; - - /** - * When user drags on the soft keyboard and the distance is enough, this - * drag will be recognized as a gesture and a gesture-based action will be - * taken, in this situation, ignore the consequent events. - */ - private volatile boolean mDiscardEvent = false; - - /** - * For finger touch, user tends to press the bottom part of the target key, - * or he/she even presses the area out of it, so it is necessary to make a - * simple bias correction in Y. - */ - private int mYBiasCorrection = 0; - - /** - * The x coordination of the last touch event. - */ - private int mXLast; - - /** - * The y coordination of the last touch event. - */ - private int mYLast; - - /** - * The soft keyboard view. - */ - private SoftKeyboardView mSkv; - - /** - * The position of the soft keyboard view in the container. - */ - private int mSkvPosInContainer[] = new int[2]; - - /** - * The key pressed by user. - */ - private SoftKey mSoftKeyDown = null; - - /** - * Used to timeout a press if user holds the key for a long time. - */ - private LongPressTimer mLongPressTimer; - - /** - * For temporary use. - */ - private int mXyPosTmp[] = new int[2]; - - public SkbContainer(Context context, AttributeSet attrs) { - super(context, attrs); - - mEnvironment = Environment.getInstance(); - - mLongPressTimer = new LongPressTimer(this); - - // If it runs on an emulator, no bias correction - if ("1".equals(SystemProperties.get("ro.kernel.qemu"))) { - mYBiasCorrection = 0; - } else { - mYBiasCorrection = Y_BIAS_CORRECTION; - } - mBalloonPopup = new BalloonHint(context, this, MeasureSpec.AT_MOST); - if (POPUPWINDOW_FOR_PRESSED_UI) { - mBalloonOnKey = new BalloonHint(context, this, MeasureSpec.AT_MOST); - } - - mPopupSkb = new PopupWindow(mContext); - mPopupSkb.setBackgroundDrawable(null); - mPopupSkb.setClippingEnabled(false); - } - - public void setService(InputMethodService service) { - mService = service; - } - - public void setInputModeSwitcher(InputModeSwitcher inputModeSwitcher) { - mInputModeSwitcher = inputModeSwitcher; - } - - public void setGestureDetector(GestureDetector gestureDetector) { - mGestureDetector = gestureDetector; - } - - public boolean isCurrentSkbSticky() { - if (null == mMajorView) return true; - SoftKeyboard skb = mMajorView.getSoftKeyboard(); - if (null != skb) { - return skb.getStickyFlag(); - } - return true; - } - - public void toggleCandidateMode(boolean candidatesShowing) { - if (null == mMajorView || !mInputModeSwitcher.isChineseText() - || mLastCandidatesShowing == candidatesShowing) return; - mLastCandidatesShowing = candidatesShowing; - - SoftKeyboard skb = mMajorView.getSoftKeyboard(); - if (null == skb) return; - - int state = mInputModeSwitcher.getTooggleStateForCnCand(); - if (!candidatesShowing) { - skb.disableToggleState(state, false); - skb.enableToggleStates(mInputModeSwitcher.getToggleStates()); - } else { - skb.enableToggleState(state, false); - } - - mMajorView.invalidate(); - } - - public void updateInputMode() { - int skbLayout = mInputModeSwitcher.getSkbLayout(); - if (mSkbLayout != skbLayout) { - mSkbLayout = skbLayout; - updateSkbLayout(); - } - - mLastCandidatesShowing = false; - - if (null == mMajorView) return; - - SoftKeyboard skb = mMajorView.getSoftKeyboard(); - if (null == skb) return; - skb.enableToggleStates(mInputModeSwitcher.getToggleStates()); - invalidate(); - return; - } - - private void updateSkbLayout() { - int screenWidth = mEnvironment.getScreenWidth(); - int keyHeight = mEnvironment.getKeyHeight(); - int skbHeight = mEnvironment.getSkbHeight(); - - Resources r = mContext.getResources(); - if (null == mSkbFlipper) { - mSkbFlipper = (ViewFlipper) findViewById(R.id.alpha_floatable); - } - mMajorView = (SoftKeyboardView) mSkbFlipper.getChildAt(0); - - SoftKeyboard majorSkb = null; - SkbPool skbPool = SkbPool.getInstance(); - - switch (mSkbLayout) { - case R.xml.skb_qwerty: - majorSkb = skbPool.getSoftKeyboard(R.xml.skb_qwerty, - R.xml.skb_qwerty, screenWidth, skbHeight, mContext); - break; - - case R.xml.skb_sym1: - majorSkb = skbPool.getSoftKeyboard(R.xml.skb_sym1, R.xml.skb_sym1, - screenWidth, skbHeight, mContext); - break; - - case R.xml.skb_sym2: - majorSkb = skbPool.getSoftKeyboard(R.xml.skb_sym2, R.xml.skb_sym2, - screenWidth, skbHeight, mContext); - break; - - case R.xml.skb_smiley: - majorSkb = skbPool.getSoftKeyboard(R.xml.skb_smiley, - R.xml.skb_smiley, screenWidth, skbHeight, mContext); - break; - - case R.xml.skb_phone: - majorSkb = skbPool.getSoftKeyboard(R.xml.skb_phone, - R.xml.skb_phone, screenWidth, skbHeight, mContext); - break; - default: - } - - if (null == majorSkb || !mMajorView.setSoftKeyboard(majorSkb)) { - return; - } - mMajorView.setBalloonHint(mBalloonOnKey, mBalloonPopup, false); - mMajorView.invalidate(); - } - - private void responseKeyEvent(SoftKey sKey) { - if (null == sKey) return; - ((PinyinIME) mService).responseSoftKeyEvent(sKey); - return; - } - - private SoftKeyboardView inKeyboardView(int x, int y, - int positionInParent[]) { - if (mPopupSkbShow) { - if (mPopupX <= x && mPopupX + mPopupSkb.getWidth() > x - && mPopupY <= y && mPopupY + mPopupSkb.getHeight() > y) { - positionInParent[0] = mPopupX; - positionInParent[1] = mPopupY; - mPopupSkbView.setOffsetToSkbContainer(positionInParent); - return mPopupSkbView; - } - return null; - } - - return mMajorView; - } - - private void popupSymbols() { - int popupResId = mSoftKeyDown.getPopupResId(); - if (popupResId > 0) { - int skbContainerWidth = getWidth(); - int skbContainerHeight = getHeight(); - // The paddings of the background are not included. - int miniSkbWidth = (int) (skbContainerWidth * 0.8); - int miniSkbHeight = (int) (skbContainerHeight * 0.23); - - SkbPool skbPool = SkbPool.getInstance(); - SoftKeyboard skb = skbPool.getSoftKeyboard(popupResId, popupResId, - miniSkbWidth, miniSkbHeight, mContext); - if (null == skb) return; - - mPopupX = (skbContainerWidth - skb.getSkbTotalWidth()) / 2; - mPopupY = (skbContainerHeight - skb.getSkbTotalHeight()) / 2; - - if (null == mPopupSkbView) { - mPopupSkbView = new SoftKeyboardView(mContext, null); - mPopupSkbView.onMeasure(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - } - mPopupSkbView.setOnTouchListener(this); - mPopupSkbView.setSoftKeyboard(skb); - mPopupSkbView.setBalloonHint(mBalloonOnKey, mBalloonPopup, true); - - mPopupSkb.setContentView(mPopupSkbView); - mPopupSkb.setWidth(skb.getSkbCoreWidth() - + mPopupSkbView.getPaddingLeft() - + mPopupSkbView.getPaddingRight()); - mPopupSkb.setHeight(skb.getSkbCoreHeight() - + mPopupSkbView.getPaddingTop() - + mPopupSkbView.getPaddingBottom()); - - getLocationInWindow(mXyPosTmp); - mPopupSkb.showAtLocation(this, Gravity.NO_GRAVITY, mPopupX, mPopupY - + mXyPosTmp[1]); - mPopupSkbShow = true; - mPopupSkbNoResponse = true; - // Invalidate itself to dim the current soft keyboards. - dimSoftKeyboard(true); - resetKeyPress(0); - } - } - - private void dimSoftKeyboard(boolean dimSkb) { - mMajorView.dimSoftKeyboard(dimSkb); - } - - private void dismissPopupSkb() { - mPopupSkb.dismiss(); - mPopupSkbShow = false; - dimSoftKeyboard(false); - resetKeyPress(0); - } - - private void resetKeyPress(long delay) { - mLongPressTimer.removeTimer(); - - if (null != mSkv) { - mSkv.resetKeyPress(delay); - } - } - - public boolean handleBack(boolean realAction) { - if (mPopupSkbShow) { - if (!realAction) return true; - - dismissPopupSkb(); - mDiscardEvent = true; - return true; - } - return false; - } - - public void dismissPopups() { - handleBack(true); - resetKeyPress(0); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - Environment env = Environment.getInstance(); - int measuredWidth = env.getScreenWidth(); - int measuredHeight = getPaddingTop(); - measuredHeight += env.getSkbHeight(); - widthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth, - MeasureSpec.EXACTLY); - heightMeasureSpec = MeasureSpec.makeMeasureSpec(measuredHeight, - MeasureSpec.EXACTLY); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - - if (mSkbFlipper.isFlipping()) { - resetKeyPress(0); - return true; - } - - int x = (int) event.getX(); - int y = (int) event.getY(); - // Bias correction - y = y + mYBiasCorrection; - - // Ignore short-distance movement event to get better performance. - if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (Math.abs(x - mXLast) <= MOVE_TOLERANCE - && Math.abs(y - mYLast) <= MOVE_TOLERANCE) { - return true; - } - } - - mXLast = x; - mYLast = y; - - if (!mPopupSkbShow) { - if (mGestureDetector.onTouchEvent(event)) { - resetKeyPress(0); - mDiscardEvent = true; - return true; - } - } - - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - resetKeyPress(0); - - mWaitForTouchUp = true; - mDiscardEvent = false; - - mSkv = null; - mSoftKeyDown = null; - mSkv = inKeyboardView(x, y, mSkvPosInContainer); - if (null != mSkv) { - mSoftKeyDown = mSkv.onKeyPress(x - mSkvPosInContainer[0], y - - mSkvPosInContainer[1], mLongPressTimer, false); - } - break; - - case MotionEvent.ACTION_MOVE: - if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) { - break; - } - if (mDiscardEvent) { - resetKeyPress(0); - break; - } - - if (mPopupSkbShow && mPopupSkbNoResponse) { - break; - } - - SoftKeyboardView skv = inKeyboardView(x, y, mSkvPosInContainer); - if (null != skv) { - if (skv != mSkv) { - mSkv = skv; - mSoftKeyDown = mSkv.onKeyPress(x - mSkvPosInContainer[0], y - - mSkvPosInContainer[1], mLongPressTimer, true); - } else if (null != skv) { - if (null != mSkv) { - mSoftKeyDown = mSkv.onKeyMove( - x - mSkvPosInContainer[0], y - - mSkvPosInContainer[1]); - if (null == mSoftKeyDown) { - mDiscardEvent = true; - } - } - } - } - break; - - case MotionEvent.ACTION_UP: - if (mDiscardEvent) { - resetKeyPress(0); - break; - } - - mWaitForTouchUp = false; - - // The view which got the {@link MotionEvent#ACTION_DOWN} event is - // always used to handle this event. - if (null != mSkv) { - mSkv.onKeyRelease(x - mSkvPosInContainer[0], y - - mSkvPosInContainer[1]); - } - - if (!mPopupSkbShow || !mPopupSkbNoResponse) { - responseKeyEvent(mSoftKeyDown); - } - - if (mSkv == mPopupSkbView && !mPopupSkbNoResponse) { - dismissPopupSkb(); - } - mPopupSkbNoResponse = false; - break; - - case MotionEvent.ACTION_CANCEL: - break; - } - - if (null == mSkv) { - return false; - } - - return true; - } - - // Function for interface OnTouchListener, it is used to handle touch events - // which will be delivered to the popup soft keyboard view. - public boolean onTouch(View v, MotionEvent event) { - // Translate the event to fit to the container. - MotionEvent newEv = MotionEvent.obtain(event.getDownTime(), event - .getEventTime(), event.getAction(), event.getX() + mPopupX, - event.getY() + mPopupY, event.getPressure(), event.getSize(), - event.getMetaState(), event.getXPrecision(), event - .getYPrecision(), event.getDeviceId(), event - .getEdgeFlags()); - boolean ret = onTouchEvent(newEv); - return ret; - } - - class LongPressTimer extends Handler implements Runnable { - /** - * When user presses a key for a long time, the timeout interval to - * generate first {@link #LONG_PRESS_KEYNUM1} key events. - */ - public static final int LONG_PRESS_TIMEOUT1 = 500; - - /** - * When user presses a key for a long time, after the first - * {@link #LONG_PRESS_KEYNUM1} key events, this timeout interval will be - * used. - */ - private static final int LONG_PRESS_TIMEOUT2 = 100; - - /** - * When user presses a key for a long time, after the first - * {@link #LONG_PRESS_KEYNUM2} key events, this timeout interval will be - * used. - */ - private static final int LONG_PRESS_TIMEOUT3 = 100; - - /** - * When user presses a key for a long time, after the first - * {@link #LONG_PRESS_KEYNUM1} key events, timeout interval - * {@link #LONG_PRESS_TIMEOUT2} will be used instead. - */ - public static final int LONG_PRESS_KEYNUM1 = 1; - - /** - * When user presses a key for a long time, after the first - * {@link #LONG_PRESS_KEYNUM2} key events, timeout interval - * {@link #LONG_PRESS_TIMEOUT3} will be used instead. - */ - public static final int LONG_PRESS_KEYNUM2 = 3; - - SkbContainer mSkbContainer; - - private int mResponseTimes = 0; - - public LongPressTimer(SkbContainer skbContainer) { - mSkbContainer = skbContainer; - } - - public void startTimer() { - postAtTime(this, SystemClock.uptimeMillis() + LONG_PRESS_TIMEOUT1); - mResponseTimes = 0; - } - - public boolean removeTimer() { - removeCallbacks(this); - return true; - } - - public void run() { - if (mWaitForTouchUp) { - mResponseTimes++; - if (mSoftKeyDown.repeatable()) { - if (mSoftKeyDown.isUserDefKey()) { - if (1 == mResponseTimes) { - if (mInputModeSwitcher - .tryHandleLongPressSwitch(mSoftKeyDown.mKeyCode)) { - mDiscardEvent = true; - resetKeyPress(0); - } - } - } else { - responseKeyEvent(mSoftKeyDown); - long timeout; - if (mResponseTimes < LONG_PRESS_KEYNUM1) { - timeout = LONG_PRESS_TIMEOUT1; - } else if (mResponseTimes < LONG_PRESS_KEYNUM2) { - timeout = LONG_PRESS_TIMEOUT2; - } else { - timeout = LONG_PRESS_TIMEOUT3; - } - postAtTime(this, SystemClock.uptimeMillis() + timeout); - } - } else { - if (1 == mResponseTimes) { - popupSymbols(); - } - } - } - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SkbPool.java b/PinyinIME/src/com/android/inputmethod/pinyin/SkbPool.java deleted file mode 100644 index 4c46951..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SkbPool.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import java.util.Vector; - -import android.content.Context; - -/** - * Class used to cache previously loaded soft keyboard layouts. - */ -public class SkbPool { - private static SkbPool mInstance = null; - - private Vector mSkbTemplates = new Vector(); - private Vector mSoftKeyboards = new Vector(); - - private SkbPool() { - } - - public static SkbPool getInstance() { - if (null == mInstance) mInstance = new SkbPool(); - return mInstance; - } - - public void resetCachedSkb() { - mSoftKeyboards.clear(); - } - - public SkbTemplate getSkbTemplate(int skbTemplateId, Context context) { - for (int i = 0; i < mSkbTemplates.size(); i++) { - SkbTemplate t = mSkbTemplates.elementAt(i); - if (t.getSkbTemplateId() == skbTemplateId) { - return t; - } - } - - if (null != context) { - XmlKeyboardLoader xkbl = new XmlKeyboardLoader(context); - SkbTemplate t = xkbl.loadSkbTemplate(skbTemplateId); - if (null != t) { - mSkbTemplates.add(t); - return t; - } - } - return null; - } - - // Try to find the keyboard in the pool with the cache id. If there is no - // keyboard found, try to load it with the given xml id. - public SoftKeyboard getSoftKeyboard(int skbCacheId, int skbXmlId, - int skbWidth, int skbHeight, Context context) { - for (int i = 0; i < mSoftKeyboards.size(); i++) { - SoftKeyboard skb = mSoftKeyboards.elementAt(i); - if (skb.getCacheId() == skbCacheId && skb.getSkbXmlId() == skbXmlId) { - skb.setSkbCoreSize(skbWidth, skbHeight); - skb.setNewlyLoadedFlag(false); - return skb; - } - } - if (null != context) { - XmlKeyboardLoader xkbl = new XmlKeyboardLoader(context); - SoftKeyboard skb = xkbl.loadKeyboard(skbXmlId, skbWidth, skbHeight); - if (skb != null) { - if (skb.getCacheFlag()) { - skb.setCacheId(skbCacheId); - mSoftKeyboards.add(skb); - } - } - return skb; - } - return null; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SkbTemplate.java b/PinyinIME/src/com/android/inputmethod/pinyin/SkbTemplate.java deleted file mode 100644 index 9ab53ff..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SkbTemplate.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.graphics.drawable.Drawable; - -import java.util.Vector; - -/** - * Key icon definition. It is defined in soft keyboard template. A soft keyboard - * can refer to such an icon in its xml file directly to improve performance. - */ -class KeyIconRecord { - int keyCode; - Drawable icon; - Drawable iconPopup; -} - - -/** - * Default definition for a certain key. It is defined in soft keyboard - * template. A soft keyboard can refer to a default key in its xml file. Nothing - * of the key can be overwritten, including the size. - */ -class KeyRecord { - int keyId; - SoftKey softKey; -} - - -/** - * Soft keyboard template used by soft keyboards to share common resources. In - * this way, memory cost is reduced. - */ -public class SkbTemplate { - private int mSkbTemplateId; - private Drawable mSkbBg; - private Drawable mBalloonBg; - private Drawable mPopupBg; - private float mXMargin = 0; - private float mYMargin = 0; - /** Key type list. */ - private Vector mKeyTypeList = new Vector(); - - /** - * Default key icon list. It is only for keys which do not have popup icons. - */ - private Vector mKeyIconRecords = new Vector(); - - /** - * Default key list. - */ - private Vector mKeyRecords = new Vector(); - - public SkbTemplate(int skbTemplateId) { - mSkbTemplateId = skbTemplateId; - } - - public int getSkbTemplateId() { - return mSkbTemplateId; - } - - public void setBackgrounds(Drawable skbBg, Drawable balloonBg, - Drawable popupBg) { - mSkbBg = skbBg; - mBalloonBg = balloonBg; - mPopupBg = popupBg; - } - - public Drawable getSkbBackground() { - return mSkbBg; - } - - public Drawable getBalloonBackground() { - return mBalloonBg; - } - - public Drawable getPopupBackground() { - return mPopupBg; - } - - public void setMargins(float xMargin, float yMargin) { - mXMargin = xMargin; - mYMargin = yMargin; - } - - public float getXMargin() { - return mXMargin; - } - - public float getYMargin() { - return mYMargin; - } - - public SoftKeyType createKeyType(int id, Drawable bg, Drawable hlBg) { - return new SoftKeyType(id, bg, hlBg); - } - - public boolean addKeyType(SoftKeyType keyType) { - // The newly added item should have the right id. - if (mKeyTypeList.size() != keyType.mKeyTypeId) return false; - mKeyTypeList.add(keyType); - return true; - } - - public SoftKeyType getKeyType(int typeId) { - if (typeId < 0 || typeId > mKeyTypeList.size()) return null; - return mKeyTypeList.elementAt(typeId); - } - - public void addDefaultKeyIcons(int keyCode, Drawable icon, - Drawable iconPopup) { - if (null == icon || null == iconPopup) return; - - KeyIconRecord iconRecord = new KeyIconRecord(); - iconRecord.icon = icon; - iconRecord.iconPopup = iconPopup; - iconRecord.keyCode = keyCode; - - int size = mKeyIconRecords.size(); - int pos = 0; - while (pos < size) { - if (mKeyIconRecords.get(pos).keyCode >= keyCode) break; - pos++; - } - mKeyIconRecords.add(pos, iconRecord); - } - - public Drawable getDefaultKeyIcon(int keyCode) { - int size = mKeyIconRecords.size(); - int pos = 0; - while (pos < size) { - KeyIconRecord iconRecord = mKeyIconRecords.get(pos); - if (iconRecord.keyCode < keyCode) { - pos++; - continue; - } - if (iconRecord.keyCode == keyCode) { - return iconRecord.icon; - } - return null; - } - return null; - } - - public Drawable getDefaultKeyIconPopup(int keyCode) { - int size = mKeyIconRecords.size(); - int pos = 0; - while (pos < size) { - KeyIconRecord iconRecord = mKeyIconRecords.get(pos); - if (iconRecord.keyCode < keyCode) { - pos++; - continue; - } - if (iconRecord.keyCode == keyCode) { - return iconRecord.iconPopup; - } - return null; - } - return null; - } - - public void addDefaultKey(int keyId, SoftKey softKey) { - if (null == softKey) return; - - KeyRecord keyRecord = new KeyRecord(); - keyRecord.keyId = keyId; - keyRecord.softKey = softKey; - - int size = mKeyRecords.size(); - int pos = 0; - while (pos < size) { - if (mKeyRecords.get(pos).keyId >= keyId) break; - pos++; - } - mKeyRecords.add(pos, keyRecord); - } - - public SoftKey getDefaultKey(int keyId) { - int size = mKeyRecords.size(); - int pos = 0; - while (pos < size) { - KeyRecord keyRecord = mKeyRecords.get(pos); - if (keyRecord.keyId < keyId) { - pos++; - continue; - } - if (keyRecord.keyId == keyId) { - return keyRecord.softKey; - } - return null; - } - return null; - } -} - - -class SoftKeyType { - public static final int KEYTYPE_ID_NORMAL_KEY = 0; - - public int mKeyTypeId; - public Drawable mKeyBg; - public Drawable mKeyHlBg; - public int mColor; - public int mColorHl; - public int mColorBalloon; - - SoftKeyType(int id, Drawable bg, Drawable hlBg) { - mKeyTypeId = id; - mKeyBg = bg; - mKeyHlBg = hlBg; - } - - public void setColors(int color, int colorHl, int colorBalloon) { - mColor = color; - mColorHl = colorHl; - mColorBalloon = colorBalloon; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKey.java b/PinyinIME/src/com/android/inputmethod/pinyin/SoftKey.java deleted file mode 100644 index 67eaf29..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKey.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.graphics.drawable.Drawable; - -/** - * Class for soft keys which defined in the keyboard xml file. A soft key can be - * a basic key or a toggling key. - * - * @see com.android.inputmethod.pinyin.SoftKeyToggle - */ -public class SoftKey { - protected static final int KEYMASK_REPEAT = 0x10000000; - protected static final int KEYMASK_BALLOON = 0x20000000; - - /** - * For a finger touch device, after user presses a key, there will be some - * consequent moving events because of the changing in touching pressure. If - * the moving distance in x is within this threshold, the moving events will - * be ignored. - */ - public static final int MAX_MOVE_TOLERANCE_X = 0; - - /** - * For a finger touch device, after user presses a key, there will be some - * consequent moving events because of the changing in touching pressure. If - * the moving distance in y is within this threshold, the moving events will - * be ignored. - */ - public static final int MAX_MOVE_TOLERANCE_Y = 0; - - /** - * Used to indicate the type and attributes of this key. the lowest 8 bits - * should be reserved for SoftkeyToggle. - */ - protected int mKeyMask; - - protected SoftKeyType mKeyType; - - protected Drawable mKeyIcon; - - protected Drawable mKeyIconPopup; - - protected String mKeyLabel; - - protected int mKeyCode; - - /** - * If this value is not 0, this key can be used to popup a sub soft keyboard - * when user presses it for some time. - */ - public int mPopupSkbId; - - public float mLeftF; - public float mRightF; - public float mTopF; - public float mBottomF; - public int mLeft; - public int mRight; - public int mTop; - public int mBottom; - - public void setKeyType(SoftKeyType keyType, Drawable keyIcon, - Drawable keyIconPopup) { - mKeyType = keyType; - mKeyIcon = keyIcon; - mKeyIconPopup = keyIconPopup; - } - - // The caller guarantees that all parameters are in [0, 1] - public void setKeyDimensions(float left, float top, float right, - float bottom) { - mLeftF = left; - mTopF = top; - mRightF = right; - mBottomF = bottom; - } - - public void setKeyAttribute(int keyCode, String label, boolean repeat, - boolean balloon) { - mKeyCode = keyCode; - mKeyLabel = label; - - if (repeat) { - mKeyMask |= KEYMASK_REPEAT; - } else { - mKeyMask &= (~KEYMASK_REPEAT); - } - - if (balloon) { - mKeyMask |= KEYMASK_BALLOON; - } else { - mKeyMask &= (~KEYMASK_BALLOON); - } - } - - public void setPopupSkbId(int popupSkbId) { - mPopupSkbId = popupSkbId; - } - - // Call after setKeyDimensions(). The caller guarantees that the - // keyboard with and height are valid. - public void setSkbCoreSize(int skbWidth, int skbHeight) { - mLeft = (int) (mLeftF * skbWidth); - mRight = (int) (mRightF * skbWidth); - mTop = (int) (mTopF * skbHeight); - mBottom = (int) (mBottomF * skbHeight); - } - - public Drawable getKeyIcon() { - return mKeyIcon; - } - - public Drawable getKeyIconPopup() { - if (null != mKeyIconPopup) { - return mKeyIconPopup; - } - return mKeyIcon; - } - - public int getKeyCode() { - return mKeyCode; - } - - public String getKeyLabel() { - return mKeyLabel; - } - - public void changeCase(boolean upperCase) { - if (null != mKeyLabel) { - if (upperCase) - mKeyLabel = mKeyLabel.toUpperCase(); - else - mKeyLabel = mKeyLabel.toLowerCase(); - } - } - - public Drawable getKeyBg() { - return mKeyType.mKeyBg; - } - - public Drawable getKeyHlBg() { - return mKeyType.mKeyHlBg; - } - - public int getColor() { - return mKeyType.mColor; - } - - public int getColorHl() { - return mKeyType.mColorHl; - } - - public int getColorBalloon() { - return mKeyType.mColorBalloon; - } - - public boolean isKeyCodeKey() { - if (mKeyCode > 0) return true; - return false; - } - - public boolean isUserDefKey() { - if (mKeyCode < 0) return true; - return false; - } - - public boolean isUniStrKey() { - if (null != mKeyLabel && mKeyCode == 0) return true; - return false; - } - - public boolean needBalloon() { - return (mKeyMask & KEYMASK_BALLOON) != 0; - } - - public boolean repeatable() { - return (mKeyMask & KEYMASK_REPEAT) != 0; - } - - public int getPopupResId() { - return mPopupSkbId; - } - - public int width() { - return mRight - mLeft; - } - - public int height() { - return mBottom - mTop; - } - - public boolean moveWithinKey(int x, int y) { - if (mLeft - MAX_MOVE_TOLERANCE_X <= x - && mTop - MAX_MOVE_TOLERANCE_Y <= y - && mRight + MAX_MOVE_TOLERANCE_X > x - && mBottom + MAX_MOVE_TOLERANCE_Y > y) { - return true; - } - return false; - } - - @Override - public String toString() { - String str = "\n"; - str += " keyCode: " + String.valueOf(mKeyCode) + "\n"; - str += " keyMask: " + String.valueOf(mKeyMask) + "\n"; - str += " keyLabel: " + (mKeyLabel == null ? "null" : mKeyLabel) + "\n"; - str += " popupResId: " + String.valueOf(mPopupSkbId) + "\n"; - str += " Position: " + String.valueOf(mLeftF) + ", " - + String.valueOf(mTopF) + ", " + String.valueOf(mRightF) + ", " - + String.valueOf(mBottomF) + "\n"; - return str; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyToggle.java b/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyToggle.java deleted file mode 100644 index 89ff2fe..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyToggle.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.graphics.drawable.Drawable; - -/** - * Class for soft keys which defined in the keyboard xml file. A soft key can be - * a basic key or a toggling key. - * - * @see com.android.inputmethod.pinyin.SoftKey - */ -public class SoftKeyToggle extends SoftKey { - /** - * The current state number is stored in the lowest 8 bits of mKeyMask, this - * mask is used to get the state number. If the current state is 0, the - * normal state is enabled; if the current state is more than 0, a toggle - * state in the toggle state chain will be enabled. - */ - private static final int KEYMASK_TOGGLE_STATE = 0x000000ff; - - private ToggleState mToggleState; - - public int getToggleStateId() { - return (mKeyMask & KEYMASK_TOGGLE_STATE); - } - - // The state id should be valid, and less than 255. - // If resetIfNotFound is true and there is no such toggle state with the - // given id, the key state will be reset. - // If the key state is newly changed (enabled to the given state, or - // reseted) and needs re-draw, return true. - public boolean enableToggleState(int stateId, boolean resetIfNotFound) { - int oldStateId = (mKeyMask & KEYMASK_TOGGLE_STATE); - if (oldStateId == stateId) return false; - - mKeyMask &= (~KEYMASK_TOGGLE_STATE); - if (stateId > 0) { - mKeyMask |= (KEYMASK_TOGGLE_STATE & stateId); - if (getToggleState() == null) { - mKeyMask &= (~KEYMASK_TOGGLE_STATE); - if (!resetIfNotFound && oldStateId > 0) { - mKeyMask |= (KEYMASK_TOGGLE_STATE & oldStateId); - } - return resetIfNotFound; - } else { - return true; - } - } else { - return true; - } - } - - // The state id should be valid, and less than 255. - // If resetIfNotFound is true and there is no such toggle state with the - // given id, the key state will be reset. - // If the key state is newly changed and needs re-draw, return true. - public boolean disableToggleState(int stateId, boolean resetIfNotFound) { - int oldStateId = (mKeyMask & KEYMASK_TOGGLE_STATE); - if (oldStateId == stateId) { - mKeyMask &= (~KEYMASK_TOGGLE_STATE); - return stateId != 0; - } - - if (resetIfNotFound) { - mKeyMask &= (~KEYMASK_TOGGLE_STATE); - return oldStateId != 0; - } - return false; - } - - // Clear any toggle state. If the key needs re-draw, return true. - public boolean disableAllToggleStates() { - int oldStateId = (mKeyMask & KEYMASK_TOGGLE_STATE); - mKeyMask &= (~KEYMASK_TOGGLE_STATE); - return oldStateId != 0; - } - - @Override - public Drawable getKeyIcon() { - ToggleState state = getToggleState(); - if (null != state) return state.mKeyIcon; - return super.getKeyIcon(); - } - - @Override - public Drawable getKeyIconPopup() { - ToggleState state = getToggleState(); - if (null != state) { - if (null != state.mKeyIconPopup) { - return state.mKeyIconPopup; - } else { - return state.mKeyIcon; - } - } - return super.getKeyIconPopup(); - } - - @Override - public int getKeyCode() { - ToggleState state = getToggleState(); - if (null != state) return state.mKeyCode; - return mKeyCode; - } - - @Override - public String getKeyLabel() { - ToggleState state = getToggleState(); - if (null != state) return state.mKeyLabel; - return mKeyLabel; - } - - @Override - public Drawable getKeyBg() { - ToggleState state = getToggleState(); - if (null != state && null != state.mKeyType) { - return state.mKeyType.mKeyBg; - } - return mKeyType.mKeyBg; - } - - @Override - public Drawable getKeyHlBg() { - ToggleState state = getToggleState(); - if (null != state && null != state.mKeyType) { - return state.mKeyType.mKeyHlBg; - } - return mKeyType.mKeyHlBg; - } - - @Override - public int getColor() { - ToggleState state = getToggleState(); - if (null != state && null != state.mKeyType) { - return state.mKeyType.mColor; - } - return mKeyType.mColor; - } - - @Override - public int getColorHl() { - ToggleState state = getToggleState(); - if (null != state && null != state.mKeyType) { - return state.mKeyType.mColorHl; - } - return mKeyType.mColorHl; - } - - @Override - public int getColorBalloon() { - ToggleState state = getToggleState(); - if (null != state && null != state.mKeyType) { - return state.mKeyType.mColorBalloon; - } - return mKeyType.mColorBalloon; - } - - @Override - public boolean isKeyCodeKey() { - ToggleState state = getToggleState(); - if (null != state) { - if (state.mKeyCode > 0) return true; - return false; - } - return super.isKeyCodeKey(); - } - - @Override - public boolean isUserDefKey() { - ToggleState state = getToggleState(); - if (null != state) { - if (state.mKeyCode < 0) return true; - return false; - } - return super.isUserDefKey(); - } - - @Override - public boolean isUniStrKey() { - ToggleState state = getToggleState(); - if (null != state) { - if (null != state.mKeyLabel && state.mKeyCode == 0) { - return true; - } - return false; - } - return super.isUniStrKey(); - } - - @Override - public boolean needBalloon() { - ToggleState state = getToggleState(); - if (null != state) { - return (state.mIdAndFlags & KEYMASK_BALLOON) != 0; - } - return super.needBalloon(); - } - - @Override - public boolean repeatable() { - ToggleState state = getToggleState(); - if (null != state) { - return (state.mIdAndFlags & KEYMASK_REPEAT) != 0; - } - return super.repeatable(); - } - - @Override - public void changeCase(boolean lowerCase) { - ToggleState state = getToggleState(); - if (null != state && null != state.mKeyLabel) { - if (lowerCase) - state.mKeyLabel = state.mKeyLabel.toLowerCase(); - else - state.mKeyLabel = state.mKeyLabel.toUpperCase(); - } - } - - public ToggleState createToggleState() { - return new ToggleState(); - } - - public boolean setToggleStates(ToggleState rootState) { - if (null == rootState) return false; - mToggleState = rootState; - return true; - } - - private ToggleState getToggleState() { - int stateId = (mKeyMask & KEYMASK_TOGGLE_STATE); - if (0 == stateId) return null; - - ToggleState state = mToggleState; - while ((null != state) - && (state.mIdAndFlags & KEYMASK_TOGGLE_STATE) != stateId) { - state = state.mNextState; - } - return state; - } - - public class ToggleState { - // The id should be bigger than 0; - private int mIdAndFlags; - public SoftKeyType mKeyType; - public int mKeyCode; - public Drawable mKeyIcon; - public Drawable mKeyIconPopup; - public String mKeyLabel; - public ToggleState mNextState; - - public void setStateId(int stateId) { - mIdAndFlags |= (stateId & KEYMASK_TOGGLE_STATE); - } - - public void setStateFlags(boolean repeat, boolean balloon) { - if (repeat) { - mIdAndFlags |= KEYMASK_REPEAT; - } else { - mIdAndFlags &= (~KEYMASK_REPEAT); - } - - if (balloon) { - mIdAndFlags |= KEYMASK_BALLOON; - } else { - mIdAndFlags &= (~KEYMASK_BALLOON); - } - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboard.java b/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboard.java deleted file mode 100644 index b8cc504..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboard.java +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import com.android.inputmethod.pinyin.InputModeSwitcher.ToggleStates; - -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.view.KeyEvent; - -import java.util.ArrayList; -import java.util.List; - -/** - * Class used to represent a soft keyboard definition, including the height, the - * background image, the image for high light, the keys, etc. - */ -public class SoftKeyboard { - /** The XML resource id for this soft keyboard. */ - private int mSkbXmlId; - - /** Do we need to cache this soft keyboard? */ - private boolean mCacheFlag; - - /** - * After user switches to this soft keyboard, if this flag is true, this - * soft keyboard will be kept unless explicit switching operation is - * performed, otherwise IME will switch back to the previous keyboard layout - * whenever user clicks on any none-function key. - **/ - private boolean mStickyFlag; - - /** - * The cache id for this soft keyboard. It is used to identify it in the - * soft keyboard pool. - */ - private int mCacheId; - - /** - * Used to indicate whether this soft keyboard is newly loaded from an XML - * file or is just gotten from the soft keyboard pool. - */ - private boolean mNewlyLoadedFlag = true; - - /** The width of the soft keyboard. */ - private int mSkbCoreWidth; - - /** The height of the soft keyboard. */ - private int mSkbCoreHeight; - - /** The soft keyboard template for this soft keyboard. */ - private SkbTemplate mSkbTemplate; - - /** Used to indicate whether this soft keyboard is a QWERTY keyboard. */ - private boolean mIsQwerty; - - /** - * When {@link #mIsQwerty} is true, this member is Used to indicate that the - * soft keyboard should be displayed in uppercase. - */ - private boolean mIsQwertyUpperCase; - - /** - * The id of the rows which are enabled. Rows with id - * {@link KeyRow#ALWAYS_SHOW_ROW_ID} are always enabled. - */ - private int mEnabledRowId; - - /** - * Rows in this soft keyboard. Each row has a id. Only matched rows will be - * enabled. - */ - private List mKeyRows; - - /** - * Background of the soft keyboard. If it is null, the one in the soft - * keyboard template will be used. - **/ - public Drawable mSkbBg; - - /** - * Background for key balloon. If it is null, the one in the soft keyboard - * template will be used. - **/ - private Drawable mBalloonBg; - - /** - * Background for popup mini soft keyboard. If it is null, the one in the - * soft keyboard template will be used. - **/ - private Drawable mPopupBg; - - /** The left and right margin of a key. */ - private float mKeyXMargin = 0; - - /** The top and bottom margin of a key. */ - private float mKeyYMargin = 0; - - private Rect mTmpRect = new Rect(); - - public SoftKeyboard(int skbXmlId, SkbTemplate skbTemplate, int skbWidth, - int skbHeight) { - mSkbXmlId = skbXmlId; - mSkbTemplate = skbTemplate; - mSkbCoreWidth = skbWidth; - mSkbCoreHeight = skbHeight; - } - - public void setFlags(boolean cacheFlag, boolean stickyFlag, - boolean isQwerty, boolean isQwertyUpperCase) { - mCacheFlag = cacheFlag; - mStickyFlag = stickyFlag; - mIsQwerty = isQwerty; - mIsQwertyUpperCase = isQwertyUpperCase; - } - - public boolean getCacheFlag() { - return mCacheFlag; - } - - public void setCacheId(int cacheId) { - mCacheId = cacheId; - } - - public boolean getStickyFlag() { - return mStickyFlag; - } - - public void setSkbBackground(Drawable skbBg) { - mSkbBg = skbBg; - } - - public void setPopupBackground(Drawable popupBg) { - mPopupBg = popupBg; - } - - public void setKeyBalloonBackground(Drawable balloonBg) { - mBalloonBg = balloonBg; - } - - public void setKeyMargins(float xMargin, float yMargin) { - mKeyXMargin = xMargin; - mKeyYMargin = yMargin; - } - - public int getCacheId() { - return mCacheId; - } - - public void reset() { - if (null != mKeyRows) mKeyRows.clear(); - } - - public void setNewlyLoadedFlag(boolean newlyLoadedFlag) { - mNewlyLoadedFlag = newlyLoadedFlag; - } - - public boolean getNewlyLoadedFlag() { - return mNewlyLoadedFlag; - } - - public void beginNewRow(int rowId, float yStartingPos) { - if (null == mKeyRows) mKeyRows = new ArrayList(); - KeyRow keyRow = new KeyRow(); - keyRow.mRowId = rowId; - keyRow.mTopF = yStartingPos; - keyRow.mBottomF = yStartingPos; - keyRow.mSoftKeys = new ArrayList(); - mKeyRows.add(keyRow); - } - - public boolean addSoftKey(SoftKey softKey) { - if (mKeyRows.size() == 0) return false; - KeyRow keyRow = mKeyRows.get(mKeyRows.size() - 1); - if (null == keyRow) return false; - List softKeys = keyRow.mSoftKeys; - - softKey.setSkbCoreSize(mSkbCoreWidth, mSkbCoreHeight); - softKeys.add(softKey); - if (softKey.mTopF < keyRow.mTopF) { - keyRow.mTopF = softKey.mTopF; - } - if (softKey.mBottomF > keyRow.mBottomF) { - keyRow.mBottomF = softKey.mBottomF; - } - return true; - } - - public int getSkbXmlId() { - return mSkbXmlId; - } - - // Set the size of the soft keyboard core. In other words, the background's - // padding are not counted. - public void setSkbCoreSize(int skbCoreWidth, int skbCoreHeight) { - if (null == mKeyRows - || (skbCoreWidth == mSkbCoreWidth && skbCoreHeight == mSkbCoreHeight)) { - return; - } - for (int row = 0; row < mKeyRows.size(); row++) { - KeyRow keyRow = mKeyRows.get(row); - keyRow.mBottom = (int) (skbCoreHeight * keyRow.mBottomF); - keyRow.mTop = (int) (skbCoreHeight * keyRow.mTopF); - - List softKeys = keyRow.mSoftKeys; - for (int i = 0; i < softKeys.size(); i++) { - SoftKey softKey = softKeys.get(i); - softKey.setSkbCoreSize(skbCoreWidth, skbCoreHeight); - } - } - mSkbCoreWidth = skbCoreWidth; - mSkbCoreHeight = skbCoreHeight; - } - - public int getSkbCoreWidth() { - return mSkbCoreWidth; - } - - public int getSkbCoreHeight() { - return mSkbCoreHeight; - } - - public int getSkbTotalWidth() { - Rect padding = getPadding(); - return mSkbCoreWidth + padding.left + padding.right; - } - - public int getSkbTotalHeight() { - Rect padding = getPadding(); - return mSkbCoreHeight + padding.top + padding.bottom; - } - - public int getKeyXMargin() { - Environment env = Environment.getInstance(); - return (int) (mKeyXMargin * mSkbCoreWidth * env.getKeyXMarginFactor()); - } - - public int getKeyYMargin() { - Environment env = Environment.getInstance(); - return (int) (mKeyYMargin * mSkbCoreHeight * env.getKeyYMarginFactor()); - } - - public Drawable getSkbBackground() { - if (null != mSkbBg) return mSkbBg; - return mSkbTemplate.getSkbBackground(); - } - - public Drawable getBalloonBackground() { - if (null != mBalloonBg) return mBalloonBg; - return mSkbTemplate.getBalloonBackground(); - } - - public Drawable getPopupBackground() { - if (null != mPopupBg) return mPopupBg; - return mSkbTemplate.getPopupBackground(); - } - - public int getRowNum() { - if (null != mKeyRows) { - return mKeyRows.size(); - } - return 0; - } - - public KeyRow getKeyRowForDisplay(int row) { - if (null != mKeyRows && mKeyRows.size() > row) { - KeyRow keyRow = mKeyRows.get(row); - if (KeyRow.ALWAYS_SHOW_ROW_ID == keyRow.mRowId - || keyRow.mRowId == mEnabledRowId) { - return keyRow; - } - } - return null; - } - - public SoftKey getKey(int row, int location) { - if (null != mKeyRows && mKeyRows.size() > row) { - List softKeys = mKeyRows.get(row).mSoftKeys; - if (softKeys.size() > location) { - return softKeys.get(location); - } - } - return null; - } - - public SoftKey mapToKey(int x, int y) { - if (null == mKeyRows) { - return null; - } - // If the position is inside the rectangle of a certain key, return that - // key. - int rowNum = mKeyRows.size(); - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mKeyRows.get(row); - if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId - && keyRow.mRowId != mEnabledRowId) continue; - if (keyRow.mTop > y && keyRow.mBottom <= y) continue; - - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int i = 0; i < keyNum; i++) { - SoftKey sKey = softKeys.get(i); - if (sKey.mLeft <= x && sKey.mTop <= y && sKey.mRight > x - && sKey.mBottom > y) { - return sKey; - } - } - } - - // If the position is outside the rectangles of all keys, find the - // nearest one. - SoftKey nearestKey = null; - float nearestDis = Float.MAX_VALUE; - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mKeyRows.get(row); - if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId - && keyRow.mRowId != mEnabledRowId) continue; - if (keyRow.mTop > y && keyRow.mBottom <= y) continue; - - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int i = 0; i < keyNum; i++) { - SoftKey sKey = softKeys.get(i); - int disx = (sKey.mLeft + sKey.mRight) / 2 - x; - int disy = (sKey.mTop + sKey.mBottom) / 2 - y; - float dis = disx * disx + disy * disy; - if (dis < nearestDis) { - nearestDis = dis; - nearestKey = sKey; - } - } - } - return nearestKey; - } - - public void switchQwertyMode(int toggle_state_id, boolean upperCase) { - if (!mIsQwerty) return; - - int rowNum = mKeyRows.size(); - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mKeyRows.get(row); - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int i = 0; i < keyNum; i++) { - SoftKey sKey = softKeys.get(i); - if (sKey instanceof SoftKeyToggle) { - ((SoftKeyToggle) sKey).enableToggleState(toggle_state_id, - true); - } - if (sKey.mKeyCode >= KeyEvent.KEYCODE_A - && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) { - sKey.changeCase(upperCase); - } - } - } - } - - public void enableToggleState(int toggleStateId, boolean resetIfNotFound) { - int rowNum = mKeyRows.size(); - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mKeyRows.get(row); - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int i = 0; i < keyNum; i++) { - SoftKey sKey = softKeys.get(i); - if (sKey instanceof SoftKeyToggle) { - ((SoftKeyToggle) sKey).enableToggleState(toggleStateId, - resetIfNotFound); - } - } - } - } - - public void disableToggleState(int toggleStateId, boolean resetIfNotFound) { - int rowNum = mKeyRows.size(); - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mKeyRows.get(row); - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int i = 0; i < keyNum; i++) { - SoftKey sKey = softKeys.get(i); - if (sKey instanceof SoftKeyToggle) { - ((SoftKeyToggle) sKey).disableToggleState(toggleStateId, - resetIfNotFound); - } - } - } - } - - public void enableToggleStates(ToggleStates toggleStates) { - if (null == toggleStates) return; - - enableRow(toggleStates.mRowIdToEnable); - - boolean isQwerty = toggleStates.mQwerty; - boolean isQwertyUpperCase = toggleStates.mQwertyUpperCase; - boolean needUpdateQwerty = (isQwerty && mIsQwerty && (mIsQwertyUpperCase != isQwertyUpperCase)); - int states[] = toggleStates.mKeyStates; - int statesNum = toggleStates.mKeyStatesNum; - - int rowNum = mKeyRows.size(); - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mKeyRows.get(row); - if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId - && keyRow.mRowId != mEnabledRowId) { - continue; - } - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int keyPos = 0; keyPos < keyNum; keyPos++) { - SoftKey sKey = softKeys.get(keyPos); - if (sKey instanceof SoftKeyToggle) { - for (int statePos = 0; statePos < statesNum; statePos++) { - ((SoftKeyToggle) sKey).enableToggleState( - states[statePos], statePos == 0); - } - if (0 == statesNum) { - ((SoftKeyToggle) sKey).disableAllToggleStates(); - } - } - if (needUpdateQwerty) { - if (sKey.mKeyCode >= KeyEvent.KEYCODE_A - && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) { - sKey.changeCase(isQwertyUpperCase); - } - } - } - } - mIsQwertyUpperCase = isQwertyUpperCase; - } - - private Rect getPadding() { - mTmpRect.set(0, 0, 0, 0); - Drawable skbBg = getSkbBackground(); - if (null == skbBg) return mTmpRect; - skbBg.getPadding(mTmpRect); - return mTmpRect; - } - - /** - * Enable a row with the give toggle Id. Rows with other toggle ids (except - * the id {@link KeyRow#ALWAYS_SHOW_ROW_ID}) will be disabled. - * - * @param rowId The row id to enable. - * @return True if the soft keyboard requires redrawing. - */ - private boolean enableRow(int rowId) { - if (KeyRow.ALWAYS_SHOW_ROW_ID == rowId) return false; - - boolean enabled = false; - int rowNum = mKeyRows.size(); - for (int row = rowNum - 1; row >= 0; row--) { - if (mKeyRows.get(row).mRowId == rowId) { - enabled = true; - break; - } - } - if (enabled) { - mEnabledRowId = rowId; - } - return enabled; - } - - @Override - public String toString() { - String str = "------------------SkbInfo----------------------\n"; - String endStr = "-----------------------------------------------\n"; - str += "Width: " + String.valueOf(mSkbCoreWidth) + "\n"; - str += "Height: " + String.valueOf(mSkbCoreHeight) + "\n"; - str += "KeyRowNum: " + mKeyRows == null ? "0" : String.valueOf(mKeyRows - .size()) - + "\n"; - if (null == mKeyRows) return str + endStr; - int rowNum = mKeyRows.size(); - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mKeyRows.get(row); - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int i = 0; i < softKeys.size(); i++) { - str += "-key " + String.valueOf(i) + ":" - + softKeys.get(i).toString(); - } - } - return str + endStr; - } - - public String toShortString() { - return super.toString(); - } - - class KeyRow { - static final int ALWAYS_SHOW_ROW_ID = -1; - static final int DEFAULT_ROW_ID = 0; - - List mSoftKeys; - /** - * If the row id is {@link #ALWAYS_SHOW_ROW_ID}, this row will always be - * enabled. - */ - int mRowId; - float mTopF; - float mBottomF; - int mTop; - int mBottom; - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboardView.java b/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboardView.java deleted file mode 100644 index 5543f33..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboardView.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow; - -import java.util.List; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Paint.FontMetricsInt; -import android.graphics.drawable.Drawable; -import android.os.Vibrator; -import android.util.AttributeSet; -import android.view.View; - -/** - * Class used to show a soft keyboard. - * - * A soft keyboard view should not handle touch event itself, because we do bias - * correction, need a global strategy to map an event into a proper view to - * achieve better user experience. - */ -public class SoftKeyboardView extends View { - /** - * The definition of the soft keyboard for the current this soft keyboard - * view. - */ - private SoftKeyboard mSoftKeyboard; - - /** - * The popup balloon hint for key press/release. - */ - private BalloonHint mBalloonPopup; - - /** - * The on-key balloon hint for key press/release. If it is null, on-key - * highlight will be drawn on th soft keyboard view directly. - */ - private BalloonHint mBalloonOnKey; - - /** Used to play key sounds. */ - private SoundManager mSoundManager; - - /** The last key pressed. */ - private SoftKey mSoftKeyDown; - - /** Used to indicate whether the user is holding on a key. */ - private boolean mKeyPressed = false; - - /** - * The location offset of the view to the keyboard container. - */ - private int mOffsetToSkbContainer[] = new int[2]; - - /** - * The location of the desired hint view to the keyboard container. - */ - private int mHintLocationToSkbContainer[] = new int[2]; - - /** - * Text size for normal key. - */ - private int mNormalKeyTextSize; - - /** - * Text size for function key. - */ - private int mFunctionKeyTextSize; - - /** - * Long press timer used to response long-press. - */ - private SkbContainer.LongPressTimer mLongPressTimer; - - /** - * Repeated events for long press - */ - private boolean mRepeatForLongPress = false; - - /** - * If this parameter is true, the balloon will never be dismissed even if - * user moves a lot from the pressed point. - */ - private boolean mMovingNeverHidePopupBalloon = false; - - /** Vibration for key press. */ - private Vibrator mVibrator; - - /** Vibration pattern for key press. */ - protected long[] mVibratePattern = new long[] {1, 20}; - - /** - * The dirty rectangle used to mark the area to re-draw during key press and - * release. Currently, whenever we can invalidate(Rect), view will call - * onDraw() and we MUST draw the whole view. This dirty information is for - * future use. - */ - private Rect mDirtyRect = new Rect(); - - private Paint mPaint; - private FontMetricsInt mFmi; - private boolean mDimSkb; - - public SoftKeyboardView(Context context, AttributeSet attrs) { - super(context, attrs); - - mSoundManager = SoundManager.getInstance(mContext); - - mPaint = new Paint(); - mPaint.setAntiAlias(true); - mFmi = mPaint.getFontMetricsInt(); - } - - public boolean setSoftKeyboard(SoftKeyboard softSkb) { - if (null == softSkb) { - return false; - } - mSoftKeyboard = softSkb; - Drawable bg = softSkb.getSkbBackground(); - if (null != bg) setBackgroundDrawable(bg); - return true; - } - - public SoftKeyboard getSoftKeyboard() { - return mSoftKeyboard; - } - - public void resizeKeyboard(int skbWidth, int skbHeight) { - mSoftKeyboard.setSkbCoreSize(skbWidth, skbHeight); - } - - public void setBalloonHint(BalloonHint balloonOnKey, - BalloonHint balloonPopup, boolean movingNeverHidePopup) { - mBalloonOnKey = balloonOnKey; - mBalloonPopup = balloonPopup; - mMovingNeverHidePopupBalloon = movingNeverHidePopup; - } - - public void setOffsetToSkbContainer(int offsetToSkbContainer[]) { - mOffsetToSkbContainer[0] = offsetToSkbContainer[0]; - mOffsetToSkbContainer[1] = offsetToSkbContainer[1]; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int measuredWidth = 0; - int measuredHeight = 0; - if (null != mSoftKeyboard) { - measuredWidth = mSoftKeyboard.getSkbCoreWidth(); - measuredHeight = mSoftKeyboard.getSkbCoreHeight(); - measuredWidth += mPaddingLeft + mPaddingRight; - measuredHeight += mPaddingTop + mPaddingBottom; - } - setMeasuredDimension(measuredWidth, measuredHeight); - } - - private void showBalloon(BalloonHint balloon, int balloonLocationToSkb[], - boolean movePress) { - long delay = BalloonHint.TIME_DELAY_SHOW; - if (movePress) delay = 0; - if (balloon.needForceDismiss()) { - balloon.delayedDismiss(0); - } - if (!balloon.isShowing()) { - balloon.delayedShow(delay, balloonLocationToSkb); - } else { - balloon.delayedUpdate(delay, balloonLocationToSkb, balloon - .getWidth(), balloon.getHeight()); - } - long b = System.currentTimeMillis(); - } - - public void resetKeyPress(long balloonDelay) { - if (!mKeyPressed) return; - mKeyPressed = false; - if (null != mBalloonOnKey) { - mBalloonOnKey.delayedDismiss(balloonDelay); - } else { - if (null != mSoftKeyDown) { - if (mDirtyRect.isEmpty()) { - mDirtyRect.set(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, - mSoftKeyDown.mRight, mSoftKeyDown.mBottom); - } - invalidate(mDirtyRect); - } else { - invalidate(); - } - } - mBalloonPopup.delayedDismiss(balloonDelay); - } - - // If movePress is true, means that this function is called because user - // moves his finger to this button. If movePress is false, means that this - // function is called when user just presses this key. - public SoftKey onKeyPress(int x, int y, - SkbContainer.LongPressTimer longPressTimer, boolean movePress) { - mKeyPressed = false; - boolean moveWithinPreviousKey = false; - if (movePress) { - SoftKey newKey = mSoftKeyboard.mapToKey(x, y); - if (newKey == mSoftKeyDown) moveWithinPreviousKey = true; - mSoftKeyDown = newKey; - } else { - mSoftKeyDown = mSoftKeyboard.mapToKey(x, y); - } - if (moveWithinPreviousKey || null == mSoftKeyDown) return mSoftKeyDown; - mKeyPressed = true; - - if (!movePress) { - tryPlayKeyDown(); - tryVibrate(); - } - - mLongPressTimer = longPressTimer; - - if (!movePress) { - if (mSoftKeyDown.getPopupResId() > 0 || mSoftKeyDown.repeatable()) { - mLongPressTimer.startTimer(); - } - } else { - mLongPressTimer.removeTimer(); - } - - int desired_width; - int desired_height; - float textSize; - Environment env = Environment.getInstance(); - - if (null != mBalloonOnKey) { - Drawable keyHlBg = mSoftKeyDown.getKeyHlBg(); - mBalloonOnKey.setBalloonBackground(keyHlBg); - - // Prepare the on-key balloon - int keyXMargin = mSoftKeyboard.getKeyXMargin(); - int keyYMargin = mSoftKeyboard.getKeyYMargin(); - desired_width = mSoftKeyDown.width() - 2 * keyXMargin; - desired_height = mSoftKeyDown.height() - 2 * keyYMargin; - textSize = env - .getKeyTextSize(SoftKeyType.KEYTYPE_ID_NORMAL_KEY != mSoftKeyDown.mKeyType.mKeyTypeId); - Drawable icon = mSoftKeyDown.getKeyIcon(); - if (null != icon) { - mBalloonOnKey.setBalloonConfig(icon, desired_width, - desired_height); - } else { - mBalloonOnKey.setBalloonConfig(mSoftKeyDown.getKeyLabel(), - textSize, true, mSoftKeyDown.getColorHl(), - desired_width, desired_height); - } - - mHintLocationToSkbContainer[0] = mPaddingLeft + mSoftKeyDown.mLeft - - (mBalloonOnKey.getWidth() - mSoftKeyDown.width()) / 2; - mHintLocationToSkbContainer[0] += mOffsetToSkbContainer[0]; - mHintLocationToSkbContainer[1] = mPaddingTop - + (mSoftKeyDown.mBottom - keyYMargin) - - mBalloonOnKey.getHeight(); - mHintLocationToSkbContainer[1] += mOffsetToSkbContainer[1]; - showBalloon(mBalloonOnKey, mHintLocationToSkbContainer, movePress); - } else { - mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, - mSoftKeyDown.mRight, mSoftKeyDown.mBottom); - invalidate(mDirtyRect); - } - - // Prepare the popup balloon - if (mSoftKeyDown.needBalloon()) { - Drawable balloonBg = mSoftKeyboard.getBalloonBackground(); - mBalloonPopup.setBalloonBackground(balloonBg); - - desired_width = mSoftKeyDown.width() + env.getKeyBalloonWidthPlus(); - desired_height = mSoftKeyDown.height() - + env.getKeyBalloonHeightPlus(); - textSize = env - .getBalloonTextSize(SoftKeyType.KEYTYPE_ID_NORMAL_KEY != mSoftKeyDown.mKeyType.mKeyTypeId); - Drawable iconPopup = mSoftKeyDown.getKeyIconPopup(); - if (null != iconPopup) { - mBalloonPopup.setBalloonConfig(iconPopup, desired_width, - desired_height); - } else { - mBalloonPopup.setBalloonConfig(mSoftKeyDown.getKeyLabel(), - textSize, mSoftKeyDown.needBalloon(), mSoftKeyDown - .getColorBalloon(), desired_width, - desired_height); - } - - // The position to show. - mHintLocationToSkbContainer[0] = mPaddingLeft + mSoftKeyDown.mLeft - + -(mBalloonPopup.getWidth() - mSoftKeyDown.width()) / 2; - mHintLocationToSkbContainer[0] += mOffsetToSkbContainer[0]; - mHintLocationToSkbContainer[1] = mPaddingTop + mSoftKeyDown.mTop - - mBalloonPopup.getHeight(); - mHintLocationToSkbContainer[1] += mOffsetToSkbContainer[1]; - showBalloon(mBalloonPopup, mHintLocationToSkbContainer, movePress); - } else { - mBalloonPopup.delayedDismiss(0); - } - - if (mRepeatForLongPress) longPressTimer.startTimer(); - return mSoftKeyDown; - } - - public SoftKey onKeyRelease(int x, int y) { - mKeyPressed = false; - if (null == mSoftKeyDown) return null; - - mLongPressTimer.removeTimer(); - - if (null != mBalloonOnKey) { - mBalloonOnKey.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS); - } else { - mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, - mSoftKeyDown.mRight, mSoftKeyDown.mBottom); - invalidate(mDirtyRect); - } - - if (mSoftKeyDown.needBalloon()) { - mBalloonPopup.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS); - } - - if (mSoftKeyDown.moveWithinKey(x - mPaddingLeft, y - mPaddingTop)) { - return mSoftKeyDown; - } - return null; - } - - public SoftKey onKeyMove(int x, int y) { - if (null == mSoftKeyDown) return null; - - if (mSoftKeyDown.moveWithinKey(x - mPaddingLeft, y - mPaddingTop)) { - return mSoftKeyDown; - } - - // The current key needs to be updated. - mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, - mSoftKeyDown.mRight, mSoftKeyDown.mBottom); - - if (mRepeatForLongPress) { - if (mMovingNeverHidePopupBalloon) { - return onKeyPress(x, y, mLongPressTimer, true); - } - - if (null != mBalloonOnKey) { - mBalloonOnKey.delayedDismiss(0); - } else { - invalidate(mDirtyRect); - } - - if (mSoftKeyDown.needBalloon()) { - mBalloonPopup.delayedDismiss(0); - } - - if (null != mLongPressTimer) { - mLongPressTimer.removeTimer(); - } - return onKeyPress(x, y, mLongPressTimer, true); - } else { - // When user moves between keys, repeated response is disabled. - return onKeyPress(x, y, mLongPressTimer, true); - } - } - - private void tryVibrate() { - if (!Settings.getVibrate()) { - return; - } - if (mVibrator == null) { - mVibrator = new Vibrator(); - } - mVibrator.vibrate(mVibratePattern, -1); - } - - private void tryPlayKeyDown() { - if (Settings.getKeySound()) { - mSoundManager.playKeyDown(); - } - } - - public void dimSoftKeyboard(boolean dimSkb) { - mDimSkb = dimSkb; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - if (null == mSoftKeyboard) return; - - canvas.translate(mPaddingLeft, mPaddingTop); - - Environment env = Environment.getInstance(); - mNormalKeyTextSize = env.getKeyTextSize(false); - mFunctionKeyTextSize = env.getKeyTextSize(true); - // Draw the last soft keyboard - int rowNum = mSoftKeyboard.getRowNum(); - int keyXMargin = mSoftKeyboard.getKeyXMargin(); - int keyYMargin = mSoftKeyboard.getKeyYMargin(); - for (int row = 0; row < rowNum; row++) { - KeyRow keyRow = mSoftKeyboard.getKeyRowForDisplay(row); - if (null == keyRow) continue; - List softKeys = keyRow.mSoftKeys; - int keyNum = softKeys.size(); - for (int i = 0; i < keyNum; i++) { - SoftKey softKey = softKeys.get(i); - if (SoftKeyType.KEYTYPE_ID_NORMAL_KEY == softKey.mKeyType.mKeyTypeId) { - mPaint.setTextSize(mNormalKeyTextSize); - } else { - mPaint.setTextSize(mFunctionKeyTextSize); - } - drawSoftKey(canvas, softKey, keyXMargin, keyYMargin); - } - } - - if (mDimSkb) { - mPaint.setColor(0xa0000000); - canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); - } - - mDirtyRect.setEmpty(); - } - - private void drawSoftKey(Canvas canvas, SoftKey softKey, int keyXMargin, - int keyYMargin) { - Drawable bg; - int textColor; - if (mKeyPressed && softKey == mSoftKeyDown) { - bg = softKey.getKeyHlBg(); - textColor = softKey.getColorHl(); - } else { - bg = softKey.getKeyBg(); - textColor = softKey.getColor(); - } - - if (null != bg) { - bg.setBounds(softKey.mLeft + keyXMargin, softKey.mTop + keyYMargin, - softKey.mRight - keyXMargin, softKey.mBottom - keyYMargin); - bg.draw(canvas); - } - - String keyLabel = softKey.getKeyLabel(); - Drawable keyIcon = softKey.getKeyIcon(); - if (null != keyIcon) { - Drawable icon = keyIcon; - int marginLeft = (softKey.width() - icon.getIntrinsicWidth()) / 2; - int marginRight = softKey.width() - icon.getIntrinsicWidth() - - marginLeft; - int marginTop = (softKey.height() - icon.getIntrinsicHeight()) / 2; - int marginBottom = softKey.height() - icon.getIntrinsicHeight() - - marginTop; - icon.setBounds(softKey.mLeft + marginLeft, - softKey.mTop + marginTop, softKey.mRight - marginRight, - softKey.mBottom - marginBottom); - icon.draw(canvas); - } else if (null != keyLabel) { - mPaint.setColor(textColor); - float x = softKey.mLeft - + (softKey.width() - mPaint.measureText(keyLabel)) / 2.0f; - int fontHeight = mFmi.bottom - mFmi.top; - float marginY = (softKey.height() - fontHeight) / 2.0f; - float y = softKey.mTop + marginY - mFmi.top + mFmi.bottom / 1.5f; - canvas.drawText(keyLabel, x, y + 1, mPaint); - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/SoundManager.java b/PinyinIME/src/com/android/inputmethod/pinyin/SoundManager.java deleted file mode 100644 index 82be407..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/SoundManager.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import android.content.Context; -import android.media.AudioManager; - -/** - * Class used to manage related sound resources. - */ -public class SoundManager { - private static SoundManager mInstance = null; - private Context mContext; - private AudioManager mAudioManager; - private final float FX_VOLUME = 1.0f; - private boolean mSilentMode; - - private SoundManager(Context context) { - mContext = context; - updateRingerMode(); - } - - public void updateRingerMode() { - if (mAudioManager == null) { - mAudioManager = (AudioManager) mContext - .getSystemService(Context.AUDIO_SERVICE); - } - mSilentMode = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL); - } - - public static SoundManager getInstance(Context context) { - if (null == mInstance) { - if (null != context) { - mInstance = new SoundManager(context); - } - } - return mInstance; - } - - public void playKeyDown() { - if (mAudioManager == null) { - updateRingerMode(); - } - if (!mSilentMode) { - int sound = AudioManager.FX_KEYPRESS_STANDARD; - mAudioManager.playSoundEffect(sound, FX_VOLUME); - } - } -} diff --git a/PinyinIME/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java b/PinyinIME/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java deleted file mode 100644 index fd192a3..0000000 --- a/PinyinIME/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java +++ /dev/null @@ -1,835 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.pinyin; - -import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.XmlResourceParser; -import android.graphics.drawable.Drawable; - -import java.io.IOException; -import java.util.regex.Pattern; - -import org.xmlpull.v1.XmlPullParserException; - -/** - * Class used to load a soft keyboard or a soft keyboard template from xml - * files. - */ -public class XmlKeyboardLoader { - /** - * The tag used to define an xml-based soft keyboard template. - */ - private static final String XMLTAG_SKB_TEMPLATE = "skb_template"; - - /** - * The tag used to indicate the soft key type which is defined inside the - * {@link #XMLTAG_SKB_TEMPLATE} element in the xml file. file. - */ - private static final String XMLTAG_KEYTYPE = "key_type"; - - /** - * The tag used to define a default key icon for enter/delete/space keys. It - * is defined inside the {@link #XMLTAG_SKB_TEMPLATE} element in the xml - * file. - */ - private static final String XMLTAG_KEYICON = "key_icon"; - - /** - * Attribute tag of the left and right margin for a key. A key's width - * should be larger than double of this value. Defined inside - * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. - */ - private static final String XMLATTR_KEY_XMARGIN = "key_xmargin"; - - /** - * Attribute tag of the top and bottom margin for a key. A key's height - * should be larger than double of this value. Defined inside - * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. - */ - private static final String XMLATTR_KEY_YMARGIN = "key_ymargin"; - - /** - * Attribute tag of the keyboard background image. Defined inside - * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. - */ - private static final String XMLATTR_SKB_BG = "skb_bg"; - - /** - * Attribute tag of the balloon background image for key press. Defined - * inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. - */ - private static final String XMLATTR_BALLOON_BG = "balloon_bg"; - - /** - * Attribute tag of the popup balloon background image for key press or - * popup mini keyboard. Defined inside {@link #XMLTAG_SKB_TEMPLATE} and - * {@link #XMLTAG_KEYBOARD}. - */ - private static final String XMLATTR_POPUP_BG = "popup_bg"; - - /** - * Attribute tag of the color to draw key label. Defined inside - * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}. - */ - private static final String XMLATTR_COLOR = "color"; - - /** - * Attribute tag of the color to draw key's highlighted label. Defined - * inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}. - */ - private static final String XMLATTR_COLOR_HIGHLIGHT = "color_highlight"; - - /** - * Attribute tag of the color to draw key's label in the popup balloon. - * Defined inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}. - */ - private static final String XMLATTR_COLOR_BALLOON = "color_balloon"; - - /** - * Attribute tag of the id of {@link #XMLTAG_KEYTYPE} and - * {@link #XMLTAG_KEY}. Key types and keys defined in a soft keyboard - * template should have id, because a soft keyboard needs the id to refer to - * these default definitions. If a key defined in {@link #XMLTAG_KEYBOARD} - * does not id, that means the key is newly defined; if it has id (and only - * has id), the id is used to find the default definition from the soft - * keyboard template. - */ - private static final String XMLATTR_ID = "id"; - - /** - * Attribute tag of the key background for a specified key type. Defined - * inside {@link #XMLTAG_KEYTYPE}. - */ - private static final String XMLATTR_KEYTYPE_BG = "bg"; - - /** - * Attribute tag of the key high-light background for a specified key type. - * Defined inside {@link #XMLTAG_KEYTYPE}. - */ - private static final String XMLATTR_KEYTYPE_HLBG = "hlbg"; - - /** - * Attribute tag of the starting x-position of an element. It can be defined - * in {@link #XMLTAG_ROW} and {@link #XMLTAG_KEY} in {XMLTAG_SKB_TEMPLATE}. - * If not defined, 0 will be used. For a key defined in - * {@link #XMLTAG_KEYBOARD}, it always use its previous keys information to - * calculate its own position. - */ - private static final String XMLATTR_START_POS_X = "start_pos_x"; - - /** - * Attribute tag of the starting y-position of an element. It can be defined - * in {@link #XMLTAG_ROW} and {@link #XMLTAG_KEY} in {XMLTAG_SKB_TEMPLATE}. - * If not defined, 0 will be used. For a key defined in - * {@link #XMLTAG_KEYBOARD}, it always use its previous keys information to - * calculate its own position. - */ - private static final String XMLATTR_START_POS_Y = "start_pos_y"; - - /** - * Attribute tag of a row's id. Defined {@link #XMLTAG_ROW}. If not defined, - * -1 will be used. Rows with id -1 will be enabled always, rows with same - * row id will be enabled when the id is the same to the activated id of the - * soft keyboard. - */ - private static final String XMLATTR_ROW_ID = "row_id"; - - /** The tag used to indicate the keyboard element in the xml file. */ - private static final String XMLTAG_KEYBOARD = "keyboard"; - - /** The tag used to indicate the row element in the xml file. */ - private static final String XMLTAG_ROW = "row"; - - /** The tag used to indicate key-array element in the xml file. */ - private static final String XMLTAG_KEYS = "keys"; - - /** - * The tag used to indicate a key element in the xml file. If the element is - * defined in a soft keyboard template, it should have an id. If it is - * defined in a soft keyboard, id is not required. - */ - private static final String XMLTAG_KEY = "key"; - - /** The tag used to indicate a key's toggle element in the xml file. */ - private static final String XMLTAG_TOGGLE_STATE = "toggle_state"; - - /** - * Attribute tag of the toggle state id for toggle key. Defined inside - * {@link #XMLTAG_TOGGLE_STATE} - */ - private static final String XMLATTR_TOGGLE_STATE_ID = "state_id"; - - /** Attribute tag of key template for the soft keyboard. */ - private static final String XMLATTR_SKB_TEMPLATE = "skb_template"; - - /** - * Attribute tag used to indicate whether this soft keyboard needs to be - * cached in memory for future use. {@link #DEFAULT_SKB_CACHE_FLAG} - * specifies the default value. - */ - private static final String XMLATTR_SKB_CACHE_FLAG = "skb_cache_flag"; - - /** - * Attribute tag used to indicate whether this soft keyboard is sticky. A - * sticky soft keyboard will keep the current layout unless user makes a - * switch explicitly. A none sticky soft keyboard will automatically goes - * back to the previous keyboard after click a none-function key. - * {@link #DEFAULT_SKB_STICKY_FLAG} specifies the default value. - */ - private static final String XMLATTR_SKB_STICKY_FLAG = "skb_sticky_flag"; - - /** Attribute tag to indicate whether it is a QWERTY soft keyboard. */ - private static final String XMLATTR_QWERTY = "qwerty"; - - /** - * When the soft keyboard is a QWERTY one, this attribute tag to get the - * information that whether it is defined in upper case. - */ - private static final String XMLATTR_QWERTY_UPPERCASE = "qwerty_uppercase"; - - /** Attribute tag of key type. */ - private static final String XMLATTR_KEY_TYPE = "key_type"; - - /** Attribute tag of key width. */ - private static final String XMLATTR_KEY_WIDTH = "width"; - - /** Attribute tag of key height. */ - private static final String XMLATTR_KEY_HEIGHT = "height"; - - /** Attribute tag of the key's repeating ability. */ - private static final String XMLATTR_KEY_REPEAT = "repeat"; - - /** Attribute tag of the key's behavior for balloon. */ - private static final String XMLATTR_KEY_BALLOON = "balloon"; - - /** Attribute tag of the key splitter in a key array. */ - private static final String XMLATTR_KEY_SPLITTER = "splitter"; - - /** Attribute tag of the key labels in a key array. */ - private static final String XMLATTR_KEY_LABELS = "labels"; - - /** Attribute tag of the key codes in a key array. */ - private static final String XMLATTR_KEY_CODES = "codes"; - - /** Attribute tag of the key label in a key. */ - private static final String XMLATTR_KEY_LABEL = "label"; - - /** Attribute tag of the key code in a key. */ - private static final String XMLATTR_KEY_CODE = "code"; - - /** Attribute tag of the key icon in a key. */ - private static final String XMLATTR_KEY_ICON = "icon"; - - /** Attribute tag of the key's popup icon in a key. */ - private static final String XMLATTR_KEY_ICON_POPUP = "icon_popup"; - - /** The id for a mini popup soft keyboard. */ - private static final String XMLATTR_KEY_POPUP_SKBID = "popup_skb"; - - private static boolean DEFAULT_SKB_CACHE_FLAG = true; - - private static boolean DEFAULT_SKB_STICKY_FLAG = true; - - /** - * The key type id for invalid key type. It is also used to generate next - * valid key type id by adding 1. - */ - private static final int KEYTYPE_ID_LAST = -1; - - private Context mContext; - - private Resources mResources; - - /** The event type in parsing the xml file. */ - private int mXmlEventType; - - /** - * The current soft keyboard template used by the current soft keyboard - * under loading. - **/ - private SkbTemplate mSkbTemplate; - - /** The x position for the next key. */ - float mKeyXPos; - - /** The y position for the next key. */ - float mKeyYPos; - - /** The width of the keyboard to load. */ - int mSkbWidth; - - /** The height of the keyboard to load. */ - int mSkbHeight; - - /** Key margin in x-way. */ - float mKeyXMargin = 0; - - /** Key margin in y-way. */ - float mKeyYMargin = 0; - - /** - * Used to indicate whether next event has been fetched during processing - * the the current event. - */ - boolean mNextEventFetched = false; - - String mAttrTmp; - - class KeyCommonAttributes { - XmlResourceParser mXrp; - int keyType; - float keyWidth; - float keyHeight; - boolean repeat; - boolean balloon; - - KeyCommonAttributes(XmlResourceParser xrp) { - mXrp = xrp; - balloon = true; - } - - // Make sure the default object is not null. - boolean getAttributes(KeyCommonAttributes defAttr) { - keyType = getInteger(mXrp, XMLATTR_KEY_TYPE, defAttr.keyType); - keyWidth = getFloat(mXrp, XMLATTR_KEY_WIDTH, defAttr.keyWidth); - keyHeight = getFloat(mXrp, XMLATTR_KEY_HEIGHT, defAttr.keyHeight); - repeat = getBoolean(mXrp, XMLATTR_KEY_REPEAT, defAttr.repeat); - balloon = getBoolean(mXrp, XMLATTR_KEY_BALLOON, defAttr.balloon); - if (keyType < 0 || keyWidth <= 0 || keyHeight <= 0) { - return false; - } - return true; - } - } - - public XmlKeyboardLoader(Context context) { - mContext = context; - mResources = mContext.getResources(); - } - - public SkbTemplate loadSkbTemplate(int resourceId) { - if (null == mContext || 0 == resourceId) { - return null; - } - Resources r = mResources; - XmlResourceParser xrp = r.getXml(resourceId); - - KeyCommonAttributes attrDef = new KeyCommonAttributes(xrp); - KeyCommonAttributes attrKey = new KeyCommonAttributes(xrp); - - mSkbTemplate = new SkbTemplate(resourceId); - int lastKeyTypeId = KEYTYPE_ID_LAST; - int globalColor = 0; - int globalColorHl = 0; - int globalColorBalloon = 0; - try { - mXmlEventType = xrp.next(); - while (mXmlEventType != XmlResourceParser.END_DOCUMENT) { - mNextEventFetched = false; - if (mXmlEventType == XmlResourceParser.START_TAG) { - String attribute = xrp.getName(); - if (XMLTAG_SKB_TEMPLATE.compareTo(attribute) == 0) { - Drawable skbBg = getDrawable(xrp, XMLATTR_SKB_BG, null); - Drawable balloonBg = getDrawable(xrp, - XMLATTR_BALLOON_BG, null); - Drawable popupBg = getDrawable(xrp, XMLATTR_POPUP_BG, - null); - if (null == skbBg || null == balloonBg - || null == popupBg) { - return null; - } - mSkbTemplate.setBackgrounds(skbBg, balloonBg, popupBg); - - float xMargin = getFloat(xrp, XMLATTR_KEY_XMARGIN, 0); - float yMargin = getFloat(xrp, XMLATTR_KEY_YMARGIN, 0); - mSkbTemplate.setMargins(xMargin, yMargin); - - // Get default global colors. - globalColor = getColor(xrp, XMLATTR_COLOR, 0); - globalColorHl = getColor(xrp, XMLATTR_COLOR_HIGHLIGHT, - 0xffffffff); - globalColorBalloon = getColor(xrp, - XMLATTR_COLOR_BALLOON, 0xffffffff); - } else if (XMLTAG_KEYTYPE.compareTo(attribute) == 0) { - int id = getInteger(xrp, XMLATTR_ID, KEYTYPE_ID_LAST); - Drawable bg = getDrawable(xrp, XMLATTR_KEYTYPE_BG, null); - Drawable hlBg = getDrawable(xrp, XMLATTR_KEYTYPE_HLBG, - null); - int color = getColor(xrp, XMLATTR_COLOR, globalColor); - int colorHl = getColor(xrp, XMLATTR_COLOR_HIGHLIGHT, - globalColorHl); - int colorBalloon = getColor(xrp, XMLATTR_COLOR_BALLOON, - globalColorBalloon); - if (id != lastKeyTypeId + 1) { - return null; - } - SoftKeyType keyType = mSkbTemplate.createKeyType(id, - bg, hlBg); - keyType.setColors(color, colorHl, colorBalloon); - if (!mSkbTemplate.addKeyType(keyType)) { - return null; - } - lastKeyTypeId = id; - } else if (XMLTAG_KEYICON.compareTo(attribute) == 0) { - int keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0); - Drawable icon = getDrawable(xrp, XMLATTR_KEY_ICON, null); - Drawable iconPopup = getDrawable(xrp, - XMLATTR_KEY_ICON_POPUP, null); - if (null != icon && null != iconPopup) { - mSkbTemplate.addDefaultKeyIcons(keyCode, icon, - iconPopup); - } - } else if (XMLTAG_KEY.compareTo(attribute) == 0) { - int keyId = this.getInteger(xrp, XMLATTR_ID, -1); - if (-1 == keyId) return null; - - if (!attrKey.getAttributes(attrDef)) { - return null; - } - - // Update the key position for the key. - mKeyXPos = getFloat(xrp, XMLATTR_START_POS_X, 0); - mKeyYPos = getFloat(xrp, XMLATTR_START_POS_Y, 0); - - SoftKey softKey = getSoftKey(xrp, attrKey); - if (null == softKey) return null; - mSkbTemplate.addDefaultKey(keyId, softKey); - } - } - // Get the next tag. - if (!mNextEventFetched) mXmlEventType = xrp.next(); - } - xrp.close(); - return mSkbTemplate; - } catch (XmlPullParserException e) { - // Log.e(TAG, "Ill-formatted keyboard template resource file"); - } catch (IOException e) { - // Log.e(TAG, "Unable to keyboard template resource file"); - } - return null; - } - - public SoftKeyboard loadKeyboard(int resourceId, int skbWidth, int skbHeight) { - if (null == mContext) return null; - Resources r = mResources; - SkbPool skbPool = SkbPool.getInstance(); - XmlResourceParser xrp = mContext.getResources().getXml(resourceId); - mSkbTemplate = null; - SoftKeyboard softKeyboard = null; - Drawable skbBg; - Drawable popupBg; - Drawable balloonBg; - SoftKey softKey = null; - - KeyCommonAttributes attrDef = new KeyCommonAttributes(xrp); - KeyCommonAttributes attrSkb = new KeyCommonAttributes(xrp); - KeyCommonAttributes attrRow = new KeyCommonAttributes(xrp); - KeyCommonAttributes attrKeys = new KeyCommonAttributes(xrp); - KeyCommonAttributes attrKey = new KeyCommonAttributes(xrp); - - mKeyXPos = 0; - mKeyYPos = 0; - mSkbWidth = skbWidth; - mSkbHeight = skbHeight; - - try { - mKeyXMargin = 0; - mKeyYMargin = 0; - mXmlEventType = xrp.next(); - while (mXmlEventType != XmlResourceParser.END_DOCUMENT) { - mNextEventFetched = false; - if (mXmlEventType == XmlResourceParser.START_TAG) { - String attr = xrp.getName(); - // 1. Is it the root element, "keyboard"? - if (XMLTAG_KEYBOARD.compareTo(attr) == 0) { - // 1.1 Get the keyboard template id. - int skbTemplateId = xrp.getAttributeResourceValue(null, - XMLATTR_SKB_TEMPLATE, 0); - - // 1.2 Try to get the template from pool. If it is not - // in, the pool will try to load it. - mSkbTemplate = skbPool.getSkbTemplate(skbTemplateId, - mContext); - - if (null == mSkbTemplate - || !attrSkb.getAttributes(attrDef)) { - return null; - } - - boolean cacheFlag = getBoolean(xrp, - XMLATTR_SKB_CACHE_FLAG, DEFAULT_SKB_CACHE_FLAG); - boolean stickyFlag = getBoolean(xrp, - XMLATTR_SKB_STICKY_FLAG, - DEFAULT_SKB_STICKY_FLAG); - boolean isQwerty = getBoolean(xrp, XMLATTR_QWERTY, - false); - boolean isQwertyUpperCase = getBoolean(xrp, - XMLATTR_QWERTY_UPPERCASE, false); - - softKeyboard = new SoftKeyboard(resourceId, - mSkbTemplate, mSkbWidth, mSkbHeight); - softKeyboard.setFlags(cacheFlag, stickyFlag, isQwerty, - isQwertyUpperCase); - - mKeyXMargin = getFloat(xrp, XMLATTR_KEY_XMARGIN, - mSkbTemplate.getXMargin()); - mKeyYMargin = getFloat(xrp, XMLATTR_KEY_YMARGIN, - mSkbTemplate.getYMargin()); - skbBg = getDrawable(xrp, XMLATTR_SKB_BG, null); - popupBg = getDrawable(xrp, XMLATTR_POPUP_BG, null); - balloonBg = getDrawable(xrp, XMLATTR_BALLOON_BG, null); - if (null != skbBg) { - softKeyboard.setSkbBackground(skbBg); - } - if (null != popupBg) { - softKeyboard.setPopupBackground(popupBg); - } - if (null != balloonBg) { - softKeyboard.setKeyBalloonBackground(balloonBg); - } - softKeyboard.setKeyMargins(mKeyXMargin, mKeyYMargin); - } else if (XMLTAG_ROW.compareTo(attr) == 0) { - if (!attrRow.getAttributes(attrSkb)) { - return null; - } - // Get the starting positions for the row. - mKeyXPos = getFloat(xrp, XMLATTR_START_POS_X, 0); - mKeyYPos = getFloat(xrp, XMLATTR_START_POS_Y, mKeyYPos); - int rowId = getInteger(xrp, XMLATTR_ROW_ID, - KeyRow.ALWAYS_SHOW_ROW_ID); - softKeyboard.beginNewRow(rowId, mKeyYPos); - } else if (XMLTAG_KEYS.compareTo(attr) == 0) { - if (null == softKeyboard) return null; - if (!attrKeys.getAttributes(attrRow)) { - return null; - } - - String splitter = xrp.getAttributeValue(null, - XMLATTR_KEY_SPLITTER); - splitter = Pattern.quote(splitter); - String labels = xrp.getAttributeValue(null, - XMLATTR_KEY_LABELS); - String codes = xrp.getAttributeValue(null, - XMLATTR_KEY_CODES); - if (null == splitter || null == labels) { - return null; - } - String labelArr[] = labels.split(splitter); - String codeArr[] = null; - if (null != codes) { - codeArr = codes.split(splitter); - if (labelArr.length != codeArr.length) { - return null; - } - } - - for (int i = 0; i < labelArr.length; i++) { - softKey = new SoftKey(); - int keyCode = 0; - if (null != codeArr) { - keyCode = Integer.valueOf(codeArr[i]); - } - softKey.setKeyAttribute(keyCode, labelArr[i], - attrKeys.repeat, attrKeys.balloon); - - softKey.setKeyType(mSkbTemplate - .getKeyType(attrKeys.keyType), null, null); - - float left, right, top, bottom; - left = mKeyXPos; - - right = left + attrKeys.keyWidth; - top = mKeyYPos; - bottom = top + attrKeys.keyHeight; - - if (right - left < 2 * mKeyXMargin) return null; - if (bottom - top < 2 * mKeyYMargin) return null; - - softKey.setKeyDimensions(left, top, right, bottom); - softKeyboard.addSoftKey(softKey); - mKeyXPos = right; - if ((int) mKeyXPos * mSkbWidth > mSkbWidth) { - return null; - } - } - } else if (XMLTAG_KEY.compareTo(attr) == 0) { - if (null == softKeyboard) { - return null; - } - if (!attrKey.getAttributes(attrRow)) { - return null; - } - - int keyId = this.getInteger(xrp, XMLATTR_ID, -1); - if (keyId >= 0) { - softKey = mSkbTemplate.getDefaultKey(keyId); - } else { - softKey = getSoftKey(xrp, attrKey); - } - if (null == softKey) return null; - - // Update the position for next key. - mKeyXPos = softKey.mRightF; - if ((int) mKeyXPos * mSkbWidth > mSkbWidth) { - return null; - } - // If the current xml event type becomes a starting tag, - // it indicates that we have parsed too much to get - // toggling states, and we started a new row. In this - // case, the row starting position information should - // be updated. - if (mXmlEventType == XmlResourceParser.START_TAG) { - attr = xrp.getName(); - if (XMLTAG_ROW.compareTo(attr) == 0) { - mKeyYPos += attrRow.keyHeight; - if ((int) mKeyYPos * mSkbHeight > mSkbHeight) { - return null; - } - } - } - softKeyboard.addSoftKey(softKey); - } - } else if (mXmlEventType == XmlResourceParser.END_TAG) { - String attr = xrp.getName(); - if (XMLTAG_ROW.compareTo(attr) == 0) { - mKeyYPos += attrRow.keyHeight; - if ((int) mKeyYPos * mSkbHeight > mSkbHeight) { - return null; - } - } - } - - // Get the next tag. - if (!mNextEventFetched) mXmlEventType = xrp.next(); - } - xrp.close(); - softKeyboard.setSkbCoreSize(mSkbWidth, mSkbHeight); - return softKeyboard; - } catch (XmlPullParserException e) { - // Log.e(TAG, "Ill-formatted keybaord resource file"); - } catch (IOException e) { - // Log.e(TAG, "Unable to read keyboard resource file"); - } - return null; - } - - // Caller makes sure xrp and r are valid. - private SoftKey getSoftKey(XmlResourceParser xrp, - KeyCommonAttributes attrKey) throws XmlPullParserException, - IOException { - int keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0); - String keyLabel = getString(xrp, XMLATTR_KEY_LABEL, null); - Drawable keyIcon = getDrawable(xrp, XMLATTR_KEY_ICON, null); - Drawable keyIconPopup = getDrawable(xrp, XMLATTR_KEY_ICON_POPUP, null); - int popupSkbId = xrp.getAttributeResourceValue(null, - XMLATTR_KEY_POPUP_SKBID, 0); - - if (null == keyLabel && null == keyIcon) { - keyIcon = mSkbTemplate.getDefaultKeyIcon(keyCode); - keyIconPopup = mSkbTemplate.getDefaultKeyIconPopup(keyCode); - if (null == keyIcon || null == keyIconPopup) return null; - } - - // Dimension information must been initialized before - // getting toggle state, because mKeyYPos may be changed - // to next row when trying to get toggle state. - float left, right, top, bottom; - left = mKeyXPos; - right = left + attrKey.keyWidth; - top = mKeyYPos; - bottom = top + attrKey.keyHeight; - - if (right - left < 2 * mKeyXMargin) return null; - if (bottom - top < 2 * mKeyYMargin) return null; - - // Try to find if the next tag is - // {@link #XMLTAG_TOGGLE_STATE_OF_KEY}, if yes, try to - // create a toggle key. - boolean toggleKey = false; - mXmlEventType = xrp.next(); - mNextEventFetched = true; - - SoftKey softKey; - if (mXmlEventType == XmlResourceParser.START_TAG) { - mAttrTmp = xrp.getName(); - if (mAttrTmp.compareTo(XMLTAG_TOGGLE_STATE) == 0) { - toggleKey = true; - } - } - if (toggleKey) { - softKey = new SoftKeyToggle(); - if (!((SoftKeyToggle) softKey).setToggleStates(getToggleStates( - attrKey, (SoftKeyToggle) softKey, keyCode))) { - return null; - } - } else { - softKey = new SoftKey(); - } - - // Set the normal state - softKey.setKeyAttribute(keyCode, keyLabel, attrKey.repeat, - attrKey.balloon); - softKey.setPopupSkbId(popupSkbId); - softKey.setKeyType(mSkbTemplate.getKeyType(attrKey.keyType), keyIcon, - keyIconPopup); - - softKey.setKeyDimensions(left, top, right, bottom); - return softKey; - } - - private SoftKeyToggle.ToggleState getToggleStates( - KeyCommonAttributes attrKey, SoftKeyToggle softKey, int defKeyCode) - throws XmlPullParserException, IOException { - XmlResourceParser xrp = attrKey.mXrp; - int stateId = getInteger(xrp, XMLATTR_TOGGLE_STATE_ID, 0); - if (0 == stateId) return null; - - String keyLabel = getString(xrp, XMLATTR_KEY_LABEL, null); - int keyTypeId = getInteger(xrp, XMLATTR_KEY_TYPE, KEYTYPE_ID_LAST); - int keyCode; - if (null == keyLabel) { - keyCode = getInteger(xrp, XMLATTR_KEY_CODE, defKeyCode); - } else { - keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0); - } - Drawable icon = getDrawable(xrp, XMLATTR_KEY_ICON, null); - Drawable iconPopup = getDrawable(xrp, XMLATTR_KEY_ICON_POPUP, null); - if (null == icon && null == keyLabel) { - return null; - } - SoftKeyToggle.ToggleState rootState = softKey.createToggleState(); - rootState.setStateId(stateId); - rootState.mKeyType = null; - if (KEYTYPE_ID_LAST != keyTypeId) { - rootState.mKeyType = mSkbTemplate.getKeyType(keyTypeId); - } - rootState.mKeyCode = keyCode; - rootState.mKeyIcon = icon; - rootState.mKeyIconPopup = iconPopup; - rootState.mKeyLabel = keyLabel; - - boolean repeat = getBoolean(xrp, XMLATTR_KEY_REPEAT, attrKey.repeat); - boolean balloon = getBoolean(xrp, XMLATTR_KEY_BALLOON, attrKey.balloon); - rootState.setStateFlags(repeat, balloon); - - rootState.mNextState = null; - - // If there is another toggle state. - mXmlEventType = xrp.next(); - while (mXmlEventType != XmlResourceParser.START_TAG - && mXmlEventType != XmlResourceParser.END_DOCUMENT) { - mXmlEventType = xrp.next(); - } - if (mXmlEventType == XmlResourceParser.START_TAG) { - String attr = xrp.getName(); - if (attr.compareTo(XMLTAG_TOGGLE_STATE) == 0) { - SoftKeyToggle.ToggleState nextState = getToggleStates(attrKey, - softKey, defKeyCode); - if (null == nextState) return null; - rootState.mNextState = nextState; - } - } - - return rootState; - } - - private int getInteger(XmlResourceParser xrp, String name, int defValue) { - int resId = xrp.getAttributeResourceValue(null, name, 0); - String s; - if (resId == 0) { - s = xrp.getAttributeValue(null, name); - if (null == s) return defValue; - try { - int ret = Integer.valueOf(s); - return ret; - } catch (NumberFormatException e) { - return defValue; - } - } else { - return Integer.parseInt(mContext.getResources().getString(resId)); - } - } - - private int getColor(XmlResourceParser xrp, String name, int defValue) { - int resId = xrp.getAttributeResourceValue(null, name, 0); - String s; - if (resId == 0) { - s = xrp.getAttributeValue(null, name); - if (null == s) return defValue; - try { - int ret = Integer.valueOf(s); - return ret; - } catch (NumberFormatException e) { - return defValue; - } - } else { - return mContext.getResources().getColor(resId); - } - } - - private String getString(XmlResourceParser xrp, String name, String defValue) { - int resId = xrp.getAttributeResourceValue(null, name, 0); - if (resId == 0) { - return xrp.getAttributeValue(null, name); - } else { - return mContext.getResources().getString(resId); - } - } - - private float getFloat(XmlResourceParser xrp, String name, float defValue) { - int resId = xrp.getAttributeResourceValue(null, name, 0); - if (resId == 0) { - String s = xrp.getAttributeValue(null, name); - if (null == s) return defValue; - try { - float ret; - if (s.endsWith("%p")) { - ret = Float.parseFloat(s.substring(0, s.length() - 2)) / 100; - } else { - ret = Float.parseFloat(s); - } - return ret; - } catch (NumberFormatException e) { - return defValue; - } - } else { - return mContext.getResources().getDimension(resId); - } - } - - private boolean getBoolean(XmlResourceParser xrp, String name, - boolean defValue) { - String s = xrp.getAttributeValue(null, name); - if (null == s) return defValue; - try { - boolean ret = Boolean.parseBoolean(s); - return ret; - } catch (NumberFormatException e) { - return defValue; - } - } - - private Drawable getDrawable(XmlResourceParser xrp, String name, - Drawable defValue) { - int resId = xrp.getAttributeResourceValue(null, name, 0); - if (0 == resId) return defValue; - return mResources.getDrawable(resId); - } -} diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 0000000..141a0b7 --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH := $(call my-dir) + +### shared library + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp \ + share/dictbuilder.cpp \ + share/dictlist.cpp \ + share/dicttrie.cpp \ + share/lpicache.cpp \ + share/matrixsearch.cpp \ + share/mystdlib.cpp \ + share/ngram.cpp \ + share/pinyinime.cpp \ + share/searchutility.cpp \ + share/spellingtable.cpp \ + share/spellingtrie.cpp \ + share/splparser.cpp \ + share/userdict.cpp \ + share/utf16char.cpp \ + share/utf16reader.cpp \ + share/sync.cpp + +LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) +LOCAL_LDLIBS += -lpthread +LOCAL_MODULE := libjni_pinyinime +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libcutils libutils + +include $(BUILD_SHARED_LIBRARY) diff --git a/jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp b/jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp new file mode 100644 index 0000000..dcb63a1 --- /dev/null +++ b/jni/android/com_android_inputmethod_pinyin_PinyinDecoderService.cpp @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "../include/pinyinime.h" +#include "../include/sync.h" +#include "../include/userdict.h" + +#ifdef __cplusplus +extern "C" { +#endif + +using namespace ime_pinyin; + +#define RET_BUF_LEN 256 + +static char16 retbuf[RET_BUF_LEN]; +static char16 (*predict_buf)[kMaxPredictSize + 1] = NULL; +static size_t predict_len; + +static Sync sync_worker; + +static struct file_descriptor_offsets_t +{ + jclass mClass; + jfieldID mDescriptor; +} gFileDescriptorOffsets; + +JNIEXPORT jboolean JNICALL nativeImOpenDecoder(JNIEnv* env, jclass jclazz, + jbyteArray fn_sys_dict, + jbyteArray fn_usr_dict) { + jbyte *fsd = (*env).GetByteArrayElements(fn_sys_dict, 0); + jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0); + + jboolean jret = JNI_FALSE; + + if (im_open_decoder((const char*)fsd, (const char*)fud)) + jret = JNI_TRUE; + + (*env).ReleaseByteArrayElements(fn_sys_dict, fsd, 0); + (*env).ReleaseByteArrayElements(fn_usr_dict, fud, 0); + + return jret; +} + +JNIEXPORT jboolean JNICALL nativeImOpenDecoderFd(JNIEnv* env, jclass jclazz, + jobject fd_sys_dict, + jlong startoffset, + jlong length, + jbyteArray fn_usr_dict) { + jint fd = env->GetIntField(fd_sys_dict, gFileDescriptorOffsets.mDescriptor); + jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0); + + jboolean jret = JNI_FALSE; + + int newfd = dup(fd); + if (im_open_decoder_fd(newfd, startoffset, length, (const char*)fud)) + jret = JNI_TRUE; + + close(newfd); + + (*env).ReleaseByteArrayElements(fn_usr_dict, fud, 0); + + return jret; +} + +JNIEXPORT void JNICALL nativeImSetMaxLens(JNIEnv* env, jclass jclazz, + jint max_sps_len, + jint max_hzs_len) { + im_set_max_lens(static_cast(max_sps_len), + static_cast(max_hzs_len)); + return; +} + +JNIEXPORT jboolean JNICALL nativeImCloseDecoder(JNIEnv* env, jclass jclazz) { + im_close_decoder(); + return JNI_TRUE; +} + +JNIEXPORT jint JNICALL nativeImSearch(JNIEnv* env, jclass jclazz, + jbyteArray pybuf, jint pylen) { + jbyte *array_body = (*env).GetByteArrayElements(pybuf, 0); + + jint jret = 0; + if (NULL != array_body) { + jret = im_search((const char*)array_body, pylen); + } + + (*env).ReleaseByteArrayElements(pybuf, array_body, 0); + + return jret; +} + +JNIEXPORT jint JNICALL nativeImDelSearch(JNIEnv* env, jclass jclazz, jint pos, + jboolean is_pos_in_splid, + jboolean clear_fixed_this_step) { + return im_delsearch(pos, is_pos_in_splid, clear_fixed_this_step); +} + +JNIEXPORT void JNICALL nativeImResetSearch(JNIEnv* env, jclass jclazz) { + im_reset_search(); + return; +} + +JNIEXPORT jint JNICALL nativeImAddLetter(JNIEnv *env, jclass clazz, jbyte ch) { + return im_add_letter(ch); +} + +JNIEXPORT jstring JNICALL nativeImGetPyStr(JNIEnv* env, jclass jclazz, + jboolean decoded) { + size_t py_len; + const char *py = im_get_sps_str(&py_len); // py_len gets decoded length + assert(NULL != py); + if (!decoded) + py_len = strlen(py); + + const unsigned short *spl_start; + size_t len; + len = im_get_spl_start_pos(spl_start); + + size_t i; + for (i = 0; i < py_len; i++) + retbuf[i] = py[i]; + retbuf[i] = (char16)'\0'; + + jstring retstr = (*env).NewString((unsigned short*)retbuf, i); + return retstr; +} + +JNIEXPORT jint JNICALL nativeImGetPyStrLen(JNIEnv* env, jclass jclazz, + jboolean decoded) { + size_t py_len; + const char *py = im_get_sps_str(&py_len); // py_len gets decoded length + assert(NULL != py); + if (!decoded) + py_len = strlen(py); + return py_len; +} + +JNIEXPORT jintArray JNICALL nativeImGetSplStart(JNIEnv* env, jclass jclazz) { + const unsigned short *spl_start; + size_t len; + + // There will be len + 1 elements in the buffer when len > 0. + len = im_get_spl_start_pos(spl_start); + + jintArray arr = (*env).NewIntArray(len + 2); + jint *arr_body = (*env).GetIntArrayElements(arr, 0); + assert(NULL != arr_body); + arr_body[0] = len; // element 0 is used to store the length of buffer. + for (size_t i = 0; i <= len; i++) + arr_body[i + 1] = spl_start[i]; + + (*env).ReleaseIntArrayElements(arr, arr_body, 0); + + return arr; +} + +JNIEXPORT jstring JNICALL nativeImGetChoice(JNIEnv *env, jclass clazz, + jint candidateId) { + jstring retstr; + if(im_get_candidate(candidateId, retbuf, RET_BUF_LEN)) { + retstr = (*env).NewString(retbuf, utf16_strlen(retbuf)); + return retstr; + } else { + retstr = (*env).NewString((unsigned short*)retbuf, 0); + return retstr; + } +} + +JNIEXPORT jint JNICALL nativeImChoose(JNIEnv *env, jclass clazz, + jint choice_id) { + return im_choose(choice_id); +} + +JNIEXPORT jint JNICALL nativeImCancelLastChoice(JNIEnv *env, jclass clazz) { + return im_cancel_last_choice(); +} + +JNIEXPORT jint JNICALL nativeImGetFixedLen(JNIEnv *env, jclass clazz) { + return im_get_fixed_len(); +} + +JNIEXPORT jboolean JNICALL nativeImCancelInput(JNIEnv *env, jclass clazz) { + if (im_cancel_input()) + return JNI_TRUE; + + return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL nativeImFlushCache(JNIEnv *env, jclass clazz) { + im_flush_cache(); + return JNI_TRUE; +} + +JNIEXPORT jint JNICALL nativeImGetPredictsNum(JNIEnv *env, jclass clazz, + jstring fixed_str) { + char16 *fixed_ptr = (char16*)(*env).GetStringChars(fixed_str, false); + size_t fixed_len = (size_t)(*env).GetStringLength(fixed_str); + + char16 fixed_buf[kMaxPredictSize + 1]; + + if (fixed_len > kMaxPredictSize) { + fixed_ptr += fixed_len - kMaxPredictSize; + fixed_len = kMaxPredictSize; + } + utf16_strncpy(fixed_buf, fixed_ptr, fixed_len); + fixed_buf[fixed_len] = (char16)'\0'; + + predict_len = im_get_predicts(fixed_buf, predict_buf); + + (*env).ReleaseStringChars(fixed_str, fixed_ptr); + + return predict_len; +} + +JNIEXPORT jstring JNICALL nativeImGetPredictItem(JNIEnv *env, jclass clazz, + jint predict_no) { + jstring retstr; + + if (predict_no < 0 || (size_t)predict_no >= predict_len) { + retstr = (*env).NewString((unsigned short*)predict_buf[0], 0); + } else { + retstr = (*env).NewString((unsigned short*)predict_buf[predict_no], + utf16_strlen(predict_buf[predict_no])); + } + return retstr; +} + +JNIEXPORT jboolean JNICALL nativeSyncBegin(JNIEnv *env, jclass clazz, + jbyteArray dict_file) { + jbyte *file_name = (*env).GetByteArrayElements(dict_file, 0); + + jboolean jret = JNI_FALSE; + if (true == sync_worker.begin((const char *)file_name)) + jret = JNI_TRUE; + + (*env).ReleaseByteArrayElements(dict_file, file_name, 0); + + return jret; +} + +JNIEXPORT jboolean JNICALL nativeSyncFinish(JNIEnv *env, jclass clazz) { + sync_worker.finish(); + return JNI_TRUE; +} + +JNIEXPORT jint JNICALL nativeSyncGetCapacity(JNIEnv *env, jclass clazz) { + return sync_worker.get_capacity(); +} + +JNIEXPORT jint JNICALL nativeSyncPutLemmas(JNIEnv *env, jclass clazz, + jstring tomerge) { + + char16 *ptr = (char16*)(*env).GetStringChars(tomerge, NULL); + int len = (size_t)(*env).GetStringLength(tomerge); + + int added = sync_worker.put_lemmas(ptr, len); + + (*env).ReleaseStringChars(tomerge, ptr); + + return added; +} + +JNIEXPORT jstring JNICALL nativeSyncGetLemmas(JNIEnv *env, jclass clazz) { + + int len = sync_worker.get_lemmas(retbuf, RET_BUF_LEN); + if (len == 0) + return NULL; + jstring retstr; + retstr = (*env).NewString((unsigned short*)retbuf, len); + return retstr; +} + +JNIEXPORT jint JNICALL nativeSyncGetLastCount(JNIEnv *env, jclass clazz) { + return sync_worker.get_last_got_count(); +} + +JNIEXPORT jint JNICALL nativeSyncGetTotalCount(JNIEnv *env, jclass clazz) { + return sync_worker.get_total_count(); +} + +JNIEXPORT jboolean JNICALL nativeSyncClearLastGot(JNIEnv *env, jclass clazz) { + sync_worker.clear_last_got(); + return JNI_TRUE; +} + +/** + * Table of methods associated with a single class. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + /* ------Functions for Pinyin-to-hanzi decoding begin--------->> */ + { "nativeImOpenDecoder", "([B[B)Z", + (void*) nativeImOpenDecoder }, + { "nativeImOpenDecoderFd", "(Ljava/io/FileDescriptor;JJ[B)Z", + (void*) nativeImOpenDecoderFd }, + { "nativeImSetMaxLens", "(II)V", + (void*) nativeImSetMaxLens }, + { "nativeImCloseDecoder", "()Z", + (void*) nativeImCloseDecoder }, + { "nativeImSearch", "([BI)I", + (void*) nativeImSearch }, + { "nativeImDelSearch", "(IZZ)I", + (void*) nativeImDelSearch }, + { "nativeImResetSearch", "()V", + (void*) nativeImResetSearch }, + { "nativeImAddLetter", "(B)I", + (void*) nativeImAddLetter }, + { "nativeImGetPyStr", "(Z)Ljava/lang/String;", + (void*) nativeImGetPyStr }, + { "nativeImGetPyStrLen", "(Z)I", + (void*) nativeImGetPyStrLen }, + { "nativeImGetSplStart", "()[I", + (void*) nativeImGetSplStart }, + { "nativeImGetChoice", "(I)Ljava/lang/String;", + (void*) nativeImGetChoice }, + { "nativeImChoose", "(I)I", + (void*) nativeImChoose }, + { "nativeImCancelLastChoice", "()I", + (void*) nativeImCancelLastChoice }, + { "nativeImGetFixedLen", "()I", + (void*) nativeImGetFixedLen }, + { "nativeImGetPredictsNum", "(Ljava/lang/String;)I", + (void*) nativeImGetPredictsNum }, + { "nativeImGetPredictItem", "(I)Ljava/lang/String;", + (void*) nativeImGetPredictItem }, + { "nativeImCancelInput", "()Z", + (void*) nativeImCancelInput }, + { "nativeImFlushCache", "()Z", + (void*) nativeImFlushCache }, + /* <<----Functions for Pinyin-to-hanzi decoding end------------- */ + + /* ------Functions for sync begin----------------------------->> */ + { "nativeSyncBegin", "([B)Z", + (void*) nativeSyncBegin }, + { "nativeSyncFinish", "()Z", + (void*) nativeSyncFinish }, + { "nativeSyncPutLemmas", "(Ljava/lang/String;)I", + (void*) nativeSyncPutLemmas }, + { "nativeSyncGetLemmas", "()Ljava/lang/String;", + (void*) nativeSyncGetLemmas }, + { "nativeSyncGetLastCount", "()I", + (void*) nativeSyncGetLastCount }, + { "nativeSyncGetTotalCount", "()I", + (void*) nativeSyncGetTotalCount }, + { "nativeSyncClearLastGot", "()Z", + (void*) nativeSyncClearLastGot }, + { "nativeSyncGetCapacity", "()I", + (void*) nativeSyncGetCapacity }, + /* <<----Functions for sync end--------------------------------- */ +}; + + +/* + * Register several native methods for one class. + */ +static int registerNativeMethods(JNIEnv* env, const char* className, + JNINativeMethod* gMethods, int numMethods) +{ + jclass clazz; + + clazz = (*env).FindClass(className); + if (clazz == NULL) { + return JNI_FALSE; + } + if ((*env).RegisterNatives(clazz, gMethods, numMethods) < 0) { + return JNI_FALSE; + } + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find Java class java.io.FileDescriptor"); + gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + return JNI_TRUE; +} + +/* + * Register native methods for all classes we know about. + */ +static int registerNatives(JNIEnv* env) +{ + if (!registerNativeMethods(env, + "com/android/inputmethod/pinyin/PinyinDecoderService", + gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) + return JNI_FALSE; + + return JNI_TRUE; +} + +/* + * Set some test stuff up. + * + * Returns the JNI version on success, -1 on failure. + */ +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + jint result = -1; + + if ((*vm).GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + goto bail; + } + assert(env != NULL); + + if (!registerNatives(env)) { + goto bail; + } + + /* success -- return valid version number */ + result = JNI_VERSION_1_4; + +bail: + return result; +} + +#ifdef __cplusplus +} +#endif diff --git a/jni/command/Makefile b/jni/command/Makefile new file mode 100644 index 0000000..0722932 --- /dev/null +++ b/jni/command/Makefile @@ -0,0 +1,33 @@ +CC=gcc +CFLAGS= -g -Wall -std=c99 +CPP=g++ +CPPFLAGS= -g3 -Wall -lpthread + +PINYINIME_DICTBUILDER=pinyinime_dictbuilder + +LIBRARY_SRC= \ + ../share/dictbuilder.cpp \ + ../share/dictlist.cpp \ + ../share/dicttrie.cpp \ + ../share/lpicache.cpp \ + ../share/mystdlib.cpp \ + ../share/ngram.cpp \ + ../share/searchutility.cpp \ + ../share/spellingtable.cpp \ + ../share/spellingtrie.cpp \ + ../share/splparser.cpp \ + ../share/utf16char.cpp \ + ../share/utf16reader.cpp \ + +all: engine + +engine: $(PINYINIME_DICTBUILDER) + +$(PINYINIME_DICTBUILDER): $(LIBRARY_SRC) pinyinime_dictbuilder.cpp + @$(CPP) $(CPPFLAGS) -o $@ $? + + +clean: + -rm -rf $(PINYINIME_DICTBUILDER) + +.PHONY: clean diff --git a/jni/command/pinyinime_dictbuilder.cpp b/jni/command/pinyinime_dictbuilder.cpp new file mode 100644 index 0000000..b7f9606 --- /dev/null +++ b/jni/command/pinyinime_dictbuilder.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "../include/dicttrie.h" + +using namespace ime_pinyin; + +/** + * Build binary dictionary model. Make sure that ___BUILD_MODEL___ is defined + * in dictdef.h. + */ +int main(int argc, char* argv[]) { + DictTrie* dict_trie = new DictTrie(); + bool success; + if (argc >= 3) + success = dict_trie->build_dict(argv[1], argv[2]); + else + success = dict_trie->build_dict("../data/rawdict_utf16_65105_freq.txt", + "../data/valid_utf16.txt"); + + if (success) { + printf("Build dictionary successfully.\n"); + } else { + printf("Build dictionary unsuccessfully.\n"); + return -1; + } + + success = dict_trie->save_dict("../../res/raw/dict_pinyin.dat"); + + if (success) { + printf("Save dictionary successfully.\n"); + } else { + printf("Save dictionary unsuccessfully.\n"); + return -1; + } + + return 0; +} diff --git a/jni/data/rawdict_utf16_65105_freq.txt b/jni/data/rawdict_utf16_65105_freq.txt new file mode 100644 index 0000000..28805ba Binary files /dev/null and b/jni/data/rawdict_utf16_65105_freq.txt differ diff --git a/jni/data/valid_utf16.txt b/jni/data/valid_utf16.txt new file mode 100644 index 0000000..fecc67e Binary files /dev/null and b/jni/data/valid_utf16.txt differ diff --git a/jni/include/atomdictbase.h b/jni/include/atomdictbase.h new file mode 100644 index 0000000..0a70a51 --- /dev/null +++ b/jni/include/atomdictbase.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This class defines AtomDictBase class which is the base class for all atom + * dictionaries. Atom dictionaries are managed by the decoder class + * MatrixSearch. + * + * When the user appends a new character to the Pinyin string, all enabled atom + * dictionaries' extend_dict() will be called at least once to get candidates + * ended in this step (the information of starting step is also given in the + * parameter). Usually, when extend_dict() is called, a MileStoneHandle object + * returned by a previous calling for a earlier step is given to speed up the + * look-up process, and a new MileStoneHandle object will be returned if + * the extension is successful. + * + * A returned MileStoneHandle object should keep alive until Function + * reset_milestones() is called and this object is noticed to be reset. + * + * Usually, the atom dictionary can use step information to manage its + * MileStoneHandle objects, or it can make the objects in ascendant order to + * make the reset easier. + * + * When the decoder loads the dictionary, it will give a starting lemma id for + * this atom dictionary to map a inner id to a global id. Global ids should be + * used when an atom dictionary talks to any component outside. + */ +#ifndef PINYINIME_INCLUDE_ATOMDICTBASE_H__ +#define PINYINIME_INCLUDE_ATOMDICTBASE_H__ + +#include +#include "./dictdef.h" +#include "./searchutility.h" + +namespace ime_pinyin { +class AtomDictBase { + public: + virtual ~AtomDictBase() {} + + /** + * Load an atom dictionary from a file. + * + * @param file_name The file name to load dictionary. + * @param start_id The starting id used for this atom dictionary. + * @param end_id The end id (included) which can be used for this atom + * dictionary. User dictionary will always use the last id space, so it can + * ignore this paramter. All other atom dictionaries should check this + * parameter. + * @return True if succeed. + */ + virtual bool load_dict(const char *file_name, LemmaIdType start_id, + LemmaIdType end_id) = 0; + + /** + * Close this atom dictionary. + * + * @return True if succeed. + */ + virtual bool close_dict() = 0; + + /** + * Get the total number of lemmas in this atom dictionary. + * + * @return The total number of lemmas. + */ + virtual size_t number_of_lemmas() = 0; + + /** + * This function is called by the decoder when user deletes a character from + * the input string, or begins a new input string. + * + * Different atom dictionaries may implement this function in different way. + * an atom dictionary can use one of these two parameters (or both) to reset + * its corresponding MileStoneHandle objects according its detailed + * implementation. + * + * For example, if an atom dictionary uses step information to manage its + * MileStoneHandle objects, parameter from_step can be used to identify which + * objects should be reset; otherwise, if another atom dictionary does not + * use the detailed step information, it only uses ascendant handles + * (according to step. For the same step, earlier call, smaller handle), it + * can easily reset those MileStoneHandle which are larger than from_handle. + * + * The decoder always reset the decoding state by step. So when it begins + * resetting, it will call reset_milestones() of its atom dictionaries with + * the step information, and the MileStoneHandle objects returned by the + * earliest calling of extend_dict() for that step. + * + * If an atom dictionary does not implement incremental search, this function + * can be totally ignored. + * + * @param from_step From which step(included) the MileStoneHandle + * objects should be reset. + * @param from_handle The ealiest MileStoneHandle object for step from_step + */ + virtual void reset_milestones(uint16 from_step, + MileStoneHandle from_handle) = 0; + + /** + * Used to extend in this dictionary. The handle returned should keep valid + * until reset_milestones() is called. + * + * @param from_handle Its previous returned extended handle without the new + * spelling id, it can be used to speed up the extending. + * @param dep The paramter used for extending. + * @param lpi_items Used to fill in the lemmas matched. + * @param lpi_max The length of the buffer + * @param lpi_num Used to return the newly added items. + * @return The new mile stone for this extending. 0 if fail. + */ + virtual MileStoneHandle extend_dict(MileStoneHandle from_handle, + const DictExtPara *dep, + LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num) = 0; + + /** + * Get lemma items with scores according to a spelling id stream. + * This atom dictionary does not need to sort the returned items. + * + * @param splid_str The spelling id stream buffer. + * @param splid_str_len The length of the spelling id stream buffer. + * @param lpi_items Used to return matched lemma items with scores. + * @param lpi_max The maximum size of the buffer to return result. + * @return The number of matched items which have been filled in to lpi_items. + */ + virtual size_t get_lpis(const uint16 *splid_str, uint16 splid_str_len, + LmaPsbItem *lpi_items, size_t lpi_max) = 0; + + /** + * Get a lemma string (The Chinese string) by the given lemma id. + * + * @param id_lemma The lemma id to get the string. + * @param str_buf The buffer to return the Chinese string. + * @param str_max The maximum size of the buffer. + * @return The length of the string, 0 if fail. + */ + virtual uint16 get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, + uint16 str_max) = 0; + + /** + * Get the full spelling ids for the given lemma id. + * If the given buffer is too short, return 0. + * + * @param splids Used to return the spelling ids. + * @param splids_max The maximum buffer length of splids. + * @param arg_valid Used to indicate if the incoming parameters have been + * initialized are valid. If it is true, the splids and splids_max are valid + * and there may be half ids in splids to be updated to full ids. In this + * case, splids_max is the number of valid ids in splids. + * @return The number of ids in the buffer. + */ + virtual uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, + uint16 splids_max, bool arg_valid) = 0; + + /** + * Function used for prediction. + * No need to sort the newly added items. + * + * @param last_hzs The last n Chinese chracters(called Hanzi), its length + * should be less than or equal to kMaxPredictSize. + * @param hzs_len specifies the length(<= kMaxPredictSize) of the history. + * @param npre_items Used used to return the result. + * @param npre_max The length of the buffer to return result + * @param b4_used Number of prediction result (from npre_items[-b4_used]) + * from other atom dictionaries. A atom ditionary can just ignore it. + * @return The number of prediction result from this atom dictionary. + */ + virtual size_t predict(const char16 last_hzs[], uint16 hzs_len, + NPredictItem *npre_items, size_t npre_max, + size_t b4_used) = 0; + + /** + * Add a lemma to the dictionary. If the dictionary allows to add new + * items and this item does not exist, add it. + * + * @param lemma_str The Chinese string of the lemma. + * @param splids The spelling ids of the lemma. + * @param lemma_len The length of the Chinese lemma. + * @param count The frequency count for this lemma. + */ + virtual LemmaIdType put_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count) = 0; + + /** + * Update a lemma's occuring count. + * + * @param lemma_id The lemma id to update. + * @param delta_count The frequnecy count to ajust. + * @param selected Indicate whether this lemma is selected by user and + * submitted to target edit box. + * @return The id if succeed, 0 if fail. + */ + virtual LemmaIdType update_lemma(LemmaIdType lemma_id, int16 delta_count, + bool selected) = 0; + + /** + * Get the lemma id for the given lemma. + * + * @param lemma_str The Chinese string of the lemma. + * @param splids The spelling ids of the lemma. + * @param lemma_len The length of the lemma. + * @return The matched lemma id, or 0 if fail. + */ + virtual LemmaIdType get_lemma_id(char16 lemma_str[], uint16 splids[], + uint16 lemma_len) = 0; + + /** + * Get the lemma score. + * + * @param lemma_id The lemma id to get score. + * @return The score of the lemma, or 0 if fail. + */ + virtual LmaScoreType get_lemma_score(LemmaIdType lemma_id) = 0; + + /** + * Get the lemma score. + * + * @param lemma_str The Chinese string of the lemma. + * @param splids The spelling ids of the lemma. + * @param lemma_len The length of the lemma. + * @return The score of the lamm, or 0 if fail. + */ + virtual LmaScoreType get_lemma_score(char16 lemma_str[], uint16 splids[], + uint16 lemma_len) = 0; + + /** + * If the dictionary allowed, remove a lemma from it. + * + * @param lemma_id The id of the lemma to remove. + * @return True if succeed. + */ + virtual bool remove_lemma(LemmaIdType lemma_id) = 0; + + /** + * Get the total occuring count of this atom dictionary. + * + * @return The total occuring count of this atom dictionary. + */ + virtual size_t get_total_lemma_count() = 0; + + /** + * Set the total occuring count of other atom dictionaries. + * + * @param count The total occuring count of other atom dictionaies. + */ + virtual void set_total_lemma_count_of_others(size_t count) = 0; + + /** + * Notify this atom dictionary to flush the cached data to persistent storage + * if necessary. + */ + virtual void flush_cache() = 0; +}; +} + +#endif // PINYINIME_INCLUDE_ATOMDICTBASE_H__ diff --git a/jni/include/dictbuilder.h b/jni/include/dictbuilder.h new file mode 100644 index 0000000..da0d6cd --- /dev/null +++ b/jni/include/dictbuilder.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_DICTBUILDER_H__ +#define PINYINIME_INCLUDE_DICTBUILDER_H__ + +#include +#include "./utf16char.h" +#include "./dictdef.h" +#include "./dictlist.h" +#include "./spellingtable.h" +#include "./spellingtrie.h" +#include "./splparser.h" + +namespace ime_pinyin { + +#ifdef ___BUILD_MODEL___ + +#define ___DO_STATISTICS___ + +class DictTrie; + +class DictBuilder { + private: + // The raw lemma array buffer. + LemmaEntry *lemma_arr_; + size_t lemma_num_; + + // Used to store all possible single char items. + // Two items may have the same Hanzi while their spelling ids are different. + SingleCharItem *scis_; + size_t scis_num_; + + // In the tree, root's level is -1. + // Lemma nodes for root, and level 0 + LmaNodeLE0 *lma_nodes_le0_; + + // Lemma nodes for layers whose levels are deeper than 0 + LmaNodeGE1 *lma_nodes_ge1_; + + // Number of used lemma nodes + size_t lma_nds_used_num_le0_; + size_t lma_nds_used_num_ge1_; + + // Used to store homophonies' ids. + LemmaIdType *homo_idx_buf_; + // Number of homophonies each of which only contains one Chinese character. + size_t homo_idx_num_eq1_; + // Number of homophonies each of which contains more than one character. + size_t homo_idx_num_gt1_; + + // The items with highest scores. + LemmaEntry *top_lmas_; + size_t top_lmas_num_; + + SpellingTable *spl_table_; + SpellingParser *spl_parser_; + +#ifdef ___DO_STATISTICS___ + size_t max_sonbuf_len_[kMaxLemmaSize]; + size_t max_homobuf_len_[kMaxLemmaSize]; + + size_t total_son_num_[kMaxLemmaSize]; + size_t total_node_hasson_[kMaxLemmaSize]; + size_t total_sonbuf_num_[kMaxLemmaSize]; + size_t total_sonbuf_allnoson_[kMaxLemmaSize]; + size_t total_node_in_sonbuf_allnoson_[kMaxLemmaSize]; + size_t total_homo_num_[kMaxLemmaSize]; + + size_t sonbufs_num1_; // Number of son buffer with only 1 son + size_t sonbufs_numgt1_; // Number of son buffer with more 1 son; + + size_t total_lma_node_num_; + + void stat_init(); + void stat_print(); +#endif + + public: + + DictBuilder(); + ~DictBuilder(); + + // Build dictionary trie from the file fn_raw. File fn_validhzs provides + // valid chars. If fn_validhzs is NULL, only chars in GB2312 will be + // included. + bool build_dict(const char* fn_raw, const char* fn_validhzs, + DictTrie *dict_trie); + + private: + // Fill in the buffer with id. The caller guarantees that the paramters are + // vaild. + void id_to_charbuf(unsigned char *buf, LemmaIdType id); + + // Update the offset of sons for a node. + void set_son_offset(LmaNodeGE1 *node, size_t offset); + + // Update the offset of homophonies' ids for a node. + void set_homo_id_buf_offset(LmaNodeGE1 *node, size_t offset); + + // Format a speling string. + void format_spelling_str(char *spl_str); + + // Sort the lemma_arr by the hanzi string, and give each of unique items + // a id. Why we need to sort the lemma list according to their Hanzi string + // is to find items started by a given prefix string to do prediction. + // Actually, the single char items are be in other order, for example, + // in spelling id order, etc. + // Return value is next un-allocated idx available. + LemmaIdType sort_lemmas_by_hz(); + + // Build the SingleCharItem list, and fill the hanzi_scis_ids in the + // lemma buffer lemma_arr_. + // This function should be called after the lemma array is ready. + // Return the number of unique SingleCharItem elements. + size_t build_scis(); + + // Construct a subtree using a subset of the spelling array (from + // item_star to item_end) + // parent is the parent node to update the necessary information + // parent can be a member of LmaNodeLE0 or LmaNodeGE1 + bool construct_subset(void* parent, LemmaEntry* lemma_arr, + size_t item_start, size_t item_end, size_t level); + + + // Read valid Chinese Hanzis from the given file. + // num is used to return number of chars. + // The return buffer is sorted and caller needs to free the returned buffer. + char16* read_valid_hanzis(const char *fn_validhzs, size_t *num); + + + // Read a raw dictionary. max_item is the maximum number of items. If there + // are more items in the ditionary, only the first max_item will be read. + // Returned value is the number of items successfully read from the file. + size_t read_raw_dict(const char* fn_raw, const char *fn_validhzs, + size_t max_item); + + // Try to find if a character is in hzs buffer. + bool hz_in_hanzis_list(const char16 *hzs, size_t hzs_len, char16 hz); + + // Try to find if all characters in str are in hzs buffer. + bool str_in_hanzis_list(const char16 *hzs, size_t hzs_len, + const char16 *str, size_t str_len); + + // Get these lemmas with toppest scores. + void get_top_lemmas(); + + // Allocate resource to build dictionary. + // lma_num is the number of items to be loaded + bool alloc_resource(size_t lma_num); + + // Free resource. + void free_resource(); +}; +#endif // ___BUILD_MODEL___ +} + +#endif // PINYINIME_INCLUDE_DICTBUILDER_H__ diff --git a/jni/include/dictdef.h b/jni/include/dictdef.h new file mode 100644 index 0000000..3e79d98 --- /dev/null +++ b/jni/include/dictdef.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_DICTDEF_H__ +#define PINYINIME_INCLUDE_DICTDEF_H__ + +#include +#include "./utf16char.h" + +namespace ime_pinyin { + +// Enable the following line when building the binary dictionary model. +// #define ___BUILD_MODEL___ + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef long long int64; +typedef unsigned long long uint64; + +const bool kPrintDebug0 = false; +const bool kPrintDebug1 = false; +const bool kPrintDebug2 = false; + +// The max length of a lemma. +const size_t kMaxLemmaSize = 8; + +// The max length of a Pinyin (spelling). +const size_t kMaxPinyinSize = 6; + +// The number of half spelling ids. For Chinese Pinyin, there 30 half ids. +// See SpellingTrie.h for details. +const size_t kHalfSpellingIdNum = 29; + +// The maximum number of full spellings. For Chinese Pinyin, there are only +// about 410 spellings. +// If change this value is bigger(needs more bits), please also update +// other structures like SpellingNode, to make sure than a spelling id can be +// stored. +// -1 is because that 0 is never used. +const size_t kMaxSpellingNum = 512 - kHalfSpellingIdNum - 1; +const size_t kMaxSearchSteps = 40; + +// One character predicts its following characters. +const size_t kMaxPredictSize = (kMaxLemmaSize - 1); + +// LemmaIdType must always be size_t. +typedef size_t LemmaIdType; +const size_t kLemmaIdSize = 3; // Actually, a Id occupies 3 bytes in storage. +const size_t kLemmaIdComposing = 0xffffff; + +typedef uint16 LmaScoreType; +typedef uint16 KeyScoreType; + +// Number of items with highest score are kept for prediction purpose. +const size_t kTopScoreLemmaNum = 10; + +const size_t kMaxPredictNumByGt3 = 1; +const size_t kMaxPredictNumBy3 = 2; +const size_t kMaxPredictNumBy2 = 2; + +// The last lemma id (included) for the system dictionary. The system +// dictionary's ids always start from 1. +const LemmaIdType kSysDictIdEnd = 500000; + +// The first lemma id for the user dictionary. +const LemmaIdType kUserDictIdStart = 500001; + +// The last lemma id (included) for the user dictionary. +const LemmaIdType kUserDictIdEnd = 600000; + +typedef struct { + uint16 half_splid:5; + uint16 full_splid:11; +} SpellingId, *PSpellingId; + + +/** + * We use different node types for different layers + * Statistical data of the building result for a testing dictionary: + * root, level 0, level 1, level 2, level 3 + * max son num of one node: 406 280 41 2 - + * max homo num of one node: 0 90 23 2 2 + * total node num of a layer: 1 406 31766 13516 993 + * total homo num of a layer: 9 5674 44609 12667 995 + * + * The node number for root and level 0 won't be larger than 500 + * According to the information above, two kinds of nodes can be used; one for + * root and level 0, the other for these layers deeper than 0. + * + * LE = less and equal, + * A node occupies 16 bytes. so, totallly less than 16 * 500 = 8K + */ +struct LmaNodeLE0 { + size_t son_1st_off; + size_t homo_idx_buf_off; + uint16 spl_idx; + uint16 num_of_son; + uint16 num_of_homo; +}; + +/** + * GE = great and equal + * A node occupies 8 bytes. + */ +struct LmaNodeGE1 { + uint16 son_1st_off_l; // Low bits of the son_1st_off + uint16 homo_idx_buf_off_l; // Low bits of the homo_idx_buf_off_1 + uint16 spl_idx; + unsigned char num_of_son; // number of son nodes + unsigned char num_of_homo; // number of homo words + unsigned char son_1st_off_h; // high bits of the son_1st_off + unsigned char homo_idx_buf_off_h; // high bits of the homo_idx_buf_off +}; + +#ifdef ___BUILD_MODEL___ +struct SingleCharItem { + float freq; + char16 hz; + SpellingId splid; +}; + +struct LemmaEntry { + LemmaIdType idx_by_py; + LemmaIdType idx_by_hz; + char16 hanzi_str[kMaxLemmaSize + 1]; + + // The SingleCharItem id for each Hanzi. + uint16 hanzi_scis_ids[kMaxLemmaSize]; + + uint16 spl_idx_arr[kMaxLemmaSize + 1]; + char pinyin_str[kMaxLemmaSize][kMaxPinyinSize + 1]; + unsigned char hz_str_len; + float freq; +}; +#endif // ___BUILD_MODEL___ + +} // namespace ime_pinyin + +#endif // PINYINIME_INCLUDE_DICTDEF_H__ diff --git a/jni/include/dictlist.h b/jni/include/dictlist.h new file mode 100644 index 0000000..5fcc12f --- /dev/null +++ b/jni/include/dictlist.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_DICTLIST_H__ +#define PINYINIME_INCLUDE_DICTLIST_H__ + +#include +#include +#include "./dictdef.h" +#include "./searchutility.h" +#include "./spellingtrie.h" +#include "./utf16char.h" + +namespace ime_pinyin { + +class DictList { + private: + bool initialized_; + + const SpellingTrie *spl_trie_; + + // Number of SingCharItem. The first is blank, because id 0 is invalid. + size_t scis_num_; + char16 *scis_hz_; + SpellingId *scis_splid_; + + // The large memory block to store the word list. + char16 *buf_; + + // Starting position of those words whose lengths are i+1, counted in + // char16 + size_t start_pos_[kMaxLemmaSize + 1]; + + size_t start_id_[kMaxLemmaSize + 1]; + + int (*cmp_func_[kMaxLemmaSize])(const void *, const void *); + + bool alloc_resource(size_t buf_size, size_t scim_num); + + void free_resource(); + +#ifdef ___BUILD_MODEL___ + // Calculate the requsted memory, including the start_pos[] buffer. + size_t calculate_size(const LemmaEntry *lemma_arr, size_t lemma_num); + + void fill_scis(const SingleCharItem *scis, size_t scis_num); + + // Copy the related content to the inner buffer + // It should be called after calculate_size() + void fill_list(const LemmaEntry *lemma_arr, size_t lemma_num); + + // Find the starting position for the buffer of those 2-character Chinese word + // whose first character is the given Chinese character. + char16* find_pos2_startedbyhz(char16 hz_char); +#endif + + // Find the starting position for the buffer of those words whose lengths are + // word_len. The given parameter cmp_func decides how many characters from + // beginning will be used to compare. + char16* find_pos_startedbyhzs(const char16 last_hzs[], + size_t word_Len, + int (*cmp_func)(const void *, const void *)); + + public: + + DictList(); + ~DictList(); + + bool save_list(FILE *fp); + bool load_list(FILE *fp); + +#ifdef ___BUILD_MODEL___ + // Init the list from the LemmaEntry array. + // lemma_arr should have been sorted by the hanzi_str, and have been given + // ids from 1 + bool init_list(const SingleCharItem *scis, size_t scis_num, + const LemmaEntry *lemma_arr, size_t lemma_num); +#endif + + // Get the hanzi string for the given id + uint16 get_lemma_str(LemmaIdType id_hz, char16 *str_buf, uint16 str_max); + + void convert_to_hanzis(char16 *str, uint16 str_len); + + void convert_to_scis_ids(char16 *str, uint16 str_len); + + // last_hzs stores the last n Chinese characters history, its length should be + // less or equal than kMaxPredictSize. + // hzs_len specifies the length(<= kMaxPredictSize). + // predict_buf is used to store the result. + // buf_len specifies the buffer length. + // b4_used specifies how many items before predict_buf have been used. + // Returned value is the number of newly added items. + size_t predict(const char16 last_hzs[], uint16 hzs_len, + NPredictItem *npre_items, size_t npre_max, + size_t b4_used); + + // If half_splid is a valid half spelling id, return those full spelling + // ids which share this half id. + uint16 get_splids_for_hanzi(char16 hanzi, uint16 half_splid, + uint16 *splids, uint16 max_splids); + + LemmaIdType get_lemma_id(const char16 *str, uint16 str_len); +}; +} + +#endif // PINYINIME_INCLUDE_DICTLIST_H__ diff --git a/jni/include/dicttrie.h b/jni/include/dicttrie.h new file mode 100644 index 0000000..268624f --- /dev/null +++ b/jni/include/dicttrie.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_DICTTRIE_H__ +#define PINYINIME_INCLUDE_DICTTRIE_H__ + +#include +#include "./atomdictbase.h" +#include "./dictdef.h" +#include "./dictlist.h" +#include "./searchutility.h" + +namespace ime_pinyin { + +class DictTrie : AtomDictBase { + private: + typedef struct ParsingMark { + size_t node_offset:24; + size_t node_num:8; // Number of nodes with this spelling id given + // by spl_id. If spl_id is a Shengmu, for nodes + // in the first layer of DictTrie, it equals to + // SpellingTrie::shm2full_num(); but for those + // nodes which are not in the first layer, + // node_num < SpellingTrie::shm2full_num(). + // For a full spelling id, node_num = 1; + }; + + // Used to indicate an extended mile stone. + // An extended mile stone is used to mark a partial match in the dictionary + // trie to speed up further potential extending. + // For example, when the user inputs "w", a mile stone is created to mark the + // partial match status, so that when user inputs another char 'm', it will be + // faster to extend search space based on this mile stone. + // + // For partial match status of "wm", there can be more than one sub mile + // stone, for example, "wm" can be matched to "wanm", "wom", ..., etc, so + // there may be more one parsing mark used to mark these partial matchings. + // A mile stone records the starting position in the mark list and number of + // marks. + struct MileStone { + uint16 mark_start; + uint16 mark_num; + }; + + DictList* dict_list_; + + const SpellingTrie *spl_trie_; + + LmaNodeLE0* root_; // Nodes for root and the first layer. + LmaNodeGE1* nodes_ge1_; // Nodes for other layers. + + // An quick index from spelling id to the LmaNodeLE0 node buffer, or + // to the root_ buffer. + // Index length: + // SpellingTrie::get_instance().get_spelling_num() + 1. The last one is used + // to get the end. + // All Shengmu ids are not indexed because they will be converted into + // corresponding full ids. + // So, given an id splid, the son is: + // root_[splid_le0_index_[splid - kFullSplIdStart]] + uint16 *splid_le0_index_; + + size_t lma_node_num_le0_; + size_t lma_node_num_ge1_; + + // The first part is for homophnies, and the last top_lma_num_ items are + // lemmas with highest scores. + unsigned char *lma_idx_buf_; + size_t lma_idx_buf_len_; // The total size of lma_idx_buf_ in byte. + size_t total_lma_num_; // Total number of lemmas in this dictionary. + size_t top_lmas_num_; // Number of lemma with highest scores. + + // Parsing mark list used to mark the detailed extended statuses. + ParsingMark *parsing_marks_; + // The position for next available mark. + uint16 parsing_marks_pos_; + + // Mile stone list used to mark the extended status. + MileStone *mile_stones_; + // The position for the next available mile stone. We use positions (except 0) + // as handles. + MileStoneHandle mile_stones_pos_; + + // Get the offset of sons for a node. + inline size_t get_son_offset(const LmaNodeGE1 *node); + + // Get the offset of homonious ids for a node. + inline size_t get_homo_idx_buf_offset(const LmaNodeGE1 *node); + + // Get the lemma id by the offset. + inline LemmaIdType get_lemma_id(size_t id_offset); + + void free_resource(bool free_dict_list); + + bool load_dict(FILE *fp); + + // Given a LmaNodeLE0 node, extract the lemmas specified by it, and fill + // them into the lpi_items buffer. + // This function is called by the search engine. + size_t fill_lpi_buffer(LmaPsbItem lpi_items[], size_t max_size, + LmaNodeLE0 *node); + + // Given a LmaNodeGE1 node, extract the lemmas specified by it, and fill + // them into the lpi_items buffer. + // This function is called by inner functions extend_dict0(), extend_dict1() + // and extend_dict2(). + size_t fill_lpi_buffer(LmaPsbItem lpi_items[], size_t max_size, + size_t homo_buf_off, LmaNodeGE1 *node, + uint16 lma_len); + + // Extend in the trie from level 0. + MileStoneHandle extend_dict0(MileStoneHandle from_handle, + const DictExtPara *dep, LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num); + + // Extend in the trie from level 1. + MileStoneHandle extend_dict1(MileStoneHandle from_handle, + const DictExtPara *dep, LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num); + + // Extend in the trie from level 2. + MileStoneHandle extend_dict2(MileStoneHandle from_handle, + const DictExtPara *dep, LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num); + + // Try to extend the given spelling id buffer, and if the given id_lemma can + // be successfully gotten, return true; + // The given spelling ids are all valid full ids. + bool try_extend(const uint16 *splids, uint16 splid_num, LemmaIdType id_lemma); + +#ifdef ___BUILD_MODEL___ + bool save_dict(FILE *fp); +#endif // ___BUILD_MODEL___ + + static const int kMaxMileStone = 100; + static const int kMaxParsingMark = 600; + static const MileStoneHandle kFirstValidMileStoneHandle = 1; + + friend class DictParser; + friend class DictBuilder; + + public: + + DictTrie(); + ~DictTrie(); + +#ifdef ___BUILD_MODEL___ + // Construct the tree from the file fn_raw. + // fn_validhzs provide the valid hanzi list. If fn_validhzs is + // NULL, only chars in GB2312 will be included. + bool build_dict(const char *fn_raw, const char *fn_validhzs); + + // Save the binary dictionary + // Actually, the SpellingTrie/DictList instance will be also saved. + bool save_dict(const char *filename); +#endif // ___BUILD_MODEL___ + + void convert_to_hanzis(char16 *str, uint16 str_len); + + void convert_to_scis_ids(char16 *str, uint16 str_len); + + // Load a binary dictionary + // The SpellingTrie instance/DictList will be also loaded + bool load_dict(const char *filename, LemmaIdType start_id, + LemmaIdType end_id); + bool load_dict_fd(int sys_fd, long start_offset, long length, + LemmaIdType start_id, LemmaIdType end_id); + bool close_dict() {return true;} + size_t number_of_lemmas() {return 0;} + + void reset_milestones(uint16 from_step, MileStoneHandle from_handle); + + MileStoneHandle extend_dict(MileStoneHandle from_handle, + const DictExtPara *dep, + LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num); + + size_t get_lpis(const uint16 *splid_str, uint16 splid_str_len, + LmaPsbItem *lpi_items, size_t lpi_max); + + uint16 get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, uint16 str_max); + + uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, + uint16 splids_max, bool arg_valid); + + size_t predict(const char16 *last_hzs, uint16 hzs_len, + NPredictItem *npre_items, size_t npre_max, + size_t b4_used); + + LemmaIdType put_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count) {return 0;} + + LemmaIdType update_lemma(LemmaIdType lemma_id, int16 delta_count, + bool selected) {return 0;} + + LemmaIdType get_lemma_id(char16 lemma_str[], uint16 splids[], + uint16 lemma_len) {return 0;} + + LmaScoreType get_lemma_score(LemmaIdType lemma_id) {return 0;} + + LmaScoreType get_lemma_score(char16 lemma_str[], uint16 splids[], + uint16 lemma_len) {return 0;} + + bool remove_lemma(LemmaIdType lemma_id) {return false;} + + size_t get_total_lemma_count() {return 0;} + void set_total_lemma_count_of_others(size_t count); + + void flush_cache() {} + + LemmaIdType get_lemma_id(const char16 lemma_str[], uint16 lemma_len); + + // Fill the lemmas with highest scores to the prediction buffer. + // his_len is the history length to fill in the prediction buffer. + size_t predict_top_lmas(size_t his_len, NPredictItem *npre_items, + size_t npre_max, size_t b4_used); +}; +} + +#endif // PINYINIME_INCLUDE_DICTTRIE_H__ diff --git a/jni/include/lpicache.h b/jni/include/lpicache.h new file mode 100644 index 0000000..6073597 --- /dev/null +++ b/jni/include/lpicache.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_ANDPY_INCLUDE_LPICACHE_H__ +#define PINYINIME_ANDPY_INCLUDE_LPICACHE_H__ + +#include +#include "./searchutility.h" +#include "./spellingtrie.h" + +namespace ime_pinyin { + +// Used to cache LmaPsbItem list for half spelling ids. +class LpiCache { + private: + static LpiCache *instance_; + static const int kMaxLpiCachePerId = 15; + + LmaPsbItem *lpi_cache_; + uint16 *lpi_cache_len_; + + public: + LpiCache(); + ~LpiCache(); + + static LpiCache& get_instance(); + + // Test if the LPI list of the given splid has been cached. + // If splid is a full spelling id, it returns false, because we only cache + // list for half ids. + bool is_cached(uint16 splid); + + // Put LPI list to cahce. If the length of the list, lpi_num, is longer than + // the cache buffer. the list will be truncated, and function returns the + // maximum length of the cache buffer. + // Note: splid must be a half id, and lpi_items must be not NULL. The + // caller of this function should guarantee this. + size_t put_cache(uint16 splid, LmaPsbItem lpi_items[], size_t lpi_num); + + // Get the cached list for the given half id. + // Return the length of the cached buffer. + // Note: splid must be a half id, and lpi_items must be not NULL. The + // caller of this function should guarantee this. + size_t get_cache(uint16 splid, LmaPsbItem lpi_items[], size_t lpi_max); +}; + +} // namespace + +#endif // PINYINIME_ANDPY_INCLUDE_LPICACHE_H__ diff --git a/jni/include/matrixsearch.h b/jni/include/matrixsearch.h new file mode 100644 index 0000000..f581d30 --- /dev/null +++ b/jni/include/matrixsearch.h @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_ANDPY_INCLUDE_MATRIXSEARCH_H__ +#define PINYINIME_ANDPY_INCLUDE_MATRIXSEARCH_H__ + +#include +#include "./atomdictbase.h" +#include "./dicttrie.h" +#include "./searchutility.h" +#include "./spellingtrie.h" +#include "./splparser.h" + +namespace ime_pinyin { + +static const size_t kMaxRowNum = kMaxSearchSteps; + +typedef struct { + // MileStoneHandle objects for the system and user dictionaries. + MileStoneHandle dict_handles[2]; + // From which DMI node. -1 means it's from root. + PoolPosType dmi_fr; + // The spelling id for the Pinyin string from the previous DMI to this node. + // If it is a half id like Shengmu, the node pointed by dict_node is the first + // node with this Shengmu, + uint16 spl_id; + // What's the level of the dict node. Level of root is 0, but root is never + // recorded by dict_node. + unsigned char dict_level:7; + // If this node is for composing phrase, this bit is 1. + unsigned char c_phrase:1; + // Whether the spl_id is parsed with a split character at the end. + unsigned char splid_end_split:1; + // What's the length of the spelling string for this match, for the whole + // word. + unsigned char splstr_len:7; + // Used to indicate whether all spelling ids from the root are full spelling + // ids. This information is useful for keymapping mode(not finished). Because + // in this mode, there is no clear boundaries, we prefer those results which + // have full spelling ids. + unsigned char all_full_id:1; +} DictMatchInfo, *PDictMatchInfo; + +typedef struct MatrixNode { + LemmaIdType id; + float score; + MatrixNode *from; + // From which DMI node. Used to trace the spelling segmentation. + PoolPosType dmi_fr; + uint16 step; +} MatrixNode, *PMatrixNode; + +typedef struct { + // The MatrixNode position in the matrix pool + PoolPosType mtrx_nd_pos; + // The DictMatchInfo position in the DictMatchInfo pool. + PoolPosType dmi_pos; + uint16 mtrx_nd_num; + uint16 dmi_num:15; + // Used to indicate whether there are dmi nodes in this step with full + // spelling id. This information is used to decide whether a substring of a + // valid Pinyin should be extended. + // + // Example1: shoudao + // When the last char 'o' is added, the parser will find "dao" is a valid + // Pinyin, and because all dmi nodes at location 'd' (including those for + // "shoud", and those for "d") have Shengmu id only, so it is not necessary + // to extend "ao", otherwise the result may be "shoud ao", that is not + // reasonable. + // + // Example2: hengao + // When the last 'o' is added, the parser finds "gao" is a valid Pinyin. + // Because some dmi nodes at 'g' has Shengmu ids (hen'g and g), but some dmi + // nodes at 'g' has full ids ('heng'), so it is necessary to extend "ao", thus + // "heng ao" can also be the result. + // + // Similarly, "ganga" is expanded to "gang a". + // + // For Pinyin string "xian", because "xian" is a valid Pinyin, because all dmi + // nodes at 'x' only have Shengmu ids, the parser will not try "x ian" (and it + // is not valid either). If the parser uses break in the loop, the result + // always be "xian"; but if the parser uses continue in the loop, "xi an" will + // also be tried. This behaviour can be set via the function + // set_xi_an_switch(). + uint16 dmi_has_full_id:1; + // Points to a MatrixNode of the current step to indicate which choice the + // user selects. + MatrixNode *mtrx_nd_fixed; +} MatrixRow, *PMatrixRow; + +// When user inputs and selects candidates, the fixed lemma ids are stored in +// lma_id_ of class MatrixSearch, and fixed_lmas_ is used to indicate how many +// lemmas from the beginning are fixed. If user deletes Pinyin characters one +// by one from the end, these fixed lemmas can be unlocked one by one when +// necessary. Whenever user deletes a Chinese character and its spelling string +// in these fixed lemmas, all fixed lemmas will be merged together into a unit +// named ComposingPhrase with a lemma id kLemmaIdComposing, and this composing +// phrase will be the first lemma in the sentence. Because it contains some +// modified lemmas (by deleting a character), these merged lemmas are called +// sub lemmas (sublma), and each of them are represented individually, so that +// when user deletes Pinyin characters from the end, these sub lemmas can also +// be unlocked one by one. +typedef struct { + uint16 spl_ids[kMaxRowNum]; + uint16 spl_start[kMaxRowNum]; + char16 chn_str[kMaxRowNum]; // Chinese string. + uint16 sublma_start[kMaxRowNum]; // Counted in Chinese characters. + size_t sublma_num; + uint16 length; // Counted in Chinese characters. +} ComposingPhrase, *TComposingPhrase; + +class MatrixSearch { + private: + // If it is true, prediction list by string whose length is greater than 1 + // will be limited to a reasonable number. + static const bool kPredictLimitGt1 = false; + + // If it is true, the engine will prefer long history based prediction, + // for example, when user inputs "BeiJing", we prefer "DaXue", etc., which are + // based on the two-character history. + static const bool kPreferLongHistoryPredict = true; + + // If it is true, prediction will only be based on user dictionary. this flag + // is for debug purpose. + static const bool kOnlyUserDictPredict = false; + + // The maximum buffer to store LmaPsbItems. + static const size_t kMaxLmaPsbItems = 1450; + + // How many rows for each step. + static const size_t kMaxNodeARow = 5; + + // The maximum length of the sentence candidates counted in chinese + // characters + static const size_t kMaxSentenceLength = 16; + + // The size of the matrix node pool. + static const size_t kMtrxNdPoolSize = 200; + + // The size of the DMI node pool. + static const size_t kDmiPoolSize = 800; + + // Used to indicate whether this object has been initialized. + bool inited_; + + // Spelling trie. + const SpellingTrie *spl_trie_; + + // Used to indicate this switcher status: when "xian" is parseed, should + // "xi an" also be extended. Default is false. + // These cases include: xia, xian, xiang, zhuan, jiang..., etc. The string + // should be valid for a FULL spelling, or a combination of two spellings, + // first of which is a FULL id too. So even it is true, "da" will never be + // split into "d a", because "d" is not a full spelling id. + bool xi_an_enabled_; + + // System dictionary. + DictTrie* dict_trie_; + + // User dictionary. + AtomDictBase* user_dict_; + + // Spelling parser. + SpellingParser* spl_parser_; + + // The maximum allowed length of spelling string (such as a Pinyin string). + size_t max_sps_len_; + + // The maximum allowed length of a result Chinese string. + size_t max_hzs_len_; + + // Pinyin string. Max length: kMaxRowNum - 1 + char pys_[kMaxRowNum]; + + // The length of the string that has been decoded successfully. + size_t pys_decoded_len_; + + // Shared buffer for multiple purposes. + size_t *share_buf_; + + MatrixNode *mtrx_nd_pool_; + PoolPosType mtrx_nd_pool_used_; // How many nodes used in the pool + DictMatchInfo *dmi_pool_; + PoolPosType dmi_pool_used_; // How many items used in the pool + + MatrixRow *matrix_; // The first row is for starting + + DictExtPara *dep_; // Parameter used to extend DMI nodes. + + NPredictItem *npre_items_; // Used to do prediction + size_t npre_items_len_; + + // The starting positions and lemma ids for the full sentence candidate. + size_t lma_id_num_; + uint16 lma_start_[kMaxRowNum]; // Counted in spelling ids. + LemmaIdType lma_id_[kMaxRowNum]; + size_t fixed_lmas_; + + // If fixed_lmas_ is bigger than i, Element i is used to indicate whether + // the i'th lemma id in lma_id_ is the first candidate for that step. + // If all candidates are the first one for that step, the whole string can be + // decoded by the engine automatically, so no need to add it to user + // dictionary. (We are considering to add it to user dictionary in the + // future). + uint8 fixed_lmas_no1_[kMaxRowNum]; + + // Composing phrase + ComposingPhrase c_phrase_; + + // If dmi_c_phrase_ is true, the decoder will try to match the + // composing phrase (And definitely it will match successfully). If it + // is false, the decoder will try to match lemmas items in dictionaries. + bool dmi_c_phrase_; + + // The starting positions and spelling ids for the first full sentence + // candidate. + size_t spl_id_num_; // Number of splling ids + uint16 spl_start_[kMaxRowNum]; // Starting positions + uint16 spl_id_[kMaxRowNum]; // Spelling ids + // Used to remember the last fixed position, counted in Hanzi. + size_t fixed_hzs_; + + // Lemma Items with possibility score, two purposes: + // 1. In Viterbi decoding, this buffer is used to get all possible candidates + // for current step; + // 2. When the search is done, this buffer is used to get candiates from the + // first un-fixed step and show them to the user. + LmaPsbItem lpi_items_[kMaxLmaPsbItems]; + size_t lpi_total_; + + // Assign the pointers with NULL. The caller makes sure that all pointers are + // not valid before calling it. This function only will be called in the + // construction function and free_resource(). + void reset_pointers_to_null(); + + bool alloc_resource(); + + void free_resource(); + + // Reset the search space totally. + bool reset_search0(); + + // Reset the search space from ch_pos step. For example, if the original + // input Pinyin is "an", reset_search(1) will reset the search space to the + // result of "a". If the given position is out of range, return false. + // if clear_fixed_this_step is true, and the ch_pos step is a fixed step, + // clear its fixed status. if clear_dmi_his_step is true, clear the DMI nodes. + // If clear_mtrx_this_sTep is true, clear the mtrx nodes of this step. + // The DMI nodes will be kept. + // + // Note: this function should not destroy content of pys_. + bool reset_search(size_t ch_pos, bool clear_fixed_this_step, + bool clear_dmi_this_step, bool clear_mtrx_this_step); + + // Delete a part of the content in pys_. + void del_in_pys(size_t start, size_t len); + + // Delete a spelling id and its corresponding Chinese character, and merge + // the fixed lemmas into the composing phrase. + // del_spl_pos indicates which spelling id needs to be delete. + // This function will update the lemma and spelling segmentation information. + // The caller guarantees that fixed_lmas_ > 0 and del_spl_pos is within + // the fixed lemmas. + void merge_fixed_lmas(size_t del_spl_pos); + + // Get spelling start posistions and ids. The result will be stored in + // spl_id_num_, spl_start_[], spl_id_[]. + // fixed_hzs_ will be also assigned. + void get_spl_start_id(); + + // Get all lemma ids with match the given spelling id stream(shorter than the + // maximum length of a word). + // If pfullsent is not NULL, means the full sentence candidate may be the + // same with the coming lemma string, if so, remove that lemma. + // The result is sorted in descendant order by the frequency score. + size_t get_lpis(const uint16* splid_str, size_t splid_str_len, + LmaPsbItem* lma_buf, size_t max_lma_buf, + const char16 *pfullsent, bool sort_by_psb); + + uint16 get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, uint16 str_max); + + uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, + uint16 splids_max, bool arg_valid); + + + // Extend a DMI node with a spelling id. ext_len is the length of the rows + // to extend, actually, it is the size of the spelling string of splid. + // return value can be 1 or 0. + // 1 means a new DMI is filled in (dmi_pool_used_ is the next blank DMI in + // the pool). + // 0 means either the dmi node can not be extended with splid, or the splid + // is a Shengmu id, which is only used to get lpi_items, or the result node + // in DictTrie has no son, it is not nccessary to keep the new DMI. + // + // This function modifies the content of lpi_items_ and lpi_total_. + // lpi_items_ is used to get the LmaPsbItem list, lpi_total_ returns the size. + // The function's returned value has no relation with the value of lpi_num. + // + // If dmi == NULL, this function will extend the root node of DictTrie + // + // This function will not change dmi_nd_pool_used_. Please change it after + // calling this function if necessary. + // + // The caller should guarantees that NULL != dep. + size_t extend_dmi(DictExtPara *dep, DictMatchInfo *dmi_s); + + // Extend dmi for the composing phrase. + size_t extend_dmi_c(DictExtPara *dep, DictMatchInfo *dmi_s); + + // Extend a MatrixNode with the give LmaPsbItem list. + // res_row is the destination row number. + // This function does not change mtrx_nd_pool_used_. Please change it after + // calling this function if necessary. + // return 0 always. + size_t extend_mtrx_nd(MatrixNode *mtrx_nd, LmaPsbItem lpi_items[], + size_t lpi_num, PoolPosType dmi_fr, size_t res_row); + + + // Try to find a dmi node at step_to position, and the found dmi node should + // match the given spelling id strings. + PoolPosType match_dmi(size_t step_to, uint16 spl_ids[], uint16 spl_id_num); + + bool add_char(char ch); + bool prepare_add_char(char ch); + + // Called after prepare_add_char, so the input char has been saved. + bool add_char_qwerty(); + + // Prepare candidates from the last fixed hanzi position. + void prepare_candidates(); + + // Is the character in step pos a splitter character? + // The caller guarantees that the position is valid. + bool is_split_at(uint16 pos); + + void fill_dmi(DictMatchInfo *dmi, MileStoneHandle *handles, + PoolPosType dmi_fr, + uint16 spl_id, uint16 node_num, unsigned char dict_level, + bool splid_end_split, unsigned char splstr_len, + unsigned char all_full_id); + + size_t inner_predict(const char16 fixed_scis_ids[], uint16 scis_num, + char16 predict_buf[][kMaxPredictSize + 1], + size_t buf_len); + + // Add the first candidate to the user dictionary. + bool try_add_cand0_to_userdict(); + + // Add a user lemma to the user dictionary. This lemma is a subset of + // candidate 0. lma_from is from which lemma in lma_ids_, lma_num is the + // number of lemmas to be combined together as a new lemma. The caller + // gurantees that the combined new lemma's length is less or equal to + // kMaxLemmaSize. + bool add_lma_to_userdict(uint16 lma_from, uint16 lma_num, float score); + + // Update dictionary frequencies. + void update_dict_freq(); + + void debug_print_dmi(PoolPosType dmi_pos, uint16 nest_level); + + public: + MatrixSearch(); + ~MatrixSearch(); + + bool init(const char *fn_sys_dict, const char *fn_usr_dict); + + bool init_fd(int sys_fd, long start_offset, long length, + const char *fn_usr_dict); + + void set_max_lens(size_t max_sps_len, size_t max_hzs_len); + + void close(); + + void flush_cache(); + + void set_xi_an_switch(bool xi_an_enabled); + + bool get_xi_an_switch(); + + // Reset the search space. Equivalent to reset_search(0). + // If inited, always return true; + bool reset_search(); + + // Search a Pinyin string. + // Return value is the position successfully parsed. + size_t search(const char *py, size_t py_len); + + // Used to delete something in the Pinyin string kept by the engine, and do + // a re-search. + // Return value is the new length of Pinyin string kept by the engine which + // is parsed successfully. + // If is_pos_in_splid is false, pos is used to indicate that pos-th Pinyin + // character needs to be deleted. If is_pos_in_splid is true, all Pinyin + // characters for pos-th spelling id needs to be deleted. + // If the deleted character(s) is just after a fixed lemma or sub lemma in + // composing phrase, clear_fixed_this_step indicates whether we needs to + // unlock the last fixed lemma or sub lemma. + // If is_pos_in_splid is false, and pos-th character is in the range for the + // fixed lemmas or composing string, this function will do nothing and just + // return the result of the previous search. + size_t delsearch(size_t pos, bool is_pos_in_splid, + bool clear_fixed_this_step); + + // Get the number of candiates, called after search(). + size_t get_candidate_num(); + + // Get the Pinyin string stored by the engine. + // *decoded_len returns the length of the successfully decoded string. + const char* get_pystr(size_t *decoded_len); + + // Get the spelling boundaries for the first sentence candidate. + // Number of spellings will be returned. The number of valid elements in + // spl_start is one more than the return value because the last one is used + // to indicate the beginning of the next un-input speling. + // For a Pinyin "women", the returned value is 2, spl_start is [0, 2, 5] . + size_t get_spl_start(const uint16 *&spl_start); + + // Get one candiate string. If full sentence candidate is available, it will + // be the first one. + char16* get_candidate(size_t cand_id, char16 *cand_str, size_t max_len); + + // Get the first candiate, which is a "full sentence". + // retstr_len is not NULL, it will be used to return the string length. + // If only_unfixed is true, only unfixed part will be fetched. + char16* get_candidate0(char16* cand_str, size_t max_len, + uint16 *retstr_len, bool only_unfixed); + + // Choose a candidate. The decoder will do a search after the fixed position. + size_t choose(size_t cand_id); + + // Cancel the last choosing operation, and return the new number of choices. + size_t cancel_last_choice(); + + // Get the length of fixed Hanzis. + size_t get_fixedlen(); + + size_t get_predicts(const char16 fixed_buf[], + char16 predict_buf[][kMaxPredictSize + 1], + size_t buf_len); +}; +} + +#endif // PINYINIME_ANDPY_INCLUDE_MATRIXSEARCH_H__ diff --git a/jni/include/mystdlib.h b/jni/include/mystdlib.h new file mode 100644 index 0000000..dfcf980 --- /dev/null +++ b/jni/include/mystdlib.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_MYSTDLIB_H__ +#define PINYINIME_INCLUDE_MYSTDLIB_H__ + +#include + +namespace ime_pinyin { + +void myqsort(void *p, size_t n, size_t es, + int (*cmp)(const void *, const void *)); + +void *mybsearch(const void *key, const void *base, + size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); +} + +#endif // PINYINIME_INCLUDE_MYSTDLIB_H__ diff --git a/jni/include/ngram.h b/jni/include/ngram.h new file mode 100644 index 0000000..ad6c304 --- /dev/null +++ b/jni/include/ngram.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_NGRAM_H__ +#define PINYINIME_INCLUDE_NGRAM_H__ + +#include +#include +#include "./dictdef.h" + +namespace ime_pinyin { + +typedef unsigned char CODEBOOK_TYPE; + +static const size_t kCodeBookSize = 256; + +class NGram { + public: + // The maximum score of a lemma item. + static const LmaScoreType kMaxScore = 0x3fff; + + // In order to reduce the storage size, the original log value is amplified by + // kScoreAmplifier, and we use LmaScoreType to store. + // After this process, an item with a lower score has a higher frequency. + static const int kLogValueAmplifier = -800; + + // System words' total frequency. It is not the real total frequency, instead, + // It is only used to adjust system lemmas' scores when the user dictionary's + // total frequency changes. + // In this version, frequencies of system lemmas are fixed. We are considering + // to make them changable in next version. + static const size_t kSysDictTotalFreq = 100000000; + + private: + + static NGram* instance_; + + bool initialized_; + size_t idx_num_; + + size_t total_freq_none_sys_; + + // Score compensation for system dictionary lemmas. + // Because after user adds some user lemmas, the total frequency changes, and + // we use this value to normalize the score. + float sys_score_compensation_; + +#ifdef ___BUILD_MODEL___ + double *freq_codes_df_; +#endif + LmaScoreType *freq_codes_; + CODEBOOK_TYPE *lma_freq_idx_; + + public: + NGram(); + ~NGram(); + + static NGram& get_instance(); + + bool save_ngram(FILE *fp); + bool load_ngram(FILE *fp); + + // Set the total frequency of all none system dictionaries. + void set_total_freq_none_sys(size_t freq_none_sys); + + float get_uni_psb(LemmaIdType lma_id); + + // Convert a probability to score. Actually, the score will be limited to + // kMaxScore, but at runtime, we also need float expression to get accurate + // value of the score. + // After the conversion, a lower score indicates a higher probability of the + // item. + static float convert_psb_to_score(double psb); + +#ifdef ___BUILD_MODEL___ + // For constructing the unigram mode model. + bool build_unigram(LemmaEntry *lemma_arr, size_t num, + LemmaIdType next_idx_unused); +#endif +}; +} + +#endif // PINYINIME_INCLUDE_NGRAM_H__ diff --git a/jni/include/pinyinime.h b/jni/include/pinyinime.h new file mode 100644 index 0000000..0744ec7 --- /dev/null +++ b/jni/include/pinyinime.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_ANDPYIME_H__ +#define PINYINIME_INCLUDE_ANDPYIME_H__ + +#include +#include "./dictdef.h" + +#ifdef __cplusplus +extern "C" { +#endif + + namespace ime_pinyin { + + /** + * Open the decoder engine via the system and user dictionary file names. + * + * @param fn_sys_dict The file name of the system dictionary. + * @param fn_usr_dict The file name of the user dictionary. + * @return true if open the decoder engine successfully. + */ + bool im_open_decoder(const char *fn_sys_dict, const char *fn_usr_dict); + + /** + * Open the decoder engine via the system dictionary FD and user dictionary + * file name. Because on Android, the system dictionary is embedded in the + * whole application apk file. + * + * @param sys_fd The file in which the system dictionary is embedded. + * @param start_offset The starting position of the system dictionary in the + * file sys_fd. + * @param length The length of the system dictionary in the file sys_fd, + * counted in byte. + * @return true if succeed. + */ + bool im_open_decoder_fd(int sys_fd, long start_offset, long length, + const char *fn_usr_dict); + + /** + * Close the decoder engine. + */ + void im_close_decoder(); + + /** + * Set maximum limitations for decoding. If this function is not called, + * default values will be used. For example, due to screen size limitation, + * the UI engine of the IME can only show a certain number of letters(input) + * to decode, and a certain number of Chinese characters(output). If after + * user adds a new letter, the input or the output string is longer than the + * limitations, the engine will discard the recent letter. + * + * @param max_sps_len Maximum length of the spelling string(Pinyin string). + * @max_hzs_len Maximum length of the decoded Chinese character string. + */ + void im_set_max_lens(size_t max_sps_len, size_t max_hzs_len); + + /** + * Flush cached data to persistent memory. Because at runtime, in order to + * achieve best performance, some data is only store in memory. + */ + void im_flush_cache(); + + /** + * Use a spelling string(Pinyin string) to search. The engine will try to do + * an incremental search based on its previous search result, so if the new + * string has the same prefix with the previous one stored in the decoder, + * the decoder will only continue the search from the end of the prefix. + * If the caller needs to do a brand new search, please call im_reset_search() + * first. Calling im_search() is equivalent to calling im_add_letter() one by + * one. + * + * @param sps_buf The spelling string buffer to decode. + * @param sps_len The length of the spelling string buffer. + * @return The number of candidates. + */ + size_t im_search(const char* sps_buf, size_t sps_len); + + /** + * Make a delete operation in the current search result, and make research if + * necessary. + * + * @param pos The posistion of char in spelling string to delete, or the + * position of spelling id in result string to delete. + * @param is_pos_in_splid Indicate whether the pos parameter is the position + * in the spelling string, or the position in the result spelling id string. + * @return The number of candidates. + */ + size_t im_delsearch(size_t pos, bool is_pos_in_splid, + bool clear_fixed_this_step); + + /** + * Reset the previous search result. + */ + void im_reset_search(); + + /** + * Add a Pinyin letter to the current spelling string kept by decoder. If the + * decoder fails in adding the letter, it will do nothing. im_get_sps_str() + * can be used to get the spelling string kept by decoder currently. + * + * @param ch The letter to add. + * @return The number of candidates. + */ + size_t im_add_letter(char ch); + + /** + * Get the spelling string kept by the decoder. + * + * @param decoded_len Used to return how many characters in the spelling + * string is successfully parsed. + * @return The spelling string kept by the decoder. + */ + const char *im_get_sps_str(size_t *decoded_len); + + /** + * Get a candidate(or choice) string. + * + * @param cand_id The id to get a candidate. Started from 0. Usually, id 0 + * is a sentence-level candidate. + * @param cand_str The buffer to store the candidate. + * @param max_len The maximum length of the buffer. + * @return cand_str if succeeds, otherwise NULL. + */ + char16* im_get_candidate(size_t cand_id, char16* cand_str, + size_t max_len); + + /** + * Get the segmentation information(the starting positions) of the spelling + * string. + * + * @param spl_start Used to return the starting posistions. + * @return The number of spelling ids. If it is L, there will be L+1 valid + * elements in spl_start, and spl_start[L] is the posistion after the end of + * the last spelling id. + */ + size_t im_get_spl_start_pos(const uint16 *&spl_start); + + /** + * Choose a candidate and make it fixed. If the candidate does not match + * the end of all spelling ids, new candidates will be provided from the + * first unfixed position. If the candidate matches the end of the all + * spelling ids, there will be only one new candidates, or the whole fixed + * sentence. + * + * @param cand_id The id of candidate to select and make it fixed. + * @return The number of candidates. If after the selection, the whole result + * string has been fixed, there will be only one candidate. + */ + size_t im_choose(size_t cand_id); + + /** + * Cancel the last selection, or revert the last operation of im_choose(). + * + * @return The number of candidates. + */ + size_t im_cancel_last_choice(); + + /** + * Get the number of fixed spelling ids, or Chinese characters. + * + * @return The number of fixed spelling ids, of Chinese characters. + */ + size_t im_get_fixed_len(); + + /** + * Cancel the input state and reset the search workspace. + */ + bool im_cancel_input(); + + /** + * Get prediction candiates based on the given fixed Chinese string as the + * history. + * + * @param his_buf The history buffer to do the prediction. It should be ended + * with '\0'. + * @param pre_buf Used to return prediction result list. + * @return The number of predicted result string. + */ + size_t im_get_predicts(const char16 *his_buf, + char16 (*&pre_buf)[kMaxPredictSize + 1]); + + /** + * Enable Shengmus in ShouZiMu mode. + */ + void im_enable_shm_as_szm(bool enable); + + /** + * Enable Yunmus in ShouZiMu mode. + */ + void im_enable_ym_as_szm(bool enable); +} + +#ifdef __cplusplus +} +#endif + +#endif // PINYINIME_INCLUDE_ANDPYIME_H__ diff --git a/jni/include/searchutility.h b/jni/include/searchutility.h new file mode 100644 index 0000000..f135710 --- /dev/null +++ b/jni/include/searchutility.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_ANDPY_INCLUDE_SEARCHCOMMON_H__ +#define PINYINIME_ANDPY_INCLUDE_SEARCHCOMMON_H__ + +#include +#include "./spellingtrie.h" + +namespace ime_pinyin { + +// Type used to identify the size of a pool, such as id pool, etc. +typedef uint16 PoolPosType; + +// Type used to identify a parsing mile stone in an atom dictionary. +typedef uint16 MileStoneHandle; + +// Type used to express a lemma and its probability score. +typedef struct { + size_t id:(kLemmaIdSize * 8); + size_t lma_len:4; + uint16 psb; // The score, the lower psb, the higher possibility. + // For single character items, we may also need Hanzi. + // For multiple characer items, ignore it. + char16 hanzi; +} LmaPsbItem, *PLmaPsbItem; + +// LmaPsbItem extended with string. +typedef struct { + LmaPsbItem lpi; + char16 str[kMaxLemmaSize + 1]; +} LmaPsbStrItem, *PLmaPsbStrItem; + + +typedef struct { + float psb; + char16 pre_hzs[kMaxPredictSize]; + uint16 his_len; // The length of the history used to do the prediction. +} NPredictItem, *PNPredictItem; + +// Parameter structure used to extend in a dictionary. All dictionaries +// receives the same DictExtPara and a dictionary specific MileStoneHandle for +// extending. +// +// When the user inputs a new character, AtomDictBase::extend_dict() will be +// called at least once for each dictionary. +// +// For example, when the user inputs "wm", extend_dict() will be called twice, +// and the DictExtPara parameter are as follows respectively: +// 1. splids = {w, m}; splids_extended = 1; ext_len = 1; step_no = 1; +// splid_end_split = false; id_start = wa(the first id start with 'w'); +// id_num = number of ids starting with 'w'. +// 2. splids = {m}; splids_extended = 0; ext_len = 1; step_no = 1; +// splid_end_split = false; id_start = wa; id_num = number of ids starting with +// 'w'. +// +// For string "women", one of the cases of the DictExtPara parameter is: +// splids = {wo, men}, splids_extended = 1, ext_len = 3 (length of "men"), +// step_no = 4; splid_end_split = false; id_start = men, id_num = 1. +// +typedef struct { + // Spelling ids for extending, there are splids_extended + 1 ids in the + // buffer. + // For a normal lemma, there can only be kMaxLemmaSize spelling ids in max, + // but for a composing phrase, there can kMaxSearchSteps spelling ids. + uint16 splids[kMaxSearchSteps]; + + // Number of ids that have been used before. splids[splids_extended] is the + // newly added id for the current extension. + uint16 splids_extended; + + // The step span of the extension. It is also the size of the string for + // the newly added spelling id. + uint16 ext_len; + + // The step number for the current extension. It is also the ending position + // in the input Pinyin string for the substring of spelling ids in splids[]. + // For example, when the user inputs "women", step_no = 4. + // This parameter may useful to manage the MileStoneHandle list for each + // step. When the user deletes a character from the string, MileStoneHandle + // objects for the the steps after that character should be reset; when the + // user begins a new string, all MileStoneHandle objects should be reset. + uint16 step_no; + + // Indicate whether the newly added spelling ends with a splitting character + bool splid_end_split; + + // If the newly added id is a half id, id_start is the first id of the + // corresponding full ids; if the newly added id is a full id, id_start is + // that id. + uint16 id_start; + + // If the newly added id is a half id, id_num is the number of corresponding + // ids; if it is a full id, id_num == 1. + uint16 id_num; +}DictExtPara, *PDictExtPara; + +bool is_system_lemma(LemmaIdType lma_id); +bool is_user_lemma(LemmaIdType lma_id); +bool is_composing_lemma(LemmaIdType lma_id); + +int cmp_lpi_with_psb(const void *p1, const void *p2); +int cmp_lpi_with_unified_psb(const void *p1, const void *p2); +int cmp_lpi_with_id(const void *p1, const void *p2); +int cmp_lpi_with_hanzi(const void *p1, const void *p2); + +int cmp_lpsi_with_str(const void *p1, const void *p2); + +int cmp_hanzis_1(const void *p1, const void *p2); +int cmp_hanzis_2(const void *p1, const void *p2); +int cmp_hanzis_3(const void *p1, const void *p2); +int cmp_hanzis_4(const void *p1, const void *p2); +int cmp_hanzis_5(const void *p1, const void *p2); +int cmp_hanzis_6(const void *p1, const void *p2); +int cmp_hanzis_7(const void *p1, const void *p2); +int cmp_hanzis_8(const void *p1, const void *p2); + +int cmp_npre_by_score(const void *p1, const void *p2); +int cmp_npre_by_hislen_score(const void *p1, const void *p2); +int cmp_npre_by_hanzi_score(const void *p1, const void *p2); + + +size_t remove_duplicate_npre(NPredictItem *npre_items, size_t npre_num); + +size_t align_to_size_t(size_t size); + +} // namespace + +#endif // PINYINIME_ANDPY_INCLUDE_SEARCHCOMMON_H__ diff --git a/jni/include/spellingtable.h b/jni/include/spellingtable.h new file mode 100644 index 0000000..fd79c6e --- /dev/null +++ b/jni/include/spellingtable.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_SPELLINGTABLE_H__ +#define PINYINIME_INCLUDE_SPELLINGTABLE_H__ + +#include +#include "./dictdef.h" + +namespace ime_pinyin { + +#ifdef ___BUILD_MODEL___ + +const size_t kMaxSpellingSize = kMaxPinyinSize; + +typedef struct { + char str[kMaxSpellingSize + 1]; + double freq; +} RawSpelling, *PRawSpelling; + +// This class is used to store the spelling strings +// The length of the input spelling string should be less or equal to the +// spelling_size_ (set by init_table). If the input string is too long, +// we only keep its first spelling_size_ chars. +class SpellingTable { + private: + static const size_t kNotSupportNum = 3; + static const char kNotSupportList[kNotSupportNum][kMaxSpellingSize + 1]; + + bool need_score_; + + size_t spelling_max_num_; + + RawSpelling *raw_spellings_; + + // Used to store spelling strings. If the spelling table needs to calculate + // score, an extra char after each spelling string is the score. + // An item with a lower score has a higher probability. + char *spelling_buf_; + size_t spelling_size_; + + double total_freq_; + + size_t spelling_num_; + + double score_amplifier_; + + unsigned char average_score_; + + // If frozen is true, put_spelling() and contain() are not allowed to call. + bool frozen_; + + size_t get_hash_pos(const char* spelling_str); + size_t hash_pos_next(size_t hash_pos); + void free_resource(); + public: + SpellingTable(); + ~SpellingTable(); + + // pure_spl_size is the pure maximum spelling string size. For example, + // "zhuang" is the longgest item in Pinyin, so pure_spl_size should be 6. + // spl_max_num is the maximum number of spelling strings to store. + // need_score is used to indicate whether the caller needs to calculate a + // score for each spelling. + bool init_table(size_t pure_spl_size, size_t spl_max_num, bool need_score); + + // Put a spelling string to the table. + // It always returns false if called after arrange() withtout a new + // init_table() operation. + // freq is the spelling's occuring count. + // If the spelling has been in the table, occuring count will accumulated. + bool put_spelling(const char* spelling_str, double spl_count); + + // Test whether a spelling string is in the table. + // It always returns false, when being called after arrange() withtout a new + // init_table() operation. + bool contain(const char* spelling_str); + + // Sort the spelling strings and put them from the begin of the buffer. + // Return the pointer of the sorted spelling strings. + // item_size and spl_num return the item size and number of spelling. + // Because each spelling uses a '\0' as terminator, the returned item_size is + // at least one char longer than the spl_size parameter specified by + // init_table(). If the table is initialized to calculate score, item_size + // will be increased by 1, and current_spl_str[item_size - 1] stores an + // unsinged char score. + // An item with a lower score has a higher probability. + // Do not call put_spelling() and contains() after arrange(). + const char* arrange(size_t *item_size, size_t *spl_num); + + float get_score_amplifier(); + + unsigned char get_average_score(); +}; +#endif // ___BUILD_MODEL___ +} + +#endif // PINYINIME_INCLUDE_SPELLINGTABLE_H__ diff --git a/jni/include/spellingtrie.h b/jni/include/spellingtrie.h new file mode 100644 index 0000000..4438757 --- /dev/null +++ b/jni/include/spellingtrie.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_SPELLINGTRIE_H__ +#define PINYINIME_INCLUDE_SPELLINGTRIE_H__ + +#include +#include +#include "./dictdef.h" + +namespace ime_pinyin { + +static const unsigned short kFullSplIdStart = kHalfSpellingIdNum + 1; + +// Node used for the trie of spellings +struct SpellingNode { + SpellingNode *first_son; + // The spelling id for each node. If you need more bits to store + // spelling id, please adjust this structure. + uint16 spelling_idx:11; + uint16 num_of_son:5; + char char_this_node; + unsigned char score; +}; + +class SpellingTrie { + private: + static const int kMaxYmNum = 64; + static const size_t kValidSplCharNum = 26; + + static const uint16 kHalfIdShengmuMask = 0x01; + static const uint16 kHalfIdYunmuMask = 0x02; + static const uint16 kHalfIdSzmMask = 0x04; + + // Map from half spelling id to single char. + // For half ids of Zh/Ch/Sh, map to z/c/s (low case) respectively. + // For example, 1 to 'A', 2 to 'B', 3 to 'C', 4 to 'c', 5 to 'D', ..., + // 28 to 'Z', 29 to 'z'. + // [0] is not used to achieve better efficiency. + static const char kHalfId2Sc_[kFullSplIdStart + 1]; + + static unsigned char char_flags_[]; + static SpellingTrie* instance_; + + // The spelling table + char *spelling_buf_; + + // The size of longest spelling string, includes '\0' and an extra char to + // store score. For example, "zhuang" is the longgest item in Pinyin list, + // so spelling_size_ is 8. + // Structure: The string ended with '\0' + score char. + // An item with a lower score has a higher probability. + size_t spelling_size_; + + // Number of full spelling ids. + size_t spelling_num_; + + float score_amplifier_; + unsigned char average_score_; + + // The Yunmu id list for the spelling ids (for half ids of Shengmu, + // the Yunmu id is 0). + // The length of the list is spelling_num_ + kFullSplIdStart, + // so that spl_ym_ids_[splid] is the Yunmu id of the splid. + uint8 *spl_ym_ids_; + + // The Yunmu table. + // Each Yunmu will be assigned with Yunmu id from 1. + char *ym_buf_; + size_t ym_size_; // The size of longest Yunmu string, '\0'included. + size_t ym_num_; + + // The spelling string just queried + char *splstr_queried_; + + // The spelling string just queried + char16 *splstr16_queried_; + + // The root node of the spelling tree + SpellingNode* root_; + + // If a none qwerty key such as a fnction key like ENTER is given, this node + // will be used to indicate that this is not a QWERTY node. + SpellingNode* dumb_node_; + + // If a splitter key is pressed, this node will be used to indicate that this + // is a splitter key. + SpellingNode* splitter_node_; + + // Used to get the first level sons. + SpellingNode* level1_sons_[kValidSplCharNum]; + + // The full spl_id range for specific half id. + // h2f means half to full. + // A half id can be a ShouZiMu id (id to represent the first char of a full + // spelling, including Shengmu and Yunmu), or id of zh/ch/sh. + // [1..kFullSplIdStart-1] is the arrange of half id. + uint16 h2f_start_[kFullSplIdStart]; + uint16 h2f_num_[kFullSplIdStart]; + + // Map from full id to half id. + uint16 *f2h_; + +#ifdef ___BUILD_MODEL___ + // How many node used to build the trie. + size_t node_num_; +#endif + + SpellingTrie(); + + void free_son_trie(SpellingNode* node); + + // Construct a subtree using a subset of the spelling array (from + // item_star to item_end). + // Member spelliing_buf_ and spelling_size_ should be valid. + // parent is used to update its num_of_son and score. + SpellingNode* construct_spellings_subset(size_t item_start, size_t item_end, + size_t level, SpellingNode *parent); + bool build_f2h(); + + // The caller should guarantee ch >= 'A' && ch <= 'Z' + bool is_shengmu_char(char ch) const; + + // The caller should guarantee ch >= 'A' && ch <= 'Z' + bool is_yunmu_char(char ch) const; + +#ifdef ___BUILD_MODEL___ + // Given a spelling string, return its Yunmu string. + // The caller guaratees spl_str is valid. + const char* get_ym_str(const char *spl_str); + + // Build the Yunmu list, and the mapping relation between the full ids and the + // Yunmu ids. This functin is called after the spelling trie is built. + bool build_ym_info(); +#endif + + friend class SpellingParser; + friend class SmartSplParser; + friend class SmartSplParser2; + + public: + ~SpellingTrie(); + + inline static bool is_valid_spl_char(char ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); + } + + // The caller guarantees that the two chars are valid spelling chars. + inline static bool is_same_spl_char(char ch1, char ch2) { + return ch1 == ch2 || ch1 - ch2 == 'a' - 'A' || ch2 - ch1 == 'a' - 'A'; + } + + // Construct the tree from the input pinyin array + // The given string list should have been sorted. + // score_amplifier is used to convert a possibility value into score. + // average_score is the average_score of all spellings. The dumb node is + // assigned with this score. + bool construct(const char* spelling_arr, size_t item_size, size_t item_num, + float score_amplifier, unsigned char average_score); + + // Test if the given id is a valid spelling id. + // If function returns true, the given splid may be updated like this: + // When 'A' is not enabled in ShouZiMu mode, the parsing result for 'A' is + // first given as a half id 1, but because 'A' is a one-char Yunmu and + // it is a valid id, it needs to updated to its corresponding full id. + bool if_valid_id_update(uint16 *splid) const; + + // Test if the given id is a half id. + bool is_half_id(uint16 splid) const; + + bool is_full_id(uint16 splid) const; + + // Test if the given id is a one-char Yunmu id (obviously, it is also a half + // id), such as 'A', 'E' and 'O'. + bool is_half_id_yunmu(uint16 splid) const; + + // Test if this char is a ShouZiMu char. This ShouZiMu char may be not enabled. + // For Pinyin, only i/u/v is not a ShouZiMu char. + // The caller should guarantee that ch >= 'A' && ch <= 'Z' + bool is_szm_char(char ch) const; + + // Test If this char is enabled in ShouZiMu mode. + // The caller should guarantee that ch >= 'A' && ch <= 'Z' + bool szm_is_enabled(char ch) const; + + // Enable/disable Shengmus in ShouZiMu mode(using the first char of a spelling + // to input). + void szm_enable_shm(bool enable); + + // Enable/disable Yunmus in ShouZiMu mode. + void szm_enable_ym(bool enable); + + // Test if this char is enabled in ShouZiMu mode. + // The caller should guarantee ch >= 'A' && ch <= 'Z' + bool is_szm_enabled(char ch) const; + + // Return the number of full ids for the given half id. + uint16 half2full_num(uint16 half_id) const; + + // Return the number of full ids for the given half id, and fill spl_id_start + // to return the first full id. + uint16 half_to_full(uint16 half_id, uint16 *spl_id_start) const; + + // Return the corresponding half id for the given full id. + // Not frequently used, low efficient. + // Return 0 if fails. + uint16 full_to_half(uint16 full_id) const; + + // To test whether a half id is compatible with a full id. + // Generally, when half_id == full_to_half(full_id), return true. + // But for "Zh, Ch, Sh", if fussy mode is on, half id for 'Z' is compatible + // with a full id like "Zhe". (Fussy mode is not ready). + bool half_full_compatible(uint16 half_id, uint16 full_id) const; + + static const SpellingTrie* get_cpinstance(); + + static SpellingTrie& get_instance(); + + // Save to the file stream + bool save_spl_trie(FILE *fp); + + // Load from the file stream + bool load_spl_trie(FILE *fp); + + // Get the number of spellings + size_t get_spelling_num(); + + // Return the Yunmu id for the given Yunmu string. + // If the string is not valid, return 0; + uint8 get_ym_id(const char* ym_str); + + // Get the readonly Pinyin string for a given spelling id + const char* get_spelling_str(uint16 splid); + + // Get the readonly Pinyin string for a given spelling id + const char16* get_spelling_str16(uint16 splid); + + // Get Pinyin string for a given spelling id. Return the length of the + // string, and fill-in '\0' at the end. + size_t get_spelling_str16(uint16 splid, char16 *splstr16, + size_t splstr16_len); +}; +} + +#endif // PINYINIME_INCLUDE_SPELLINGTRIE_H__ diff --git a/jni/include/splparser.h b/jni/include/splparser.h new file mode 100644 index 0000000..d783bd7 --- /dev/null +++ b/jni/include/splparser.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_SPLPARSER_H__ +#define PINYINIME_INCLUDE_SPLPARSER_H__ + +#include "./dictdef.h" +#include "./spellingtrie.h" + +namespace ime_pinyin { + +class SpellingParser { + protected: + const SpellingTrie *spl_trie_; + + public: + SpellingParser(); + + // Given a string, parse it into a spelling id stream. + // If the whole string are sucessfully parsed, last_is_pre will be true; + // if the whole string is not fullly parsed, last_is_pre will return whether + // the last part of the string is a prefix of a full spelling string. For + // example, given string "zhengzhon", "zhon" is not a valid speling, but it is + // the prefix of "zhong". + // + // If splstr starts with a character not in ['a'-z'] (it is a split char), + // return 0. + // Split char can only appear in the middle of the string or at the end. + uint16 splstr_to_idxs(const char *splstr, uint16 str_len, uint16 splidx[], + uint16 start_pos[], uint16 max_size, bool &last_is_pre); + + // Similar to splstr_to_idxs(), the only difference is that splstr_to_idxs() + // convert single-character Yunmus into half ids, while this function converts + // them into full ids. + uint16 splstr_to_idxs_f(const char *splstr, uint16 str_len, uint16 splidx[], + uint16 start_pos[], uint16 max_size, bool &last_is_pre); + + // Similar to splstr_to_idxs(), the only difference is that this function + // uses char16 instead of char8. + uint16 splstr16_to_idxs(const char16 *splstr, uint16 str_len, uint16 splidx[], + uint16 start_pos[], uint16 max_size, bool &last_is_pre); + + // Similar to splstr_to_idxs_f(), the only difference is that this function + // uses char16 instead of char8. + uint16 splstr16_to_idxs_f(const char16 *splstr16, uint16 str_len, + uint16 splidx[], uint16 start_pos[], + uint16 max_size, bool &last_is_pre); + + // If the given string is a spelling, return the id, others, return 0. + // If the give string is a single char Yunmus like "A", and the char is + // enabled in ShouZiMu mode, the returned spelling id will be a half id. + // When the returned spelling id is a half id, *is_pre returns whether it + // is a prefix of a full spelling string. + uint16 get_splid_by_str(const char *splstr, uint16 str_len, bool *is_pre); + + // If the given string is a spelling, return the id, others, return 0. + // If the give string is a single char Yunmus like "a", no matter the char + // is enabled in ShouZiMu mode or not, the returned spelling id will be + // a full id. + // When the returned spelling id is a half id, *p_is_pre returns whether it + // is a prefix of a full spelling string. + uint16 get_splid_by_str_f(const char *splstr, uint16 str_len, bool *is_pre); + + // Splitter chars are not included. + bool is_valid_to_parse(char ch); + + // When auto-correction is not enabled, get_splid_by_str() will be called to + // return the single result. When auto-correction is enabled, this function + // will be called to get the results. Auto-correction is not ready. + // full_id_num returns number of full spelling ids. + // is_pre returns whether the given string is the prefix of a full spelling + // string. + // If splstr starts with a character not in [a-zA-Z] (it is a split char), + // return 0. + // Split char can only appear in the middle of the string or at the end. + // The caller should guarantee NULL != splstr && str_len > 0 && NULL != splidx + uint16 get_splids_parallel(const char *splstr, uint16 str_len, + uint16 splidx[], uint16 max_size, + uint16 &full_id_num, bool &is_pre); +}; +} + +#endif // PINYINIME_INCLUDE_SPLPARSER_H__ diff --git a/jni/include/sync.h b/jni/include/sync.h new file mode 100644 index 0000000..bf42d1f --- /dev/null +++ b/jni/include/sync.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_SYNC_H__ +#define PINYINIME_INCLUDE_SYNC_H__ + +#define ___SYNC_ENABLED___ + +#ifdef ___SYNC_ENABLED___ + +#include "userdict.h" + +namespace ime_pinyin { + +// Class for user dictionary synchronization +// This class is not thread safe +// Normal invoking flow will be +// begin() -> +// put_lemmas() x N -> +// { +// get_lemmas() -> +// [ get_last_got_count() ] -> +// clear_last_got() -> +// } x N -> +// finish() +class Sync { + public: + Sync(); + ~Sync(); + + static const int kUserDictMaxLemmaCount = 5000; + static const int kUserDictMaxLemmaSize = 200000; + static const int kUserDictRatio = 20; + + bool begin(const char * filename); + + // Merge lemmas downloaded from sync server into local dictionary + // lemmas, lemmas string encoded in UTF16LE + // len, length of lemmas string + // Return how many lemmas merged successfully + int put_lemmas(char16 * lemmas, int len); + + // Get local new user lemmas into UTF16LE string + // str, buffer ptr to store new user lemmas + // size, size of buffer + // Return length of returned buffer in measure of UTF16LE + int get_lemmas(char16 * str, int size); + + // Return lemmas count in last get_lemmas() + int get_last_got_count(); + + // Return total lemmas count need get_lemmas() + int get_total_count(); + + // Clear lemmas got by recent get_lemmas() + void clear_last_got(); + + void finish(); + + int get_capacity(); + + private: + UserDict * userdict_; + char * dictfile_; + int last_count_; +}; + +} + +#endif + +#endif // PINYINIME_INCLUDE_SYNC_H__ diff --git a/jni/include/userdict.h b/jni/include/userdict.h new file mode 100644 index 0000000..02da218 --- /dev/null +++ b/jni/include/userdict.h @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_USERDICT_H__ +#define PINYINIME_INCLUDE_USERDICT_H__ + +#define ___CACHE_ENABLED___ +#define ___SYNC_ENABLED___ +#define ___PREDICT_ENABLED___ + +// Debug performance for operations +// #define ___DEBUG_PERF___ + +#include +#include "atomdictbase.h" + +namespace ime_pinyin { + +class UserDict : public AtomDictBase { + public: + UserDict(); + ~UserDict(); + + bool load_dict(const char *file_name, LemmaIdType start_id, + LemmaIdType end_id); + + bool close_dict(); + + size_t number_of_lemmas(); + + void reset_milestones(uint16 from_step, MileStoneHandle from_handle); + + MileStoneHandle extend_dict(MileStoneHandle from_handle, + const DictExtPara *dep, LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num); + + size_t get_lpis(const uint16 *splid_str, uint16 splid_str_len, + LmaPsbItem *lpi_items, size_t lpi_max); + + uint16 get_lemma_str(LemmaIdType id_lemma, char16* str_buf, + uint16 str_max); + + uint16 get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, + uint16 splids_max, bool arg_valid); + + size_t predict(const char16 last_hzs[], uint16 hzs_len, + NPredictItem *npre_items, size_t npre_max, + size_t b4_used); + + // Full spelling ids are required + LemmaIdType put_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count); + + LemmaIdType update_lemma(LemmaIdType lemma_id, int16 delta_count, + bool selected); + + LemmaIdType get_lemma_id(char16 lemma_str[], uint16 splids[], + uint16 lemma_len); + + LmaScoreType get_lemma_score(LemmaIdType lemma_id); + + LmaScoreType get_lemma_score(char16 lemma_str[], uint16 splids[], + uint16 lemma_len); + + bool remove_lemma(LemmaIdType lemma_id); + + size_t get_total_lemma_count(); + void set_total_lemma_count_of_others(size_t count); + + void flush_cache(); + + void set_limit(uint32 max_lemma_count, uint32 max_lemma_size, + uint32 reclaim_ratio); + + void reclaim(); + + void defragment(); + +#ifdef ___SYNC_ENABLED___ + void clear_sync_lemmas(unsigned int start, unsigned int end); + + int get_sync_count(); + + LemmaIdType put_lemma_no_sync(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count, uint64 lmt); + /** + * Add lemmas encoded in UTF-16LE into dictionary without adding sync flag. + * + * @param lemmas in format of 'wo men,WM,0.32;da jia,DJ,0.12' + * @param len length of lemmas string in UTF-16LE + * @return newly added lemma count + */ + int put_lemmas_no_sync_from_utf16le_string(char16 * lemmas, int len); + + /** + * Get lemmas need sync to a UTF-16LE string of above format. + * Note: input buffer (str) must not be too small. If str is too small to + * contain single one lemma, there might be a dead loop. + * + * @param str buffer to write lemmas + * @param size buffer size in UTF-16LE + * @param count output value of lemma returned + * @return UTF-16LE string length + */ + int get_sync_lemmas_in_utf16le_string_from_beginning( + char16 * str, int size, int * count); + +#endif + + struct UserDictStat { + uint32 version; + const char * file_name; + struct timeval load_time; + struct timeval last_update; + uint32 disk_size; + uint32 lemma_count; + uint32 lemma_size; + uint32 delete_count; + uint32 delete_size; +#ifdef ___SYNC_ENABLED___ + uint32 sync_count; +#endif + uint32 reclaim_ratio; + uint32 limit_lemma_count; + uint32 limit_lemma_size; + }; + + bool state(UserDictStat * stat); + + private: + uint32 total_other_nfreq_; + struct timeval load_time_; + LemmaIdType start_id_; + uint32 version_; + uint8 * lemmas_; + + // In-Memory-Only flag for each lemma + static const uint8 kUserDictLemmaFlagRemove = 1; + // Inuse lemmas' offset + uint32 * offsets_; + // Highest bit in offset tells whether corresponding lemma is removed + static const uint32 kUserDictOffsetFlagRemove = (1 << 31); + // Maximum possible for the offset + static const uint32 kUserDictOffsetMask = ~(kUserDictOffsetFlagRemove); + // Bit width for last modified time, from 1 to 16 + static const uint32 kUserDictLMTBitWidth = 16; + // Granularity for last modified time in second + static const uint32 kUserDictLMTGranularity = 60 * 60 * 24 * 7; + // Maximum frequency count + static const uint16 kUserDictMaxFrequency = 0xFFFF; + +#define COARSE_UTC(year, month, day, hour, minute, second) \ + ( \ + (year - 1970) * 365 * 24 * 60 * 60 + \ + (month - 1) * 30 * 24 * 60 * 60 + \ + (day - 1) * 24 * 60 * 60 + \ + (hour - 0) * 60 * 60 + \ + (minute - 0) * 60 + \ + (second - 0) \ + ) + static const uint64 kUserDictLMTSince = COARSE_UTC(2009, 1, 1, 0, 0, 0); + + // Correspond to offsets_ + uint32 * scores_; + // Following two fields are only valid in memory + uint32 * ids_; +#ifdef ___PREDICT_ENABLED___ + uint32 * predicts_; +#endif +#ifdef ___SYNC_ENABLED___ + uint32 * syncs_; + size_t sync_count_size_; +#endif + uint32 * offsets_by_id_; + + size_t lemma_count_left_; + size_t lemma_size_left_; + + const char * dict_file_; + + // Be sure size is 4xN + struct UserDictInfo { + // When limitation reached, how much percentage will be reclaimed (1 ~ 100) + uint32 reclaim_ratio; + // maximum lemma count, 0 means no limitation + uint32 limit_lemma_count; + // Maximum lemma size, it's different from + // whole disk file size or in-mem dict size + // 0 means no limitation + uint32 limit_lemma_size; + // Total lemma count including deleted and inuse + // Also indicate offsets_ size + uint32 lemma_count; + // Total size of lemmas including used and freed + uint32 lemma_size; + // Freed lemma count + uint32 free_count; + // Freed lemma size in byte + uint32 free_size; +#ifdef ___SYNC_ENABLED___ + uint32 sync_count; +#endif + int32 total_nfreq; + } dict_info_; + + static const uint32 kUserDictVersion = 0x0ABCDEF0; + + static const uint32 kUserDictPreAlloc = 32; + static const uint32 kUserDictAverageNchar = 8; + + enum UserDictState { + // Keep in order + USER_DICT_NONE = 0, + USER_DICT_SYNC, +#ifdef ___SYNC_ENABLED___ + USER_DICT_SYNC_DIRTY, +#endif + USER_DICT_SCORE_DIRTY, + USER_DICT_OFFSET_DIRTY, + USER_DICT_LEMMA_DIRTY, + + USER_DICT_DEFRAGMENTED, + } state_; + + struct UserDictSearchable { + uint16 splids_len; + uint16 splid_start[kMaxLemmaSize]; + uint16 splid_count[kMaxLemmaSize]; + // Compact inital letters for both FuzzyCompareSpellId and cache system + uint32 signature[kMaxLemmaSize / 4]; + }; + +#ifdef ___CACHE_ENABLED___ + enum UserDictCacheType { + USER_DICT_CACHE, + USER_DICT_MISS_CACHE, + }; + + static const int kUserDictCacheSize = 4; + static const int kUserDictMissCacheSize = kMaxLemmaSize - 1; + + struct UserDictMissCache { + uint32 signatures[kUserDictMissCacheSize][kMaxLemmaSize / 4]; + uint16 head, tail; + } miss_caches_[kMaxLemmaSize]; + + struct UserDictCache { + uint32 signatures[kUserDictCacheSize][kMaxLemmaSize / 4]; + uint32 offsets[kUserDictCacheSize]; + uint32 lengths[kUserDictCacheSize]; + // Ring buffer + uint16 head, tail; + } caches_[kMaxLemmaSize]; + + void cache_init(); + + void cache_push(UserDictCacheType type, + UserDictSearchable *searchable, + uint32 offset, uint32 length); + + bool cache_hit(UserDictSearchable *searchable, + uint32 *offset, uint32 *length); + + bool load_cache(UserDictSearchable *searchable, + uint32 *offset, uint32 *length); + + void save_cache(UserDictSearchable *searchable, + uint32 offset, uint32 length); + + void reset_cache(); + + bool load_miss_cache(UserDictSearchable *searchable); + + void save_miss_cache(UserDictSearchable *searchable); + + void reset_miss_cache(); +#endif + + LmaScoreType translate_score(int f); + + int extract_score_freq(int raw_score); + + uint64 extract_score_lmt(int raw_score); + + inline int build_score(uint64 lmt, int freq); + + inline int64 utf16le_atoll(uint16 *s, int len); + + inline int utf16le_lltoa(int64 v, uint16 *s, int size); + + LemmaIdType _put_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count, uint64 lmt); + + size_t _get_lpis(const uint16 *splid_str, uint16 splid_str_len, + LmaPsbItem *lpi_items, size_t lpi_max, bool * need_extend); + + int _get_lemma_score(char16 lemma_str[], uint16 splids[], uint16 lemma_len); + + int _get_lemma_score(LemmaIdType lemma_id); + + int is_fuzzy_prefix_spell_id(const uint16 * id1, uint16 len1, + const UserDictSearchable *searchable); + + bool is_prefix_spell_id(const uint16 * fullids, + uint16 fulllen, const UserDictSearchable *searchable); + + uint32 get_dict_file_size(UserDictInfo * info); + + bool reset(const char *file); + + bool validate(const char *file); + + bool load(const char *file, LemmaIdType start_id); + + bool is_valid_state(); + + bool is_valid_lemma_id(LemmaIdType id); + + LemmaIdType get_max_lemma_id(); + + void set_lemma_flag(uint32 offset, uint8 flag); + + char get_lemma_flag(uint32 offset); + + char get_lemma_nchar(uint32 offset); + + uint16 * get_lemma_spell_ids(uint32 offset); + + uint16 * get_lemma_word(uint32 offset); + + // Prepare searchable to fasten locate process + void prepare_locate(UserDictSearchable *searchable, + const uint16 * splids, uint16 len); + + // Compare initial letters only + int32 fuzzy_compare_spell_id(const uint16 * id1, uint16 len1, + const UserDictSearchable *searchable); + + // Compare exactly two spell ids + // First argument must be a full id spell id + bool equal_spell_id(const uint16 * fullids, + uint16 fulllen, const UserDictSearchable *searchable); + + // Find first item by initial letters + int32 locate_first_in_offsets(const UserDictSearchable *searchable); + + LemmaIdType append_a_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count, uint64 lmt); + + // Check if a lemma is in dictionary + int32 locate_in_offsets(char16 lemma_str[], + uint16 splid_str[], uint16 lemma_len); + + bool remove_lemma_by_offset_index(int offset_index); +#ifdef ___PREDICT_ENABLED___ + uint32 locate_where_to_insert_in_predicts(const uint16 * words, + int lemma_len); + + int32 locate_first_in_predicts(const uint16 * words, int lemma_len); + + void remove_lemma_from_predict_list(uint32 offset); +#endif +#ifdef ___SYNC_ENABLED___ + void queue_lemma_for_sync(LemmaIdType id); + + void remove_lemma_from_sync_list(uint32 offset); + + void write_back_sync(int fd); +#endif + void write_back_score(int fd); + void write_back_offset(int fd); + void write_back_lemma(int fd); + void write_back_all(int fd); + void write_back(); + + struct UserDictScoreOffsetPair { + int score; + uint32 offset_index; + }; + + inline void swap(UserDictScoreOffsetPair * sop, int i, int j); + + void shift_down(UserDictScoreOffsetPair * sop, int i, int n); + + // On-disk format for each lemma + // +-------------+ + // | Version (4) | + // +-------------+ + // +-----------+-----------+--------------------+-------------------+ + // | Spare (1) | Nchar (1) | Splids (2 x Nchar) | Lemma (2 x Nchar) | + // +-----------+-----------+--------------------+-------------------+ + // ... + // +-----------------------+ +-------------+ <---Offset of offset + // | Offset1 by_splids (4) | ... | OffsetN (4) | + // +-----------------------+ +-------------+ +#ifdef ___PREDICT_ENABLED___ + // +----------------------+ +-------------+ + // | Offset1 by_lemma (4) | ... | OffsetN (4) | + // +----------------------+ +-------------+ +#endif + // +------------+ +------------+ + // | Score1 (4) | ... | ScoreN (4) | + // +------------+ +------------+ +#ifdef ___SYNC_ENABLED___ + // +-------------+ +-------------+ + // | NewAdd1 (4) | ... | NewAddN (4) | + // +-------------+ +-------------+ +#endif + // +----------------+ + // | Dict Info (4x) | + // +----------------+ +}; +} + +#endif diff --git a/jni/include/utf16char.h b/jni/include/utf16char.h new file mode 100644 index 0000000..7e957db --- /dev/null +++ b/jni/include/utf16char.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_UTF16CHAR_H__ +#define PINYINIME_INCLUDE_UTF16CHAR_H__ + +#include + +namespace ime_pinyin { + +#ifdef __cplusplus +extern "C" { +#endif + + typedef unsigned short char16; + + // Get a token from utf16_str, + // Returned pointer is a '\0'-terminated utf16 string, or NULL + // *utf16_str_next returns the next part of the string for further tokenizing + char16* utf16_strtok(char16 *utf16_str, size_t *token_size, + char16 **utf16_str_next); + + int utf16_atoi(const char16 *utf16_str); + + float utf16_atof(const char16 *utf16_str); + + size_t utf16_strlen(const char16 *utf16_str); + + int utf16_strcmp(const char16 *str1, const char16 *str2); + int utf16_strncmp(const char16 *str1, const char16 *str2, size_t size); + + char16* utf16_strcpy(char16 *dst, const char16 *src); + char16* utf16_strncpy(char16 *dst, const char16 *src, size_t size); + + + char* utf16_strcpy_tochar(char *dst, const char16 *src); + +#ifdef __cplusplus +} +#endif +} + +#endif // PINYINIME_INCLUDE_UTF16CHAR_H__ diff --git a/jni/include/utf16reader.h b/jni/include/utf16reader.h new file mode 100644 index 0000000..b6d6719 --- /dev/null +++ b/jni/include/utf16reader.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PINYINIME_INCLUDE_UTF16READER_H__ +#define PINYINIME_INCLUDE_UTF16READER_H__ + +#include +#include "./utf16char.h" + +namespace ime_pinyin { + +class Utf16Reader { + private: + FILE *fp_; + char16 *buffer_; + size_t buffer_total_len_; + size_t buffer_next_pos_; + + // Always less than buffer_total_len_ - buffer_next_pos_ + size_t buffer_valid_len_; + + public: + Utf16Reader(); + ~Utf16Reader(); + + // filename is the name of the file to open. + // buffer_len specifies how long buffer should be allocated to speed up the + // future reading + bool open(const char* filename, size_t buffer_len); + char16* readline(char16* read_buf, size_t max_len); + bool close(); +}; +} + +#endif // PINYINIME_INCLUDE_UTF16READER_H__ diff --git a/jni/share/dictbuilder.cpp b/jni/share/dictbuilder.cpp new file mode 100644 index 0000000..6f0bd4f --- /dev/null +++ b/jni/share/dictbuilder.cpp @@ -0,0 +1,1070 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "../include/dictbuilder.h" +#include "../include/dicttrie.h" +#include "../include/mystdlib.h" +#include "../include/ngram.h" +#include "../include/searchutility.h" +#include "../include/spellingtable.h" +#include "../include/spellingtrie.h" +#include "../include/splparser.h" +#include "../include/utf16reader.h" + +namespace ime_pinyin { + +#ifdef ___BUILD_MODEL___ + +static const size_t kReadBufLen = 512; +static const size_t kSplTableHashLen = 2000; + +// Compare a SingleCharItem, first by Hanzis, then by spelling ids, then by +// frequencies. +int cmp_scis_hz_splid_freq(const void* p1, const void* p2) { + const SingleCharItem *s1, *s2; + s1 = static_cast(p1); + s2 = static_cast(p2); + + if (s1->hz < s2->hz) + return -1; + if (s1->hz > s2->hz) + return 1; + + if (s1->splid.half_splid < s2->splid.half_splid) + return -1; + if (s1->splid.half_splid > s2->splid.half_splid) + return 1; + + if (s1->splid.full_splid < s2->splid.full_splid) + return -1; + if (s1->splid.full_splid > s2->splid.full_splid) + return 1; + + if (s1->freq > s2->freq) + return -1; + if (s1->freq < s2->freq) + return 1; + return 0; +} + +int cmp_scis_hz_splid(const void* p1, const void* p2) { + const SingleCharItem *s1, *s2; + s1 = static_cast(p1); + s2 = static_cast(p2); + + if (s1->hz < s2->hz) + return -1; + if (s1->hz > s2->hz) + return 1; + + if (s1->splid.half_splid < s2->splid.half_splid) + return -1; + if (s1->splid.half_splid > s2->splid.half_splid) + return 1; + + if (s1->splid.full_splid < s2->splid.full_splid) + return -1; + if (s1->splid.full_splid > s2->splid.full_splid) + return 1; + + return 0; +} + +int cmp_lemma_entry_hzs(const void* p1, const void* p2) { + size_t size1 = utf16_strlen(((const LemmaEntry*)p1)->hanzi_str); + size_t size2 = utf16_strlen(((const LemmaEntry*)p2)->hanzi_str); + if (size1 < size2) + return -1; + else if (size1 > size2) + return 1; + + return utf16_strcmp(((const LemmaEntry*)p1)->hanzi_str, + ((const LemmaEntry*)p2)->hanzi_str); +} + +int compare_char16(const void* p1, const void* p2) { + if (*((const char16*)p1) < *((const char16*)p2)) + return -1; + if (*((const char16*)p1) > *((const char16*)p2)) + return 1; + return 0; +} + +int compare_py(const void* p1, const void* p2) { + int ret = utf16_strcmp(((const LemmaEntry*)p1)->spl_idx_arr, + ((const LemmaEntry*)p2)->spl_idx_arr); + + if (0 != ret) + return ret; + + return static_cast(((const LemmaEntry*)p2)->freq) - + static_cast(((const LemmaEntry*)p1)->freq); +} + +// First hanzi, if the same, then Pinyin +int cmp_lemma_entry_hzspys(const void* p1, const void* p2) { + size_t size1 = utf16_strlen(((const LemmaEntry*)p1)->hanzi_str); + size_t size2 = utf16_strlen(((const LemmaEntry*)p2)->hanzi_str); + if (size1 < size2) + return -1; + else if (size1 > size2) + return 1; + int ret = utf16_strcmp(((const LemmaEntry*)p1)->hanzi_str, + ((const LemmaEntry*)p2)->hanzi_str); + + if (0 != ret) + return ret; + + ret = utf16_strcmp(((const LemmaEntry*)p1)->spl_idx_arr, + ((const LemmaEntry*)p2)->spl_idx_arr); + return ret; +} + +int compare_splid2(const void* p1, const void* p2) { + int ret = utf16_strcmp(((const LemmaEntry*)p1)->spl_idx_arr, + ((const LemmaEntry*)p2)->spl_idx_arr); + return ret; +} + +DictBuilder::DictBuilder() { + lemma_arr_ = NULL; + lemma_num_ = 0; + + scis_ = NULL; + scis_num_ = 0; + + lma_nodes_le0_ = NULL; + lma_nodes_ge1_ = NULL; + + lma_nds_used_num_le0_ = 0; + lma_nds_used_num_ge1_ = 0; + + homo_idx_buf_ = NULL; + homo_idx_num_eq1_ = 0; + homo_idx_num_gt1_ = 0; + + top_lmas_ = NULL; + top_lmas_num_ = 0; + + spl_table_ = NULL; + spl_parser_ = NULL; +} + +DictBuilder::~DictBuilder() { + free_resource(); +} + +bool DictBuilder::alloc_resource(size_t lma_num) { + if (0 == lma_num) + return false; + + free_resource(); + + lemma_num_ = lma_num; + lemma_arr_ = new LemmaEntry[lemma_num_]; + + top_lmas_num_ = 0; + top_lmas_ = new LemmaEntry[kTopScoreLemmaNum]; + + // New the scis_ buffer to the possible maximum size. + scis_num_ = lemma_num_ * kMaxLemmaSize; + scis_ = new SingleCharItem[scis_num_]; + + // The root and first level nodes is less than kMaxSpellingNum + 1 + lma_nds_used_num_le0_ = 0; + lma_nodes_le0_ = new LmaNodeLE0[kMaxSpellingNum + 1]; + + // Other nodes is less than lemma_num + lma_nds_used_num_ge1_ = 0; + lma_nodes_ge1_ = new LmaNodeGE1[lemma_num_]; + + homo_idx_buf_ = new LemmaIdType[lemma_num_]; + spl_table_ = new SpellingTable(); + spl_parser_ = new SpellingParser(); + + if (NULL == lemma_arr_ || NULL == top_lmas_ || + NULL == scis_ || NULL == spl_table_ || + NULL == spl_parser_ || NULL == lma_nodes_le0_ || + NULL == lma_nodes_ge1_ || NULL == homo_idx_buf_) { + free_resource(); + return false; + } + + memset(lemma_arr_, 0, sizeof(LemmaEntry) * lemma_num_); + memset(scis_, 0, sizeof(SingleCharItem) * scis_num_); + memset(lma_nodes_le0_, 0, sizeof(LmaNodeLE0) * (kMaxSpellingNum + 1)); + memset(lma_nodes_ge1_, 0, sizeof(LmaNodeGE1) * lemma_num_); + memset(homo_idx_buf_, 0, sizeof(LemmaIdType) * lemma_num_); + spl_table_->init_table(kMaxPinyinSize, kSplTableHashLen, true); + + return true; +} + +char16* DictBuilder::read_valid_hanzis(const char *fn_validhzs, size_t *num) { + if (NULL == fn_validhzs || NULL == num) + return NULL; + + *num = 0; + FILE *fp = fopen(fn_validhzs, "rb"); + if (NULL == fp) + return NULL; + + char16 utf16header; + if (fread(&utf16header, sizeof(char16), 1, fp) != 1 || + 0xfeff != utf16header) { + fclose(fp); + return NULL; + } + + fseek(fp, 0, SEEK_END); + *num = ftell(fp) / sizeof(char16); + assert(*num >= 1); + *num -= 1; + + char16 *hzs = new char16[*num]; + if (NULL == hzs) { + fclose(fp); + return NULL; + } + + fseek(fp, 2, SEEK_SET); + + if (fread(hzs, sizeof(char16), *num, fp) != *num) { + fclose(fp); + delete [] hzs; + return NULL; + } + fclose(fp); + + myqsort(hzs, *num, sizeof(char16), compare_char16); + return hzs; +} + +bool DictBuilder::hz_in_hanzis_list(const char16 *hzs, size_t hzs_len, + char16 hz) { + if (NULL == hzs) + return false; + + char16 *found; + found = static_cast( + mybsearch(&hz, hzs, hzs_len, sizeof(char16), compare_char16)); + if (NULL == found) + return false; + + assert(*found == hz); + return true; +} + +// The caller makes sure that the parameters are valid. +bool DictBuilder::str_in_hanzis_list(const char16 *hzs, size_t hzs_len, + const char16 *str, size_t str_len) { + if (NULL == hzs || NULL == str) + return false; + + for (size_t pos = 0; pos < str_len; pos++) { + if (!hz_in_hanzis_list(hzs, hzs_len, str[pos])) + return false; + } + return true; +} + +void DictBuilder::get_top_lemmas() { + top_lmas_num_ = 0; + if (NULL == lemma_arr_) + return; + + for (size_t pos = 0; pos < lemma_num_; pos++) { + if (0 == top_lmas_num_) { + top_lmas_[0] = lemma_arr_[pos]; + top_lmas_num_ = 1; + continue; + } + + if (lemma_arr_[pos].freq > top_lmas_[top_lmas_num_ - 1].freq) { + if (kTopScoreLemmaNum > top_lmas_num_) + top_lmas_num_ += 1; + + size_t move_pos; + for (move_pos = top_lmas_num_ - 1; move_pos > 0; move_pos--) { + top_lmas_[move_pos] = top_lmas_[move_pos - 1]; + if (0 == move_pos - 1 || + (move_pos - 1 > 0 && + top_lmas_[move_pos - 2].freq > lemma_arr_[pos].freq)) { + break; + } + } + assert(move_pos > 0); + top_lmas_[move_pos - 1] = lemma_arr_[pos]; + } else if (kTopScoreLemmaNum > top_lmas_num_) { + top_lmas_[top_lmas_num_] = lemma_arr_[pos]; + top_lmas_num_ += 1; + } + } + + if (kPrintDebug0) { + printf("\n------Top Lemmas------------------\n"); + for (size_t pos = 0; pos < top_lmas_num_; pos++) { + printf("--%d, idx:%06d, score:%.5f\n", pos, top_lmas_[pos].idx_by_hz, + top_lmas_[pos].freq); + } + } +} + +void DictBuilder::free_resource() { + if (NULL != lemma_arr_) + delete [] lemma_arr_; + + if (NULL != scis_) + delete [] scis_; + + if (NULL != lma_nodes_le0_) + delete [] lma_nodes_le0_; + + if (NULL != lma_nodes_ge1_) + delete [] lma_nodes_ge1_; + + if (NULL != homo_idx_buf_) + delete [] homo_idx_buf_; + + if (NULL != spl_table_) + delete spl_table_; + + if (NULL != spl_parser_) + delete spl_parser_; + + lemma_arr_ = NULL; + scis_ = NULL; + lma_nodes_le0_ = NULL; + lma_nodes_ge1_ = NULL; + homo_idx_buf_ = NULL; + spl_table_ = NULL; + spl_parser_ = NULL; + + lemma_num_ = 0; + lma_nds_used_num_le0_ = 0; + lma_nds_used_num_ge1_ = 0; + homo_idx_num_eq1_ = 0; + homo_idx_num_gt1_ = 0; +} + +size_t DictBuilder::read_raw_dict(const char* fn_raw, + const char *fn_validhzs, + size_t max_item) { + if (NULL == fn_raw) return 0; + + Utf16Reader utf16_reader; + if (!utf16_reader.open(fn_raw, kReadBufLen * 10)) + return false; + + char16 read_buf[kReadBufLen]; + + // Read the number of lemmas in the file + size_t lemma_num = 240000; + + // allocate resource required + if (!alloc_resource(lemma_num)) { + utf16_reader.close(); + } + + // Read the valid Hanzi list. + char16 *valid_hzs = NULL; + size_t valid_hzs_num = 0; + valid_hzs = read_valid_hanzis(fn_validhzs, &valid_hzs_num); + + // Begin reading the lemma entries + for (size_t i = 0; i < max_item; i++) { + // read next entry + if (!utf16_reader.readline(read_buf, kReadBufLen)) { + lemma_num = i; + break; + } + + size_t token_size; + char16 *token; + char16 *to_tokenize = read_buf; + + // Get the Hanzi string + token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); + if (NULL == token) { + free_resource(); + utf16_reader.close(); + return false; + } + + size_t lemma_size = utf16_strlen(token); + + if (lemma_size > kMaxLemmaSize) { + i--; + continue; + } + + if (lemma_size > 4) { + i--; + continue; + } + + // Copy to the lemma entry + utf16_strcpy(lemma_arr_[i].hanzi_str, token); + + lemma_arr_[i].hz_str_len = token_size; + + // Get the freq string + token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); + if (NULL == token) { + free_resource(); + utf16_reader.close(); + return false; + } + lemma_arr_[i].freq = utf16_atof(token); + + if (lemma_size > 1 && lemma_arr_[i].freq < 60) { + i--; + continue; + } + + // Get GBK mark, if no valid Hanzi list available, all items which contains + // GBK characters will be discarded. Otherwise, all items which contains + // characters outside of the valid Hanzi list will be discarded. + token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); + assert(NULL != token); + int gbk_flag = utf16_atoi(token); + if (NULL == valid_hzs || 0 == valid_hzs_num) { + if (0 != gbk_flag) { + i--; + continue; + } + } else { + if (!str_in_hanzis_list(valid_hzs, valid_hzs_num, + lemma_arr_[i].hanzi_str, lemma_arr_[i].hz_str_len)) { + i--; + continue; + } + } + + // Get spelling String + bool spelling_not_support = false; + for (size_t hz_pos = 0; hz_pos < (size_t)lemma_arr_[i].hz_str_len; + hz_pos++) { + // Get a Pinyin + token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); + if (NULL == token) { + free_resource(); + utf16_reader.close(); + return false; + } + + assert(utf16_strlen(token) <= kMaxPinyinSize); + + utf16_strcpy_tochar(lemma_arr_[i].pinyin_str[hz_pos], token); + + format_spelling_str(lemma_arr_[i].pinyin_str[hz_pos]); + + // Put the pinyin to the spelling table + if (!spl_table_->put_spelling(lemma_arr_[i].pinyin_str[hz_pos], + lemma_arr_[i].freq)) { + spelling_not_support = true; + break; + } + } + + // The whole line must have been parsed fully, otherwise discard this one. + token = utf16_strtok(to_tokenize, &token_size, &to_tokenize); + if (spelling_not_support || NULL != token) { + i--; + continue; + } + } + + delete [] valid_hzs; + utf16_reader.close(); + + printf("read succesfully, lemma num: %d\n", lemma_num); + + return lemma_num; +} + +bool DictBuilder::build_dict(const char *fn_raw, + const char *fn_validhzs, + DictTrie *dict_trie) { + if (NULL == fn_raw || NULL == dict_trie) + return false; + + lemma_num_ = read_raw_dict(fn_raw, fn_validhzs, 240000); + if (0 == lemma_num_) + return false; + + // Arrange the spelling table, and build a spelling tree + // The size of an spelling. '\0' is included. If the spelling table is + // initialized to calculate the spelling scores, the last char in the + // spelling string will be score, and it is also included in spl_item_size. + size_t spl_item_size; + size_t spl_num; + const char* spl_buf; + spl_buf = spl_table_->arrange(&spl_item_size, &spl_num); + if (NULL == spl_buf) { + free_resource(); + return false; + } + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + + if (!spl_trie.construct(spl_buf, spl_item_size, spl_num, + spl_table_->get_score_amplifier(), + spl_table_->get_average_score())) { + free_resource(); + return false; + } + + printf("spelling tree construct successfully.\n"); + + // Convert the spelling string to idxs + for (size_t i = 0; i < lemma_num_; i++) { + for (size_t hz_pos = 0; hz_pos < (size_t)lemma_arr_[i].hz_str_len; + hz_pos++) { + uint16 spl_idxs[2]; + uint16 spl_start_pos[3]; + bool is_pre = true; + int spl_idx_num = + spl_parser_->splstr_to_idxs(lemma_arr_[i].pinyin_str[hz_pos], + strlen(lemma_arr_[i].pinyin_str[hz_pos]), + spl_idxs, spl_start_pos, 2, is_pre); + assert(1 == spl_idx_num); + + if (spl_trie.is_half_id(spl_idxs[0])) { + uint16 num = spl_trie.half_to_full(spl_idxs[0], spl_idxs); + assert(0 != num); + } + lemma_arr_[i].spl_idx_arr[hz_pos] = spl_idxs[0]; + } + } + + // Sort the lemma items according to the hanzi, and give each unique item a + // id + sort_lemmas_by_hz(); + + scis_num_ = build_scis(); + + // Construct the dict list + dict_trie->dict_list_ = new DictList(); + bool dl_success = dict_trie->dict_list_->init_list(scis_, scis_num_, + lemma_arr_, lemma_num_); + assert(dl_success); + + // Construct the NGram information + NGram& ngram = NGram::get_instance(); + ngram.build_unigram(lemma_arr_, lemma_num_, + lemma_arr_[lemma_num_ - 1].idx_by_hz + 1); + + // sort the lemma items according to the spelling idx string + myqsort(lemma_arr_, lemma_num_, sizeof(LemmaEntry), compare_py); + + get_top_lemmas(); + +#ifdef ___DO_STATISTICS___ + stat_init(); +#endif + + lma_nds_used_num_le0_ = 1; // The root node + bool dt_success = construct_subset(static_cast(lma_nodes_le0_), + lemma_arr_, 0, lemma_num_, 0); + if (!dt_success) { + free_resource(); + return false; + } + +#ifdef ___DO_STATISTICS___ + stat_print(); +#endif + + // Move the node data and homo data to the DictTrie + dict_trie->root_ = new LmaNodeLE0[lma_nds_used_num_le0_]; + dict_trie->nodes_ge1_ = new LmaNodeGE1[lma_nds_used_num_ge1_]; + size_t lma_idx_num = homo_idx_num_eq1_ + homo_idx_num_gt1_ + top_lmas_num_; + dict_trie->lma_idx_buf_ = new unsigned char[lma_idx_num * kLemmaIdSize]; + assert(NULL != dict_trie->root_); + assert(NULL != dict_trie->lma_idx_buf_); + dict_trie->lma_node_num_le0_ = lma_nds_used_num_le0_; + dict_trie->lma_node_num_ge1_ = lma_nds_used_num_ge1_; + dict_trie->lma_idx_buf_len_ = lma_idx_num * kLemmaIdSize; + dict_trie->top_lmas_num_ = top_lmas_num_; + + memcpy(dict_trie->root_, lma_nodes_le0_, + sizeof(LmaNodeLE0) * lma_nds_used_num_le0_); + memcpy(dict_trie->nodes_ge1_, lma_nodes_ge1_, + sizeof(LmaNodeGE1) * lma_nds_used_num_ge1_); + + for (size_t pos = 0; pos < homo_idx_num_eq1_ + homo_idx_num_gt1_; pos++) { + id_to_charbuf(dict_trie->lma_idx_buf_ + pos * kLemmaIdSize, + homo_idx_buf_[pos]); + } + + for (size_t pos = homo_idx_num_eq1_ + homo_idx_num_gt1_; + pos < lma_idx_num; pos++) { + LemmaIdType idx = + top_lmas_[pos - homo_idx_num_eq1_ - homo_idx_num_gt1_].idx_by_hz; + id_to_charbuf(dict_trie->lma_idx_buf_ + pos * kLemmaIdSize, idx); + } + + if (kPrintDebug0) { + printf("homo_idx_num_eq1_: %d\n", homo_idx_num_eq1_); + printf("homo_idx_num_gt1_: %d\n", homo_idx_num_gt1_); + printf("top_lmas_num_: %d\n", top_lmas_num_); + } + + free_resource(); + + if (kPrintDebug0) { + printf("Building dict succeds\n"); + } + return dt_success; +} + +void DictBuilder::id_to_charbuf(unsigned char *buf, LemmaIdType id) { + if (NULL == buf) return; + for (size_t pos = 0; pos < kLemmaIdSize; pos++) { + (buf)[pos] = (unsigned char)(id >> (pos * 8)); + } +} + +void DictBuilder::set_son_offset(LmaNodeGE1 *node, size_t offset) { + node->son_1st_off_l = static_cast(offset); + node->son_1st_off_h = static_cast(offset >> 16); +} + +void DictBuilder:: set_homo_id_buf_offset(LmaNodeGE1 *node, size_t offset) { + node->homo_idx_buf_off_l = static_cast(offset); + node->homo_idx_buf_off_h = static_cast(offset >> 16); + +} + +// All spelling strings will be converted to upper case, except that +// spellings started with "ZH"/"CH"/"SH" will be converted to +// "Zh"/"Ch"/"Sh" +void DictBuilder::format_spelling_str(char *spl_str) { + if (NULL == spl_str) + return; + + uint16 pos = 0; + while ('\0' != spl_str[pos]) { + if (spl_str[pos] >= 'a' && spl_str[pos] <= 'z') + spl_str[pos] = spl_str[pos] - 'a' + 'A'; + + if (1 == pos && 'H' == spl_str[pos]) { + if ('C' == spl_str[0] || 'S' == spl_str[0] || 'Z' == spl_str[0]) { + spl_str[pos] = 'h'; + } + } + pos++; + } +} + +LemmaIdType DictBuilder::sort_lemmas_by_hz() { + if (NULL == lemma_arr_ || 0 == lemma_num_) + return 0; + + myqsort(lemma_arr_, lemma_num_, sizeof(LemmaEntry), cmp_lemma_entry_hzs); + + lemma_arr_[0].idx_by_hz = 1; + LemmaIdType idx_max = 1; + for (size_t i = 1; i < lemma_num_; i++) { + if (utf16_strcmp(lemma_arr_[i].hanzi_str, lemma_arr_[i-1].hanzi_str)) { + idx_max++; + lemma_arr_[i].idx_by_hz = idx_max; + } else { + idx_max++; + lemma_arr_[i].idx_by_hz = idx_max; + } + } + return idx_max + 1; +} + +size_t DictBuilder::build_scis() { + if (NULL == scis_ || lemma_num_ * kMaxLemmaSize > scis_num_) + return 0; + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + + // This first one is blank, because id 0 is invalid. + scis_[0].freq = 0; + scis_[0].hz = 0; + scis_[0].splid.full_splid = 0; + scis_[0].splid.half_splid = 0; + scis_num_ = 1; + + // Copy the hanzis to the buffer + for (size_t pos = 0; pos < lemma_num_; pos++) { + size_t hz_num = lemma_arr_[pos].hz_str_len; + for (size_t hzpos = 0; hzpos < hz_num; hzpos++) { + scis_[scis_num_].hz = lemma_arr_[pos].hanzi_str[hzpos]; + scis_[scis_num_].splid.full_splid = lemma_arr_[pos].spl_idx_arr[hzpos]; + scis_[scis_num_].splid.half_splid = + spl_trie.full_to_half(scis_[scis_num_].splid.full_splid); + if (1 == hz_num) + scis_[scis_num_].freq = lemma_arr_[pos].freq; + else + scis_[scis_num_].freq = 0.000001; + scis_num_++; + } + } + + myqsort(scis_, scis_num_, sizeof(SingleCharItem), cmp_scis_hz_splid_freq); + + // Remove repeated items + size_t unique_scis_num = 1; + for (size_t pos = 1; pos < scis_num_; pos++) { + if (scis_[pos].hz == scis_[pos - 1].hz && + scis_[pos].splid.full_splid == scis_[pos - 1].splid.full_splid) + continue; + scis_[unique_scis_num] = scis_[pos]; + scis_[unique_scis_num].splid.half_splid = + spl_trie.full_to_half(scis_[pos].splid.full_splid); + unique_scis_num++; + } + + scis_num_ = unique_scis_num; + + // Update the lemma list. + for (size_t pos = 0; pos < lemma_num_; pos++) { + size_t hz_num = lemma_arr_[pos].hz_str_len; + for (size_t hzpos = 0; hzpos < hz_num; hzpos++) { + SingleCharItem key; + key.hz = lemma_arr_[pos].hanzi_str[hzpos]; + key.splid.full_splid = lemma_arr_[pos].spl_idx_arr[hzpos]; + key.splid.half_splid = spl_trie.full_to_half(key.splid.full_splid); + + SingleCharItem *found; + found = static_cast(mybsearch(&key, scis_, + unique_scis_num, + sizeof(SingleCharItem), + cmp_scis_hz_splid)); + + assert(found); + + lemma_arr_[pos].hanzi_scis_ids[hzpos] = + static_cast(found - scis_); + lemma_arr_[pos].spl_idx_arr[hzpos] = found->splid.full_splid; + } + } + + return scis_num_; +} + +bool DictBuilder::construct_subset(void* parent, LemmaEntry* lemma_arr, + size_t item_start, size_t item_end, + size_t level) { + if (level >= kMaxLemmaSize || item_end <= item_start) + return false; + + // 1. Scan for how many sons + size_t parent_son_num = 0; + // LemmaNode *son_1st = NULL; + // parent.num_of_son = 0; + + LemmaEntry *lma_last_start = lemma_arr_ + item_start; + uint16 spl_idx_node = lma_last_start->spl_idx_arr[level]; + + // Scan for how many sons to be allocaed + for (size_t i = item_start + 1; i< item_end; i++) { + LemmaEntry *lma_current = lemma_arr + i; + uint16 spl_idx_current = lma_current->spl_idx_arr[level]; + if (spl_idx_current != spl_idx_node) { + parent_son_num++; + spl_idx_node = spl_idx_current; + } + } + parent_son_num++; + +#ifdef ___DO_STATISTICS___ + // Use to indicate whether all nodes of this layer have no son. + bool allson_noson = true; + + assert(level < kMaxLemmaSize); + if (parent_son_num > max_sonbuf_len_[level]) + max_sonbuf_len_[level] = parent_son_num; + + total_son_num_[level] += parent_son_num; + total_sonbuf_num_[level] += 1; + + if (parent_son_num == 1) + sonbufs_num1_++; + else + sonbufs_numgt1_++; + total_lma_node_num_ += parent_son_num; +#endif + + // 2. Update the parent's information + // Update the parent's son list; + LmaNodeLE0 *son_1st_le0 = NULL; // only one of le0 or ge1 is used + LmaNodeGE1 *son_1st_ge1 = NULL; // only one of le0 or ge1 is used. + if (0 == level) { // the parent is root + (static_cast(parent))->son_1st_off = + lma_nds_used_num_le0_; + son_1st_le0 = lma_nodes_le0_ + lma_nds_used_num_le0_; + lma_nds_used_num_le0_ += parent_son_num; + + assert(parent_son_num <= 65535); + (static_cast(parent))->num_of_son = + static_cast(parent_son_num); + } else if (1 == level) { // the parent is a son of root + (static_cast(parent))->son_1st_off = + lma_nds_used_num_ge1_; + son_1st_ge1 = lma_nodes_ge1_ + lma_nds_used_num_ge1_; + lma_nds_used_num_ge1_ += parent_son_num; + + assert(parent_son_num <= 65535); + (static_cast(parent))->num_of_son = + static_cast(parent_son_num); + } else { + set_son_offset((static_cast(parent)), + lma_nds_used_num_ge1_); + son_1st_ge1 = lma_nodes_ge1_ + lma_nds_used_num_ge1_; + lma_nds_used_num_ge1_ += parent_son_num; + + assert(parent_son_num <= 255); + (static_cast(parent))->num_of_son = + (unsigned char)parent_son_num; + } + + // 3. Now begin to construct the son one by one + size_t son_pos = 0; + + lma_last_start = lemma_arr_ + item_start; + spl_idx_node = lma_last_start->spl_idx_arr[level]; + + size_t homo_num = 0; + if (lma_last_start->spl_idx_arr[level + 1] == 0) + homo_num = 1; + + size_t item_start_next = item_start; + + for (size_t i = item_start + 1; i < item_end; i++) { + LemmaEntry* lma_current = lemma_arr_ + i; + uint16 spl_idx_current = lma_current->spl_idx_arr[level]; + + if (spl_idx_current == spl_idx_node) { + if (lma_current->spl_idx_arr[level + 1] == 0) + homo_num++; + } else { + // Construct a node + LmaNodeLE0 *node_cur_le0 = NULL; // only one of them is valid + LmaNodeGE1 *node_cur_ge1 = NULL; + if (0 == level) { + node_cur_le0 = son_1st_le0 + son_pos; + node_cur_le0->spl_idx = spl_idx_node; + node_cur_le0->homo_idx_buf_off = homo_idx_num_eq1_ + homo_idx_num_gt1_; + node_cur_le0->son_1st_off = 0; + homo_idx_num_eq1_ += homo_num; + } else { + node_cur_ge1 = son_1st_ge1 + son_pos; + node_cur_ge1->spl_idx = spl_idx_node; + + set_homo_id_buf_offset(node_cur_ge1, + (homo_idx_num_eq1_ + homo_idx_num_gt1_)); + set_son_offset(node_cur_ge1, 0); + homo_idx_num_gt1_ += homo_num; + } + + if (homo_num > 0) { + LemmaIdType* idx_buf = homo_idx_buf_ + homo_idx_num_eq1_ + + homo_idx_num_gt1_ - homo_num; + if (0 == level) { + assert(homo_num <= 65535); + node_cur_le0->num_of_homo = static_cast(homo_num); + } else { + assert(homo_num <= 255); + node_cur_ge1->num_of_homo = (unsigned char)homo_num; + } + + for (size_t homo_pos = 0; homo_pos < homo_num; homo_pos++) { + idx_buf[homo_pos] = lemma_arr_[item_start_next + homo_pos].idx_by_hz; + } + +#ifdef ___DO_STATISTICS___ + if (homo_num > max_homobuf_len_[level]) + max_homobuf_len_[level] = homo_num; + + total_homo_num_[level] += homo_num; +#endif + } + + if (i - item_start_next > homo_num) { + void *next_parent; + if (0 == level) + next_parent = static_cast(node_cur_le0); + else + next_parent = static_cast(node_cur_ge1); + construct_subset(next_parent, lemma_arr, + item_start_next + homo_num, i, level + 1); +#ifdef ___DO_STATISTICS___ + + total_node_hasson_[level] += 1; + allson_noson = false; +#endif + } + + // for the next son + lma_last_start = lma_current; + spl_idx_node = spl_idx_current; + item_start_next = i; + homo_num = 0; + if (lma_current->spl_idx_arr[level + 1] == 0) + homo_num = 1; + + son_pos++; + } + } + + // 4. The last one to construct + LmaNodeLE0 *node_cur_le0 = NULL; // only one of them is valid + LmaNodeGE1 *node_cur_ge1 = NULL; + if (0 == level) { + node_cur_le0 = son_1st_le0 + son_pos; + node_cur_le0->spl_idx = spl_idx_node; + node_cur_le0->homo_idx_buf_off = homo_idx_num_eq1_ + homo_idx_num_gt1_; + node_cur_le0->son_1st_off = 0; + homo_idx_num_eq1_ += homo_num; + } else { + node_cur_ge1 = son_1st_ge1 + son_pos; + node_cur_ge1->spl_idx = spl_idx_node; + + set_homo_id_buf_offset(node_cur_ge1, + (homo_idx_num_eq1_ + homo_idx_num_gt1_)); + set_son_offset(node_cur_ge1, 0); + homo_idx_num_gt1_ += homo_num; + } + + if (homo_num > 0) { + LemmaIdType* idx_buf = homo_idx_buf_ + homo_idx_num_eq1_ + + homo_idx_num_gt1_ - homo_num; + if (0 == level) { + assert(homo_num <= 65535); + node_cur_le0->num_of_homo = static_cast(homo_num); + } else { + assert(homo_num <= 255); + node_cur_ge1->num_of_homo = (unsigned char)homo_num; + } + + for (size_t homo_pos = 0; homo_pos < homo_num; homo_pos++) { + idx_buf[homo_pos] = lemma_arr[item_start_next + homo_pos].idx_by_hz; + } + +#ifdef ___DO_STATISTICS___ + if (homo_num > max_homobuf_len_[level]) + max_homobuf_len_[level] = homo_num; + + total_homo_num_[level] += homo_num; +#endif + } + + if (item_end - item_start_next > homo_num) { + void *next_parent; + if (0 == level) + next_parent = static_cast(node_cur_le0); + else + next_parent = static_cast(node_cur_ge1); + construct_subset(next_parent, lemma_arr, + item_start_next + homo_num, item_end, level + 1); +#ifdef ___DO_STATISTICS___ + + total_node_hasson_[level] += 1; + allson_noson = false; +#endif + } + +#ifdef ___DO_STATISTICS___ + if (allson_noson) { + total_sonbuf_allnoson_[level] += 1; + total_node_in_sonbuf_allnoson_[level] += parent_son_num; + } +#endif + + assert(son_pos + 1 == parent_son_num); + return true; +} + +#ifdef ___DO_STATISTICS___ +void DictBuilder::stat_init() { + memset(max_sonbuf_len_, 0, sizeof(size_t) * kMaxLemmaSize); + memset(max_homobuf_len_, 0, sizeof(size_t) * kMaxLemmaSize); + memset(total_son_num_, 0, sizeof(size_t) * kMaxLemmaSize); + memset(total_node_hasson_, 0, sizeof(size_t) * kMaxLemmaSize); + memset(total_sonbuf_num_, 0, sizeof(size_t) * kMaxLemmaSize); + memset(total_sonbuf_allnoson_, 0, sizeof(size_t) * kMaxLemmaSize); + memset(total_node_in_sonbuf_allnoson_, 0, sizeof(size_t) * kMaxLemmaSize); + memset(total_homo_num_, 0, sizeof(size_t) * kMaxLemmaSize); + + sonbufs_num1_ = 0; + sonbufs_numgt1_ = 0; + total_lma_node_num_ = 0; +} + +void DictBuilder::stat_print() { + printf("\n------------STAT INFO-------------\n"); + printf("[root is layer -1]\n"); + printf(".. max_sonbuf_len per layer(from layer 0):\n "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", max_sonbuf_len_[i]); + printf("-, \n"); + + printf(".. max_homobuf_len per layer:\n -, "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", max_homobuf_len_[i]); + printf("\n"); + + printf(".. total_son_num per layer:\n "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", total_son_num_[i]); + printf("-, \n"); + + printf(".. total_node_hasson per layer:\n 1, "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", total_node_hasson_[i]); + printf("\n"); + + printf(".. total_sonbuf_num per layer:\n "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", total_sonbuf_num_[i]); + printf("-, \n"); + + printf(".. total_sonbuf_allnoson per layer:\n "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", total_sonbuf_allnoson_[i]); + printf("-, \n"); + + printf(".. total_node_in_sonbuf_allnoson per layer:\n "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", total_node_in_sonbuf_allnoson_[i]); + printf("-, \n"); + + printf(".. total_homo_num per layer:\n 0, "); + for (size_t i = 0; i < kMaxLemmaSize; i++) + printf("%d, ", total_homo_num_[i]); + printf("\n"); + + printf(".. son buf allocation number with only 1 son: %d\n", sonbufs_num1_); + printf(".. son buf allocation number with more than 1 son: %d\n", + sonbufs_numgt1_); + printf(".. total lemma node number: %d\n", total_lma_node_num_ + 1); +} +#endif // ___DO_STATISTICS___ + +#endif // ___BUILD_MODEL___ +} // namespace ime_pinyin diff --git a/jni/share/dictlist.cpp b/jni/share/dictlist.cpp new file mode 100644 index 0000000..aa7905c --- /dev/null +++ b/jni/share/dictlist.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "../include/dictlist.h" +#include "../include/mystdlib.h" +#include "../include/ngram.h" +#include "../include/searchutility.h" + +namespace ime_pinyin { + +DictList::DictList() { + initialized_ = false; + scis_num_ = 0; + scis_hz_ = NULL; + scis_splid_ = NULL; + buf_ = NULL; + spl_trie_ = SpellingTrie::get_cpinstance(); + + assert(kMaxLemmaSize == 8); + cmp_func_[0] = cmp_hanzis_1; + cmp_func_[1] = cmp_hanzis_2; + cmp_func_[2] = cmp_hanzis_3; + cmp_func_[3] = cmp_hanzis_4; + cmp_func_[4] = cmp_hanzis_5; + cmp_func_[5] = cmp_hanzis_6; + cmp_func_[6] = cmp_hanzis_7; + cmp_func_[7] = cmp_hanzis_8; +} + +DictList::~DictList() { + free_resource(); +} + +bool DictList::alloc_resource(size_t buf_size, size_t scis_num) { + // Allocate memory + buf_ = static_cast(malloc(buf_size * sizeof(char16))); + if (NULL == buf_) + return false; + + scis_num_ = scis_num; + + scis_hz_ = static_cast(malloc(scis_num_ * sizeof(char16))); + if (NULL == scis_hz_) + return false; + + scis_splid_ = static_cast + (malloc(scis_num_ * sizeof(SpellingId))); + + if (NULL == scis_splid_) + return false; + + return true; +} + +void DictList::free_resource() { + if (NULL != buf_) + free(buf_); + buf_ = NULL; + + if (NULL != scis_hz_) + free(scis_hz_); + scis_hz_ = NULL; + + if (NULL != scis_splid_) + free(scis_splid_); + scis_splid_ = NULL; +} + +#ifdef ___BUILD_MODEL___ +bool DictList::init_list(const SingleCharItem *scis, size_t scis_num, + const LemmaEntry *lemma_arr, size_t lemma_num) { + if (NULL == scis || 0 == scis_num || NULL == lemma_arr || 0 == lemma_num) + return false; + + initialized_ = false; + + if (NULL != buf_) + free(buf_); + + // calculate the size + size_t buf_size = calculate_size(lemma_arr, lemma_num); + if (0 == buf_size) + return false; + + if (!alloc_resource(buf_size, scis_num)) + return false; + + fill_scis(scis, scis_num); + + // Copy the related content from the array to inner buffer + fill_list(lemma_arr, lemma_num); + + initialized_ = true; + return true; +} + +size_t DictList::calculate_size(const LemmaEntry* lemma_arr, size_t lemma_num) { + size_t last_hz_len = 0; + size_t list_size = 0; + size_t id_num = 0; + + for (size_t i = 0; i < lemma_num; i++) { + if (0 == i) { + last_hz_len = lemma_arr[i].hz_str_len; + + assert(last_hz_len > 0); + assert(lemma_arr[0].idx_by_hz == 1); + + id_num++; + start_pos_[0] = 0; + start_id_[0] = id_num; + + last_hz_len = 1; + list_size += last_hz_len; + } else { + size_t current_hz_len = lemma_arr[i].hz_str_len; + + assert(current_hz_len >= last_hz_len); + + if (current_hz_len == last_hz_len) { + list_size += current_hz_len; + id_num++; + } else { + for (size_t len = last_hz_len; len < current_hz_len - 1; len++) { + start_pos_[len] = start_pos_[len - 1]; + start_id_[len] = start_id_[len - 1]; + } + + start_pos_[current_hz_len - 1] = list_size; + + id_num++; + start_id_[current_hz_len - 1] = id_num; + + last_hz_len = current_hz_len; + list_size += current_hz_len; + } + } + } + + for (size_t i = last_hz_len; i <= kMaxLemmaSize; i++) { + if (0 == i) { + start_pos_[0] = 0; + start_id_[0] = 1; + } else { + start_pos_[i] = list_size; + start_id_[i] = id_num; + } + } + + return start_pos_[kMaxLemmaSize]; +} + +void DictList::fill_scis(const SingleCharItem *scis, size_t scis_num) { + assert(scis_num_ == scis_num); + + for (size_t pos = 0; pos < scis_num_; pos++) { + scis_hz_[pos] = scis[pos].hz; + scis_splid_[pos] = scis[pos].splid; + } +} + +void DictList::fill_list(const LemmaEntry* lemma_arr, size_t lemma_num) { + size_t current_pos = 0; + + utf16_strncpy(buf_, lemma_arr[0].hanzi_str, + lemma_arr[0].hz_str_len); + + current_pos = lemma_arr[0].hz_str_len; + + size_t id_num = 1; + + for (size_t i = 1; i < lemma_num; i++) { + utf16_strncpy(buf_ + current_pos, lemma_arr[i].hanzi_str, + lemma_arr[i].hz_str_len); + + id_num++; + current_pos += lemma_arr[i].hz_str_len; + } + + assert(current_pos == start_pos_[kMaxLemmaSize]); + assert(id_num == start_id_[kMaxLemmaSize]); +} + +char16* DictList::find_pos2_startedbyhz(char16 hz_char) { + char16 *found_2w = static_cast + (mybsearch(&hz_char, buf_ + start_pos_[1], + (start_pos_[2] - start_pos_[1]) / 2, + sizeof(char16) * 2, cmp_hanzis_1)); + if (NULL == found_2w) + return NULL; + + while (found_2w > buf_ + start_pos_[1] && *found_2w == *(found_2w - 1)) + found_2w -= 2; + + return found_2w; +} +#endif // ___BUILD_MODEL___ + +char16* DictList::find_pos_startedbyhzs(const char16 last_hzs[], + size_t word_len, int (*cmp_func)(const void *, const void *)) { + char16 *found_w = static_cast + (mybsearch(last_hzs, buf_ + start_pos_[word_len - 1], + (start_pos_[word_len] - start_pos_[word_len - 1]) + / word_len, + sizeof(char16) * word_len, cmp_func)); + + if (NULL == found_w) + return NULL; + + while (found_w > buf_ + start_pos_[word_len -1] && + cmp_func(found_w, found_w - word_len) == 0) + found_w -= word_len; + + return found_w; +} + +size_t DictList::predict(const char16 last_hzs[], uint16 hzs_len, + NPredictItem *npre_items, size_t npre_max, + size_t b4_used) { + assert(hzs_len <= kMaxPredictSize && hzs_len > 0); + + // 1. Prepare work + int (*cmp_func)(const void *, const void *) = cmp_func_[hzs_len - 1]; + + NGram& ngram = NGram::get_instance(); + + size_t item_num = 0; + + // 2. Do prediction + for (uint16 pre_len = 1; pre_len <= kMaxPredictSize + 1 - hzs_len; + pre_len++) { + uint16 word_len = hzs_len + pre_len; + char16 *w_buf = find_pos_startedbyhzs(last_hzs, word_len, cmp_func); + if (NULL == w_buf) + continue; + while (w_buf < buf_ + start_pos_[word_len] && + cmp_func(w_buf, last_hzs) == 0 && + item_num < npre_max) { + memset(npre_items + item_num, 0, sizeof(NPredictItem)); + utf16_strncpy(npre_items[item_num].pre_hzs, w_buf + hzs_len, pre_len); + npre_items[item_num].psb = + ngram.get_uni_psb((size_t)(w_buf - buf_ - start_pos_[word_len - 1]) + / word_len + start_id_[word_len - 1]); + npre_items[item_num].his_len = hzs_len; + item_num++; + w_buf += word_len; + } + } + + size_t new_num = 0; + for (size_t i = 0; i < item_num; i++) { + // Try to find it in the existing items + size_t e_pos; + for (e_pos = 1; e_pos <= b4_used; e_pos++) { + if (utf16_strncmp((*(npre_items - e_pos)).pre_hzs, npre_items[i].pre_hzs, + kMaxPredictSize) == 0) + break; + } + if (e_pos <= b4_used) + continue; + + // If not found, append it to the buffer + npre_items[new_num] = npre_items[i]; + new_num++; + } + + return new_num; +} + +uint16 DictList::get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, + uint16 str_max) { + if (!initialized_ || id_lemma >= start_id_[kMaxLemmaSize] || NULL == str_buf + || str_max <= 1) + return 0; + + // Find the range + for (uint16 i = 0; i < kMaxLemmaSize; i++) { + if (i + 1 > str_max - 1) + return 0; + if (start_id_[i] <= id_lemma && start_id_[i + 1] > id_lemma) { + size_t id_span = id_lemma - start_id_[i]; + + uint16 *buf = buf_ + start_pos_[i] + id_span * (i + 1); + for (uint16 len = 0; len <= i; len++) { + str_buf[len] = buf[len]; + } + str_buf[i+1] = (char16)'\0'; + return i + 1; + } + } + return 0; +} + +uint16 DictList::get_splids_for_hanzi(char16 hanzi, uint16 half_splid, + uint16 *splids, uint16 max_splids) { + char16 *hz_found = static_cast + (mybsearch(&hanzi, scis_hz_, scis_num_, sizeof(char16), cmp_hanzis_1)); + assert(NULL != hz_found && hanzi == *hz_found); + + // Move to the first one. + while (hz_found > scis_hz_ && hanzi == *(hz_found - 1)) + hz_found--; + + // First try to found if strict comparison result is not zero. + char16 *hz_f = hz_found; + bool strict = false; + while (hz_f < scis_hz_ + scis_num_ && hanzi == *hz_f) { + uint16 pos = hz_f - scis_hz_; + if (0 == half_splid || scis_splid_[pos].half_splid == half_splid) { + strict = true; + } + hz_f++; + } + + uint16 found_num = 0; + while (hz_found < scis_hz_ + scis_num_ && hanzi == *hz_found) { + uint16 pos = hz_found - scis_hz_; + if (0 == half_splid || + (strict && scis_splid_[pos].half_splid == half_splid) || + (!strict && spl_trie_->half_full_compatible(half_splid, + scis_splid_[pos].full_splid))) { + assert(found_num + 1 < max_splids); + splids[found_num] = scis_splid_[pos].full_splid; + found_num++; + } + hz_found++; + } + + return found_num; +} + +LemmaIdType DictList::get_lemma_id(const char16 *str, uint16 str_len) { + if (NULL == str || str_len > kMaxLemmaSize) + return 0; + + char16 *found = find_pos_startedbyhzs(str, str_len, cmp_func_[str_len - 1]); + if (NULL == found) + return 0; + + assert(found > buf_); + assert(static_cast(found - buf_) >= start_pos_[str_len - 1]); + return static_cast + (start_id_[str_len - 1] + + (found - buf_ - start_pos_[str_len - 1]) / str_len); +} + +void DictList::convert_to_hanzis(char16 *str, uint16 str_len) { + assert(NULL != str); + + for (uint16 str_pos = 0; str_pos < str_len; str_pos++) { + str[str_pos] = scis_hz_[str[str_pos]]; + } +} + +void DictList::convert_to_scis_ids(char16 *str, uint16 str_len) { + assert(NULL != str); + + for (uint16 str_pos = 0; str_pos < str_len; str_pos++) { + str[str_pos] = 0x100; + } +} + +bool DictList::save_list(FILE *fp) { + if (!initialized_ || NULL == fp) + return false; + + if (NULL == buf_ || 0 == start_pos_[kMaxLemmaSize] || + NULL == scis_hz_ || NULL == scis_splid_ || 0 == scis_num_) + return false; + + if (fwrite(&scis_num_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(start_pos_, sizeof(size_t), kMaxLemmaSize + 1, fp) != + kMaxLemmaSize + 1) + return false; + + if (fwrite(start_id_, sizeof(size_t), kMaxLemmaSize + 1, fp) != + kMaxLemmaSize + 1) + return false; + + if (fwrite(scis_hz_, sizeof(char16), scis_num_, fp) != scis_num_) + return false; + + if (fwrite(scis_splid_, sizeof(SpellingId), scis_num_, fp) != scis_num_) + return false; + + if (fwrite(buf_, sizeof(char16), start_pos_[kMaxLemmaSize], fp) != + start_pos_[kMaxLemmaSize]) + return false; + + return true; +} + +bool DictList::load_list(FILE *fp) { + if (NULL == fp) + return false; + + initialized_ = false; + + if (fread(&scis_num_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fread(start_pos_, sizeof(size_t), kMaxLemmaSize + 1, fp) != + kMaxLemmaSize + 1) + return false; + + if (fread(start_id_, sizeof(size_t), kMaxLemmaSize + 1, fp) != + kMaxLemmaSize + 1) + return false; + + free_resource(); + + if (!alloc_resource(start_pos_[kMaxLemmaSize], scis_num_)) + return false; + + if (fread(scis_hz_, sizeof(char16), scis_num_, fp) != scis_num_) + return false; + + if (fread(scis_splid_, sizeof(SpellingId), scis_num_, fp) != scis_num_) + return false; + + if (fread(buf_, sizeof(char16), start_pos_[kMaxLemmaSize], fp) != + start_pos_[kMaxLemmaSize]) + return false; + + initialized_ = true; + return true; +} +} // namespace ime_pinyin diff --git a/jni/share/dicttrie.cpp b/jni/share/dicttrie.cpp new file mode 100644 index 0000000..88b819d --- /dev/null +++ b/jni/share/dicttrie.cpp @@ -0,0 +1,942 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "../include/dicttrie.h" +#include "../include/dictbuilder.h" +#include "../include/lpicache.h" +#include "../include/mystdlib.h" +#include "../include/ngram.h" + +namespace ime_pinyin { + +DictTrie::DictTrie() { + spl_trie_ = SpellingTrie::get_cpinstance(); + + root_ = NULL; + splid_le0_index_ = NULL; + lma_node_num_le0_ = 0; + nodes_ge1_ = NULL; + lma_node_num_ge1_ = 0; + lma_idx_buf_ = NULL; + lma_idx_buf_len_ = 0; + total_lma_num_ = 0; + top_lmas_num_ = 0; + dict_list_ = NULL; + + parsing_marks_ = NULL; + mile_stones_ = NULL; + reset_milestones(0, kFirstValidMileStoneHandle); +} + +DictTrie::~DictTrie() { + free_resource(true); +} + +void DictTrie::free_resource(bool free_dict_list) { + if (NULL != root_) + free(root_); + root_ = NULL; + + if (NULL != splid_le0_index_) + free(splid_le0_index_); + splid_le0_index_ = NULL; + + if (NULL != nodes_ge1_) + free(nodes_ge1_); + nodes_ge1_ = NULL; + + if (NULL != nodes_ge1_) + free(nodes_ge1_); + nodes_ge1_ = NULL; + + if (free_dict_list) { + if (NULL != dict_list_) { + delete dict_list_; + } + dict_list_ = NULL; + } + + if (parsing_marks_) + delete [] parsing_marks_; + parsing_marks_ = NULL; + + if (mile_stones_) + delete [] mile_stones_; + mile_stones_ = NULL; + + reset_milestones(0, kFirstValidMileStoneHandle); +} + +inline size_t DictTrie::get_son_offset(const LmaNodeGE1 *node) { + return ((size_t)node->son_1st_off_l + ((size_t)node->son_1st_off_h << 16)); +} + +inline size_t DictTrie::get_homo_idx_buf_offset(const LmaNodeGE1 *node) { + return ((size_t)node->homo_idx_buf_off_l + + ((size_t)node->homo_idx_buf_off_h << 16)); +} + +inline LemmaIdType DictTrie::get_lemma_id(size_t id_offset) { + LemmaIdType id = 0; + for (uint16 pos = kLemmaIdSize - 1; pos > 0; pos--) + id = (id << 8) + lma_idx_buf_[id_offset * kLemmaIdSize + pos]; + id = (id << 8) + lma_idx_buf_[id_offset * kLemmaIdSize]; + return id; +} + +#ifdef ___BUILD_MODEL___ +bool DictTrie::build_dict(const char* fn_raw, const char* fn_validhzs) { + DictBuilder* dict_builder = new DictBuilder(); + + free_resource(true); + + return dict_builder->build_dict(fn_raw, fn_validhzs, this); +} + +bool DictTrie::save_dict(FILE *fp) { + if (NULL == fp) + return false; + + if (fwrite(&lma_node_num_le0_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(&lma_node_num_ge1_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(&lma_idx_buf_len_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(&top_lmas_num_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(root_, sizeof(LmaNodeLE0), lma_node_num_le0_, fp) + != lma_node_num_le0_) + return false; + + if (fwrite(nodes_ge1_, sizeof(LmaNodeGE1), lma_node_num_ge1_, fp) + != lma_node_num_ge1_) + return false; + + if (fwrite(lma_idx_buf_, sizeof(unsigned char), lma_idx_buf_len_, fp) != + lma_idx_buf_len_) + return false; + + return true; +} + +bool DictTrie::save_dict(const char *filename) { + if (NULL == filename) + return false; + + if (NULL == root_ || NULL == dict_list_) + return false; + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + NGram &ngram = NGram::get_instance(); + + FILE *fp = fopen(filename, "wb"); + if (NULL == fp) + return false; + + if (!spl_trie.save_spl_trie(fp) || !dict_list_->save_list(fp) || + !save_dict(fp) || !ngram.save_ngram(fp)) { + fclose(fp); + return false; + } + + fclose(fp); + return true; +} +#endif // ___BUILD_MODEL___ + +bool DictTrie::load_dict(FILE *fp) { + if (NULL == fp) + return false; + + if (fread(&lma_node_num_le0_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fread(&lma_node_num_ge1_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fread(&lma_idx_buf_len_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fread(&top_lmas_num_, sizeof(size_t), 1, fp) != 1 || + top_lmas_num_ >= lma_idx_buf_len_) + return false; + + free_resource(false); + + root_ = static_cast + (malloc(lma_node_num_le0_ * sizeof(LmaNodeLE0))); + nodes_ge1_ = static_cast + (malloc(lma_node_num_ge1_ * sizeof(LmaNodeGE1))); + lma_idx_buf_ = (unsigned char*)malloc(lma_idx_buf_len_); + total_lma_num_ = lma_idx_buf_len_ / kLemmaIdSize; + + size_t buf_size = SpellingTrie::get_instance().get_spelling_num() + 1; + assert(lma_node_num_le0_ <= buf_size); + splid_le0_index_ = static_cast(malloc(buf_size * sizeof(uint16))); + + // Init the space for parsing. + parsing_marks_ = new ParsingMark[kMaxParsingMark]; + mile_stones_ = new MileStone[kMaxMileStone]; + reset_milestones(0, kFirstValidMileStoneHandle); + + if (NULL == root_ || NULL == nodes_ge1_ || NULL == lma_idx_buf_ || + NULL == splid_le0_index_ || NULL == parsing_marks_ || + NULL == mile_stones_) { + free_resource(false); + return false; + } + + if (fread(root_, sizeof(LmaNodeLE0), lma_node_num_le0_, fp) + != lma_node_num_le0_) + return false; + + if (fread(nodes_ge1_, sizeof(LmaNodeGE1), lma_node_num_ge1_, fp) + != lma_node_num_ge1_) + return false; + + if (fread(lma_idx_buf_, sizeof(unsigned char), lma_idx_buf_len_, fp) != + lma_idx_buf_len_) + return false; + + // The quick index for the first level sons + uint16 last_splid = kFullSplIdStart; + size_t last_pos = 0; + for (size_t i = 1; i < lma_node_num_le0_; i++) { + for (uint16 splid = last_splid; splid < root_[i].spl_idx; splid++) + splid_le0_index_[splid - kFullSplIdStart] = last_pos; + + splid_le0_index_[root_[i].spl_idx - kFullSplIdStart] = + static_cast(i); + last_splid = root_[i].spl_idx; + last_pos = i; + } + + for (uint16 splid = last_splid + 1; + splid < buf_size + kFullSplIdStart; splid++) { + assert(static_cast(splid - kFullSplIdStart) < buf_size); + splid_le0_index_[splid - kFullSplIdStart] = last_pos + 1; + } + + return true; +} + +bool DictTrie::load_dict(const char *filename, LemmaIdType start_id, + LemmaIdType end_id) { + if (NULL == filename || end_id <= start_id) + return false; + + FILE *fp = fopen(filename, "rb"); + if (NULL == fp) + return false; + + free_resource(true); + + dict_list_ = new DictList(); + if (NULL == dict_list_) { + fclose(fp); + return false; + } + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + NGram &ngram = NGram::get_instance(); + + if (!spl_trie.load_spl_trie(fp) || !dict_list_->load_list(fp) || + !load_dict(fp) || !ngram.load_ngram(fp) || + total_lma_num_ > end_id - start_id + 1) { + free_resource(true); + fclose(fp); + return false; + } + + fclose(fp); + return true; +} + +bool DictTrie::load_dict_fd(int sys_fd, long start_offset, + long length, LemmaIdType start_id, + LemmaIdType end_id) { + if (start_offset < 0 || length <= 0 || end_id <= start_id) + return false; + + FILE *fp = fdopen(sys_fd, "rb"); + if (NULL == fp) + return false; + + if (-1 == fseek(fp, start_offset, SEEK_SET)) { + fclose(fp); + return false; + } + + free_resource(true); + + dict_list_ = new DictList(); + if (NULL == dict_list_) { + fclose(fp); + return false; + } + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + NGram &ngram = NGram::get_instance(); + + if (!spl_trie.load_spl_trie(fp) || !dict_list_->load_list(fp) || + !load_dict(fp) || !ngram.load_ngram(fp) || + ftell(fp) < start_offset + length || + total_lma_num_ > end_id - start_id + 1) { + free_resource(true); + fclose(fp); + return false; + } + + fclose(fp); + return true; +} + +size_t DictTrie::fill_lpi_buffer(LmaPsbItem lpi_items[], size_t lpi_max, + LmaNodeLE0 *node) { + size_t lpi_num = 0; + NGram& ngram = NGram::get_instance(); + for (size_t homo = 0; homo < (size_t)node->num_of_homo; homo++) { + lpi_items[lpi_num].id = get_lemma_id(node->homo_idx_buf_off + + homo); + lpi_items[lpi_num].lma_len = 1; + lpi_items[lpi_num].psb = + static_cast(ngram.get_uni_psb(lpi_items[lpi_num].id)); + lpi_num++; + if (lpi_num >= lpi_max) + break; + } + + return lpi_num; +} + +size_t DictTrie::fill_lpi_buffer(LmaPsbItem lpi_items[], size_t lpi_max, + size_t homo_buf_off, LmaNodeGE1 *node, + uint16 lma_len) { + size_t lpi_num = 0; + NGram& ngram = NGram::get_instance(); + for (size_t homo = 0; homo < (size_t)node->num_of_homo; homo++) { + lpi_items[lpi_num].id = get_lemma_id(homo_buf_off + homo); + lpi_items[lpi_num].lma_len = lma_len; + lpi_items[lpi_num].psb = + static_cast(ngram.get_uni_psb(lpi_items[lpi_num].id)); + lpi_num++; + if (lpi_num >= lpi_max) + break; + } + + return lpi_num; +} + +void DictTrie::reset_milestones(uint16 from_step, MileStoneHandle from_handle) { + if (0 == from_step) { + parsing_marks_pos_ = 0; + mile_stones_pos_ = kFirstValidMileStoneHandle; + } else { + if (from_handle > 0 && from_handle < mile_stones_pos_) { + mile_stones_pos_ = from_handle; + + MileStone *mile_stone = mile_stones_ + from_handle; + parsing_marks_pos_ = mile_stone->mark_start; + } + } +} + +MileStoneHandle DictTrie::extend_dict(MileStoneHandle from_handle, + const DictExtPara *dep, + LmaPsbItem *lpi_items, size_t lpi_max, + size_t *lpi_num) { + if (NULL == dep) + return 0; + + // from LmaNodeLE0 (root) to LmaNodeLE0 + if (0 == from_handle) { + assert(0 == dep->splids_extended); + return extend_dict0(from_handle, dep, lpi_items, lpi_max, lpi_num); + } + + // from LmaNodeLE0 to LmaNodeGE1 + if (1 == dep->splids_extended) + return extend_dict1(from_handle, dep, lpi_items, lpi_max, lpi_num); + + // From LmaNodeGE1 to LmaNodeGE1 + return extend_dict2(from_handle, dep, lpi_items, lpi_max, lpi_num); +} + +MileStoneHandle DictTrie::extend_dict0(MileStoneHandle from_handle, + const DictExtPara *dep, + LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num) { + assert(NULL != dep && 0 == from_handle); + *lpi_num = 0; + MileStoneHandle ret_handle = 0; + + uint16 splid = dep->splids[dep->splids_extended]; + uint16 id_start = dep->id_start; + uint16 id_num = dep->id_num; + + LpiCache& lpi_cache = LpiCache::get_instance(); + bool cached = lpi_cache.is_cached(splid); + + // 2. Begin exgtending + // 2.1 Get the LmaPsbItem list + LmaNodeLE0 *node = root_; + size_t son_start = splid_le0_index_[id_start - kFullSplIdStart]; + size_t son_end = splid_le0_index_[id_start + id_num - kFullSplIdStart]; + for (size_t son_pos = son_start; son_pos < son_end; son_pos++) { + assert(1 == node->son_1st_off); + LmaNodeLE0 *son = root_ + son_pos; + assert(son->spl_idx >= id_start && son->spl_idx < id_start + id_num); + + if (!cached && *lpi_num < lpi_max) { + bool need_lpi = true; + if (spl_trie_->is_half_id_yunmu(splid) && son_pos != son_start) + need_lpi = false; + + if (need_lpi) + *lpi_num += fill_lpi_buffer(lpi_items + (*lpi_num), + lpi_max - *lpi_num, son); + } + + // If necessary, fill in a new mile stone. + if (son->spl_idx == id_start) { + if (mile_stones_pos_ < kMaxMileStone && + parsing_marks_pos_ < kMaxParsingMark) { + parsing_marks_[parsing_marks_pos_].node_offset = son_pos; + parsing_marks_[parsing_marks_pos_].node_num = id_num; + mile_stones_[mile_stones_pos_].mark_start = parsing_marks_pos_; + mile_stones_[mile_stones_pos_].mark_num = 1; + ret_handle = mile_stones_pos_; + parsing_marks_pos_++; + mile_stones_pos_++; + } + } + + if (son->spl_idx >= id_start + id_num -1) + break; + } + + // printf("----- parsing marks: %d, mile stone: %d \n", parsing_marks_pos_, + // mile_stones_pos_); + return ret_handle; +} + +MileStoneHandle DictTrie::extend_dict1(MileStoneHandle from_handle, + const DictExtPara *dep, + LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num) { + assert(NULL != dep && from_handle > 0 && from_handle < mile_stones_pos_); + + MileStoneHandle ret_handle = 0; + + // 1. If this is a half Id, get its corresponding full starting Id and + // number of full Id. + size_t ret_val = 0; + + uint16 id_start = dep->id_start; + uint16 id_num = dep->id_num; + + // 2. Begin extending. + MileStone *mile_stone = mile_stones_ + from_handle; + + for (uint16 h_pos = 0; h_pos < mile_stone->mark_num; h_pos++) { + ParsingMark p_mark = parsing_marks_[mile_stone->mark_start + h_pos]; + uint16 ext_num = p_mark.node_num; + for (uint16 ext_pos = 0; ext_pos < ext_num; ext_pos++) { + LmaNodeLE0 *node = root_ + p_mark.node_offset + ext_pos; + size_t found_start = 0; + size_t found_num = 0; + for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; son_pos++) { + assert(node->son_1st_off <= lma_node_num_ge1_); + LmaNodeGE1 *son = nodes_ge1_ + node->son_1st_off + son_pos; + if (son->spl_idx >= id_start + && son->spl_idx < id_start + id_num) { + if (*lpi_num < lpi_max) { + size_t homo_buf_off = get_homo_idx_buf_offset(son); + *lpi_num += fill_lpi_buffer(lpi_items + (*lpi_num), + lpi_max - *lpi_num, homo_buf_off, son, + 2); + } + + // If necessary, fill in the new DTMI + if (0 == found_num) { + found_start = son_pos; + } + found_num++; + } + if (son->spl_idx >= id_start + id_num - 1 || son_pos == + (size_t)node->num_of_son - 1) { + if (found_num > 0) { + if (mile_stones_pos_ < kMaxMileStone && + parsing_marks_pos_ < kMaxParsingMark) { + parsing_marks_[parsing_marks_pos_].node_offset = + node->son_1st_off + found_start; + parsing_marks_[parsing_marks_pos_].node_num = found_num; + if (0 == ret_val) + mile_stones_[mile_stones_pos_].mark_start = + parsing_marks_pos_; + parsing_marks_pos_++; + } + + ret_val++; + } + break; + } // for son_pos + } // for ext_pos + } // for h_pos + } + + if (ret_val > 0) { + mile_stones_[mile_stones_pos_].mark_num = ret_val; + ret_handle = mile_stones_pos_; + mile_stones_pos_++; + ret_val = 1; + } + + // printf("----- parsing marks: %d, mile stone: %d \n", parsing_marks_pos_, + // mile_stones_pos_); + return ret_handle; +} + +MileStoneHandle DictTrie::extend_dict2(MileStoneHandle from_handle, + const DictExtPara *dep, + LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num) { + assert(NULL != dep && from_handle > 0 && from_handle < mile_stones_pos_); + + MileStoneHandle ret_handle = 0; + + // 1. If this is a half Id, get its corresponding full starting Id and + // number of full Id. + size_t ret_val = 0; + + uint16 id_start = dep->id_start; + uint16 id_num = dep->id_num; + + // 2. Begin extending. + MileStone *mile_stone = mile_stones_ + from_handle; + + for (uint16 h_pos = 0; h_pos < mile_stone->mark_num; h_pos++) { + ParsingMark p_mark = parsing_marks_[mile_stone->mark_start + h_pos]; + uint16 ext_num = p_mark.node_num; + for (uint16 ext_pos = 0; ext_pos < ext_num; ext_pos++) { + LmaNodeGE1 *node = nodes_ge1_ + p_mark.node_offset + ext_pos; + size_t found_start = 0; + size_t found_num = 0; + + for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; son_pos++) { + assert(node->son_1st_off_l > 0 || node->son_1st_off_h > 0); + LmaNodeGE1 *son = nodes_ge1_ + get_son_offset(node) + son_pos; + if (son->spl_idx >= id_start + && son->spl_idx < id_start + id_num) { + if (*lpi_num < lpi_max) { + size_t homo_buf_off = get_homo_idx_buf_offset(son); + *lpi_num += fill_lpi_buffer(lpi_items + (*lpi_num), + lpi_max - *lpi_num, homo_buf_off, son, + dep->splids_extended + 1); + } + + // If necessary, fill in the new DTMI + if (0 == found_num) { + found_start = son_pos; + } + found_num++; + } + if (son->spl_idx >= id_start + id_num - 1 || son_pos == + (size_t)node->num_of_son - 1) { + if (found_num > 0) { + if (mile_stones_pos_ < kMaxMileStone && + parsing_marks_pos_ < kMaxParsingMark) { + parsing_marks_[parsing_marks_pos_].node_offset = + get_son_offset(node) + found_start; + parsing_marks_[parsing_marks_pos_].node_num = found_num; + if (0 == ret_val) + mile_stones_[mile_stones_pos_].mark_start = + parsing_marks_pos_; + parsing_marks_pos_++; + } + + ret_val++; + } + break; + } + } // for son_pos + } // for ext_pos + } // for h_pos + + if (ret_val > 0) { + mile_stones_[mile_stones_pos_].mark_num = ret_val; + ret_handle = mile_stones_pos_; + mile_stones_pos_++; + } + + // printf("----- parsing marks: %d, mile stone: %d \n", parsing_marks_pos_, + // mile_stones_pos_); + return ret_handle; +} + +bool DictTrie::try_extend(const uint16 *splids, uint16 splid_num, + LemmaIdType id_lemma) { + if (0 == splid_num || NULL == splids) + return false; + + void *node = root_ + splid_le0_index_[splids[0] - kFullSplIdStart]; + + for (uint16 pos = 1; pos < splid_num; pos++) { + if (1 == pos) { + LmaNodeLE0 *node_le0 = reinterpret_cast(node); + LmaNodeGE1 *node_son; + uint16 son_pos; + for (son_pos = 0; son_pos < static_cast(node_le0->num_of_son); + son_pos++) { + assert(node_le0->son_1st_off <= lma_node_num_ge1_); + node_son = nodes_ge1_ + node_le0->son_1st_off + + son_pos; + if (node_son->spl_idx == splids[pos]) + break; + } + if (son_pos < node_le0->num_of_son) + node = reinterpret_cast(node_son); + else + return false; + } else { + LmaNodeGE1 *node_ge1 = reinterpret_cast(node); + LmaNodeGE1 *node_son; + uint16 son_pos; + for (son_pos = 0; son_pos < static_cast(node_ge1->num_of_son); + son_pos++) { + assert(node_ge1->son_1st_off_l > 0 || node_ge1->son_1st_off_h > 0); + node_son = nodes_ge1_ + get_son_offset(node_ge1) + son_pos; + if (node_son->spl_idx == splids[pos]) + break; + } + if (son_pos < node_ge1->num_of_son) + node = reinterpret_cast(node_son); + else + return false; + } + } + + if (1 == splid_num) { + LmaNodeLE0* node_le0 = reinterpret_cast(node); + size_t num_of_homo = (size_t)node_le0->num_of_homo; + for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { + LemmaIdType id_this = get_lemma_id(node_le0->homo_idx_buf_off + homo_pos); + char16 str[2]; + get_lemma_str(id_this, str, 2); + if (id_this == id_lemma) + return true; + } + } else { + LmaNodeGE1* node_ge1 = reinterpret_cast(node); + size_t num_of_homo = (size_t)node_ge1->num_of_homo; + for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { + size_t node_homo_off = get_homo_idx_buf_offset(node_ge1); + if (get_lemma_id(node_homo_off + homo_pos) == id_lemma) + return true; + } + } + + return false; +} + +size_t DictTrie::get_lpis(const uint16* splid_str, uint16 splid_str_len, + LmaPsbItem* lma_buf, size_t max_lma_buf) { + if (splid_str_len > kMaxLemmaSize) + return 0; + +#define MAX_EXTENDBUF_LEN 200 + + size_t* node_buf1[MAX_EXTENDBUF_LEN]; // use size_t for data alignment + size_t* node_buf2[MAX_EXTENDBUF_LEN]; + LmaNodeLE0** node_fr_le0 = + reinterpret_cast(node_buf1); // Nodes from. + LmaNodeLE0** node_to_le0 = + reinterpret_cast(node_buf2); // Nodes to. + LmaNodeGE1** node_fr_ge1 = NULL; + LmaNodeGE1** node_to_ge1 = NULL; + size_t node_fr_num = 1; + size_t node_to_num = 0; + node_fr_le0[0] = root_; + if (NULL == node_fr_le0[0]) + return 0; + + size_t spl_pos = 0; + + while (spl_pos < splid_str_len) { + uint16 id_num = 1; + uint16 id_start = splid_str[spl_pos]; + // If it is a half id + if (spl_trie_->is_half_id(splid_str[spl_pos])) { + id_num = spl_trie_->half_to_full(splid_str[spl_pos], &id_start); + assert(id_num > 0); + } + + // Extend the nodes + if (0 == spl_pos) { // From LmaNodeLE0 (root) to LmaNodeLE0 nodes + for (size_t node_fr_pos = 0; node_fr_pos < node_fr_num; node_fr_pos++) { + LmaNodeLE0 *node = node_fr_le0[node_fr_pos]; + assert(node == root_ && 1 == node_fr_num); + size_t son_start = splid_le0_index_[id_start - kFullSplIdStart]; + size_t son_end = + splid_le0_index_[id_start + id_num - kFullSplIdStart]; + for (size_t son_pos = son_start; son_pos < son_end; son_pos++) { + assert(1 == node->son_1st_off); + LmaNodeLE0 *node_son = root_ + son_pos; + assert(node_son->spl_idx >= id_start + && node_son->spl_idx < id_start + id_num); + if (node_to_num < MAX_EXTENDBUF_LEN) { + node_to_le0[node_to_num] = node_son; + node_to_num++; + } + // id_start + id_num - 1 is the last one, which has just been + // recorded. + if (node_son->spl_idx >= id_start + id_num - 1) + break; + } + } + + spl_pos++; + if (spl_pos >= splid_str_len || node_to_num == 0) + break; + // Prepare the nodes for next extending + // next time, from LmaNodeLE0 to LmaNodeGE1 + LmaNodeLE0** node_tmp = node_fr_le0; + node_fr_le0 = node_to_le0; + node_to_le0 = NULL; + node_to_ge1 = reinterpret_cast(node_tmp); + } else if (1 == spl_pos) { // From LmaNodeLE0 to LmaNodeGE1 nodes + for (size_t node_fr_pos = 0; node_fr_pos < node_fr_num; node_fr_pos++) { + LmaNodeLE0 *node = node_fr_le0[node_fr_pos]; + for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; + son_pos++) { + assert(node->son_1st_off <= lma_node_num_ge1_); + LmaNodeGE1 *node_son = nodes_ge1_ + node->son_1st_off + + son_pos; + if (node_son->spl_idx >= id_start + && node_son->spl_idx < id_start + id_num) { + if (node_to_num < MAX_EXTENDBUF_LEN) { + node_to_ge1[node_to_num] = node_son; + node_to_num++; + } + } + // id_start + id_num - 1 is the last one, which has just been + // recorded. + if (node_son->spl_idx >= id_start + id_num - 1) + break; + } + } + + spl_pos++; + if (spl_pos >= splid_str_len || node_to_num == 0) + break; + // Prepare the nodes for next extending + // next time, from LmaNodeGE1 to LmaNodeGE1 + node_fr_ge1 = node_to_ge1; + node_to_ge1 = reinterpret_cast(node_fr_le0); + node_fr_le0 = NULL; + node_to_le0 = NULL; + } else { // From LmaNodeGE1 to LmaNodeGE1 nodes + for (size_t node_fr_pos = 0; node_fr_pos < node_fr_num; node_fr_pos++) { + LmaNodeGE1 *node = node_fr_ge1[node_fr_pos]; + for (size_t son_pos = 0; son_pos < (size_t)node->num_of_son; + son_pos++) { + assert(node->son_1st_off_l > 0 || node->son_1st_off_h > 0); + LmaNodeGE1 *node_son = nodes_ge1_ + + get_son_offset(node) + son_pos; + if (node_son->spl_idx >= id_start + && node_son->spl_idx < id_start + id_num) { + if (node_to_num < MAX_EXTENDBUF_LEN) { + node_to_ge1[node_to_num] = node_son; + node_to_num++; + } + } + // id_start + id_num - 1 is the last one, which has just been + // recorded. + if (node_son->spl_idx >= id_start + id_num - 1) + break; + } + } + + spl_pos++; + if (spl_pos >= splid_str_len || node_to_num == 0) + break; + // Prepare the nodes for next extending + // next time, from LmaNodeGE1 to LmaNodeGE1 + LmaNodeGE1 **node_tmp = node_fr_ge1; + node_fr_ge1 = node_to_ge1; + node_to_ge1 = node_tmp; + } + + // The number of node for next extending + node_fr_num = node_to_num; + node_to_num = 0; + } // while + + if (0 == node_to_num) + return 0; + + NGram &ngram = NGram::get_instance(); + size_t lma_num = 0; + + // If the length is 1, and the splid is a one-char Yunmu like 'a', 'o', 'e', + // only those candidates for the full matched one-char id will be returned. + if (1 == splid_str_len && spl_trie_->is_half_id_yunmu(splid_str[0])) + node_to_num = node_to_num > 0 ? 1 : 0; + + for (size_t node_pos = 0; node_pos < node_to_num; node_pos++) { + size_t num_of_homo = 0; + if (spl_pos <= 1) { // Get from LmaNodeLE0 nodes + LmaNodeLE0* node_le0 = node_to_le0[node_pos]; + num_of_homo = (size_t)node_le0->num_of_homo; + for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { + size_t ch_pos = lma_num + homo_pos; + lma_buf[ch_pos].id = + get_lemma_id(node_le0->homo_idx_buf_off + homo_pos); + lma_buf[ch_pos].lma_len = 1; + lma_buf[ch_pos].psb = + static_cast(ngram.get_uni_psb(lma_buf[ch_pos].id)); + + if (lma_num + homo_pos >= max_lma_buf - 1) + break; + } + } else { // Get from LmaNodeGE1 nodes + LmaNodeGE1* node_ge1 = node_to_ge1[node_pos]; + num_of_homo = (size_t)node_ge1->num_of_homo; + for (size_t homo_pos = 0; homo_pos < num_of_homo; homo_pos++) { + size_t ch_pos = lma_num + homo_pos; + size_t node_homo_off = get_homo_idx_buf_offset(node_ge1); + lma_buf[ch_pos].id = get_lemma_id(node_homo_off + homo_pos); + lma_buf[ch_pos].lma_len = splid_str_len; + lma_buf[ch_pos].psb = + static_cast(ngram.get_uni_psb(lma_buf[ch_pos].id)); + + if (lma_num + homo_pos >= max_lma_buf - 1) + break; + } + } + + lma_num += num_of_homo; + if (lma_num >= max_lma_buf) { + lma_num = max_lma_buf; + break; + } + } + return lma_num; +} + +uint16 DictTrie::get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, + uint16 str_max) { + return dict_list_->get_lemma_str(id_lemma, str_buf, str_max); +} + +uint16 DictTrie::get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, + uint16 splids_max, bool arg_valid) { + char16 lma_str[kMaxLemmaSize + 1]; + uint16 lma_len = get_lemma_str(id_lemma, lma_str, kMaxLemmaSize + 1); + assert((!arg_valid && splids_max >= lma_len) || lma_len == splids_max); + + uint16 spl_mtrx[kMaxLemmaSize * 5]; + uint16 spl_start[kMaxLemmaSize + 1]; + spl_start[0] = 0; + uint16 try_num = 1; + + for (uint16 pos = 0; pos < lma_len; pos++) { + uint16 cand_splids_this = 0; + if (arg_valid && spl_trie_->is_full_id(splids[pos])) { + spl_mtrx[spl_start[pos]] = splids[pos]; + cand_splids_this = 1; + } else { + cand_splids_this = dict_list_->get_splids_for_hanzi(lma_str[pos], + arg_valid ? splids[pos] : 0, spl_mtrx + spl_start[pos], + kMaxLemmaSize * 5 - spl_start[pos]); + assert(cand_splids_this > 0); + } + spl_start[pos + 1] = spl_start[pos] + cand_splids_this; + try_num *= cand_splids_this; + } + + for (uint16 try_pos = 0; try_pos < try_num; try_pos++) { + uint16 mod = 1; + for (uint16 pos = 0; pos < lma_len; pos++) { + uint16 radix = spl_start[pos + 1] - spl_start[pos]; + splids[pos] = spl_mtrx[ spl_start[pos] + try_pos / mod % radix]; + mod *= radix; + } + + if (try_extend(splids, lma_len, id_lemma)) + return lma_len; + } + + return 0; +} + +void DictTrie::set_total_lemma_count_of_others(size_t count) { + NGram& ngram = NGram::get_instance(); + ngram.set_total_freq_none_sys(count); +} + +void DictTrie::convert_to_hanzis(char16 *str, uint16 str_len) { + return dict_list_->convert_to_hanzis(str, str_len); +} + +void DictTrie::convert_to_scis_ids(char16 *str, uint16 str_len) { + return dict_list_->convert_to_scis_ids(str, str_len); +} + +LemmaIdType DictTrie::get_lemma_id(const char16 lemma_str[], uint16 lemma_len) { + if (NULL == lemma_str || lemma_len > kMaxLemmaSize) + return 0; + + return dict_list_->get_lemma_id(lemma_str, lemma_len); +} + +size_t DictTrie::predict_top_lmas(size_t his_len, NPredictItem *npre_items, + size_t npre_max, size_t b4_used) { + NGram &ngram = NGram::get_instance(); + + size_t item_num = 0; + size_t top_lmas_id_offset = lma_idx_buf_len_ / kLemmaIdSize - top_lmas_num_; + size_t top_lmas_pos = 0; + while (item_num < npre_max && top_lmas_pos < top_lmas_num_) { + memset(npre_items + item_num, 0, sizeof(NPredictItem)); + LemmaIdType top_lma_id = get_lemma_id(top_lmas_id_offset + top_lmas_pos); + top_lmas_pos += 1; + if (dict_list_->get_lemma_str(top_lma_id, + npre_items[item_num].pre_hzs, + kMaxLemmaSize - 1) == 0) { + continue; + } + npre_items[item_num].psb = ngram.get_uni_psb(top_lma_id); + npre_items[item_num].his_len = his_len; + item_num++; + } + return item_num; +} + +size_t DictTrie::predict(const char16 *last_hzs, uint16 hzs_len, + NPredictItem *npre_items, size_t npre_max, + size_t b4_used) { + return dict_list_->predict(last_hzs, hzs_len, npre_items, npre_max, b4_used); +} +} // namespace ime_pinyin diff --git a/jni/share/lpicache.cpp b/jni/share/lpicache.cpp new file mode 100644 index 0000000..4bb4ca2 --- /dev/null +++ b/jni/share/lpicache.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "../include/lpicache.h" + +namespace ime_pinyin { + +LpiCache* LpiCache::instance_ = NULL; + +LpiCache::LpiCache() { + lpi_cache_ = new LmaPsbItem[kFullSplIdStart * kMaxLpiCachePerId]; + lpi_cache_len_ = new uint16[kFullSplIdStart]; + assert(NULL != lpi_cache_); + assert(NULL != lpi_cache_len_); + for (uint16 id = 0; id < kFullSplIdStart; id++) + lpi_cache_len_[id] = 0; +} + +LpiCache::~LpiCache() { + if (NULL != lpi_cache_) + delete [] lpi_cache_; + + if (NULL != lpi_cache_len_) + delete [] lpi_cache_len_; +} + +LpiCache& LpiCache::get_instance() { + if (NULL == instance_) { + instance_ = new LpiCache(); + assert(NULL != instance_); + } + return *instance_; +} + +bool LpiCache::is_cached(uint16 splid) { + if (splid >= kFullSplIdStart) + return false; + return lpi_cache_len_[splid] != 0; +} + +size_t LpiCache::put_cache(uint16 splid, LmaPsbItem lpi_items[], + size_t lpi_num) { + uint16 num = kMaxLpiCachePerId; + if (num > lpi_num) + num = static_cast(lpi_num); + + LmaPsbItem *lpi_cache_this = lpi_cache_ + splid * kMaxLpiCachePerId; + for (uint16 pos = 0; pos < num; pos++) + lpi_cache_this[pos] = lpi_items[pos]; + + lpi_cache_len_[splid] = num; + return num; +} + +size_t LpiCache::get_cache(uint16 splid, LmaPsbItem lpi_items[], + size_t lpi_max) { + if (lpi_max > lpi_cache_len_[splid]) + lpi_max = lpi_cache_len_[splid]; + + LmaPsbItem *lpi_cache_this = lpi_cache_ + splid * kMaxLpiCachePerId; + for (uint16 pos = 0; pos < lpi_max; pos++) { + lpi_items[pos] = lpi_cache_this[pos]; + } + return lpi_max; +} + +} // namespace ime_pinyin diff --git a/jni/share/matrixsearch.cpp b/jni/share/matrixsearch.cpp new file mode 100644 index 0000000..dd19f59 --- /dev/null +++ b/jni/share/matrixsearch.cpp @@ -0,0 +1,1958 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "../include/lpicache.h" +#include "../include/matrixsearch.h" +#include "../include/mystdlib.h" +#include "../include/ngram.h" +#include "../include/userdict.h" + +namespace ime_pinyin { + +#define PRUMING_SCORE 8000.0 + +MatrixSearch::MatrixSearch() { + inited_ = false; + spl_trie_ = SpellingTrie::get_cpinstance(); + + reset_pointers_to_null(); + + pys_decoded_len_ = 0; + mtrx_nd_pool_used_ = 0; + dmi_pool_used_ = 0; + xi_an_enabled_ = false; + dmi_c_phrase_ = false; + + assert(kMaxSearchSteps > 0); + max_sps_len_ = kMaxSearchSteps - 1; + max_hzs_len_ = kMaxSearchSteps; +} + +MatrixSearch::~MatrixSearch() { + free_resource(); +} + +void MatrixSearch::reset_pointers_to_null() { + dict_trie_ = NULL; + user_dict_ = NULL; + spl_parser_ = NULL; + + share_buf_ = NULL; + + // The following four buffers are used for decoding, and they are based on + // share_buf_, no need to delete them. + mtrx_nd_pool_ = NULL; + dmi_pool_ = NULL; + matrix_ = NULL; + dep_ = NULL; + + // Based on share_buf_, no need to delete them. + npre_items_ = NULL; +} + +bool MatrixSearch::alloc_resource() { + free_resource(); + + dict_trie_ = new DictTrie(); + user_dict_ = static_cast(new UserDict()); + spl_parser_ = new SpellingParser(); + + size_t mtrx_nd_size = sizeof(MatrixNode) * kMtrxNdPoolSize; + mtrx_nd_size = align_to_size_t(mtrx_nd_size) / sizeof(size_t); + size_t dmi_size = sizeof(DictMatchInfo) * kDmiPoolSize; + dmi_size = align_to_size_t(dmi_size) / sizeof(size_t); + size_t matrix_size = sizeof(MatrixRow) * kMaxRowNum; + matrix_size = align_to_size_t(matrix_size) / sizeof(size_t); + size_t dep_size = sizeof(DictExtPara); + dep_size = align_to_size_t(dep_size) / sizeof(size_t); + + // share_buf's size is determined by the buffers for search. + share_buf_ = new size_t[mtrx_nd_size + dmi_size + matrix_size + dep_size]; + + if (NULL == dict_trie_ || NULL == user_dict_ || NULL == spl_parser_ || + NULL == share_buf_) + return false; + + // The buffers for search are based on the share buffer + mtrx_nd_pool_ = reinterpret_cast(share_buf_); + dmi_pool_ = reinterpret_cast(share_buf_ + mtrx_nd_size); + matrix_ = reinterpret_cast(share_buf_ + mtrx_nd_size + dmi_size); + dep_ = reinterpret_cast + (share_buf_ + mtrx_nd_size + dmi_size + matrix_size); + + // The prediction buffer is also based on the share buffer. + npre_items_ = reinterpret_cast(share_buf_); + npre_items_len_ = (mtrx_nd_size + dmi_size + matrix_size + dep_size) * + sizeof(size_t) / sizeof(NPredictItem); + return true; +} + +void MatrixSearch::free_resource() { + if (NULL != dict_trie_) + delete dict_trie_; + + if (NULL != user_dict_) + delete user_dict_; + + if (NULL != spl_parser_) + delete spl_parser_; + + if (NULL != share_buf_) + delete [] share_buf_; + + reset_pointers_to_null(); +} + +bool MatrixSearch::init(const char *fn_sys_dict, const char *fn_usr_dict) { + if (NULL == fn_sys_dict || NULL == fn_usr_dict) + return false; + + if (!alloc_resource()) + return false; + + if (!dict_trie_->load_dict(fn_sys_dict, 1, kSysDictIdEnd)) + return false; + + // If engine fails to load the user dictionary, reset the user dictionary + // to NULL. + if (!user_dict_->load_dict(fn_usr_dict, kUserDictIdStart, kUserDictIdEnd)) { + delete user_dict_; + user_dict_ = NULL; + } else{ + user_dict_->set_total_lemma_count_of_others(NGram::kSysDictTotalFreq); + } + + reset_search0(); + + inited_ = true; + return true; +} + +bool MatrixSearch::init_fd(int sys_fd, long start_offset, long length, + const char *fn_usr_dict) { + if (NULL == fn_usr_dict) + return false; + + if (!alloc_resource()) + return false; + + if (!dict_trie_->load_dict_fd(sys_fd, start_offset, length, 1, kSysDictIdEnd)) + return false; + + if (!user_dict_->load_dict(fn_usr_dict, kUserDictIdStart, kUserDictIdEnd)) { + delete user_dict_; + user_dict_ = NULL; + } else { + user_dict_->set_total_lemma_count_of_others(NGram::kSysDictTotalFreq); + } + + reset_search0(); + + inited_ = true; + return true; +} + +void MatrixSearch::set_max_lens(size_t max_sps_len, size_t max_hzs_len) { + if (0 != max_sps_len) + max_sps_len_ = max_sps_len; + if (0 != max_hzs_len) + max_hzs_len_ = max_hzs_len; +} + +void MatrixSearch::close() { + flush_cache(); + free_resource(); + inited_ = false; +} + +void MatrixSearch::flush_cache() { + if (NULL != user_dict_) + user_dict_->flush_cache(); +} + +void MatrixSearch::set_xi_an_switch(bool xi_an_enabled) { + xi_an_enabled_ = xi_an_enabled; +} + +bool MatrixSearch::get_xi_an_switch() { + return xi_an_enabled_; +} + +bool MatrixSearch::reset_search() { + if (!inited_) + return false; + return reset_search0(); +} + +bool MatrixSearch::reset_search0() { + if (!inited_) + return false; + + pys_decoded_len_ = 0; + mtrx_nd_pool_used_ = 0; + dmi_pool_used_ = 0; + + // Get a MatrixNode from the pool + matrix_[0].mtrx_nd_pos = mtrx_nd_pool_used_; + matrix_[0].mtrx_nd_num = 1; + mtrx_nd_pool_used_ += 1; + + // Update the node, and make it to be a starting node + MatrixNode *node = mtrx_nd_pool_ + matrix_[0].mtrx_nd_pos; + node->id = 0; + node->score = 0; + node->from = NULL; + node->step = 0; + node->dmi_fr = (PoolPosType)-1; + + matrix_[0].dmi_pos = 0; + matrix_[0].dmi_num = 0; + matrix_[0].dmi_has_full_id = 1; + matrix_[0].mtrx_nd_fixed = node; + + lma_start_[0] = 0; + fixed_lmas_ = 0; + spl_start_[0] = 0; + fixed_hzs_ = 0; + + dict_trie_->reset_milestones(0, 0); + if (NULL != user_dict_) + user_dict_->reset_milestones(0, 0); + + return true; +} + +bool MatrixSearch::reset_search(size_t ch_pos, bool clear_fixed_this_step, + bool clear_dmi_this_step, + bool clear_mtrx_this_step) { + if (!inited_ || ch_pos > pys_decoded_len_ || ch_pos >= kMaxRowNum) + return false; + + if (0 == ch_pos) { + reset_search0(); + } else { + // Prepare mile stones of this step to clear. + MileStoneHandle *dict_handles_to_clear = NULL; + if (clear_dmi_this_step && matrix_[ch_pos].dmi_num > 0) { + dict_handles_to_clear = dmi_pool_[matrix_[ch_pos].dmi_pos].dict_handles; + } + + // If there are more steps, and this step is not allowed to clear, find + // milestones of next step. + if (pys_decoded_len_ > ch_pos && !clear_dmi_this_step) { + dict_handles_to_clear = NULL; + if (matrix_[ch_pos + 1].dmi_num > 0) { + dict_handles_to_clear = + dmi_pool_[matrix_[ch_pos + 1].dmi_pos].dict_handles; + } + } + + if (NULL != dict_handles_to_clear) { + dict_trie_->reset_milestones(ch_pos, dict_handles_to_clear[0]); + if (NULL != user_dict_) + user_dict_->reset_milestones(ch_pos, dict_handles_to_clear[1]); + } + + pys_decoded_len_ = ch_pos; + + if (clear_dmi_this_step) { + dmi_pool_used_ = matrix_[ch_pos - 1].dmi_pos + + matrix_[ch_pos - 1].dmi_num; + matrix_[ch_pos].dmi_num = 0; + } else { + dmi_pool_used_ = matrix_[ch_pos].dmi_pos + matrix_[ch_pos].dmi_num; + } + + if (clear_mtrx_this_step) { + mtrx_nd_pool_used_ = matrix_[ch_pos - 1].mtrx_nd_pos + + matrix_[ch_pos - 1].mtrx_nd_num; + matrix_[ch_pos].mtrx_nd_num = 0; + } else { + mtrx_nd_pool_used_ = matrix_[ch_pos].mtrx_nd_pos + + matrix_[ch_pos].mtrx_nd_num; + } + + // Modify fixed_hzs_ + if (fixed_hzs_ > 0 && + ((kLemmaIdComposing != lma_id_[0]) || + (kLemmaIdComposing == lma_id_[0] && + spl_start_[c_phrase_.length] <= ch_pos))) { + size_t fixed_ch_pos = ch_pos; + if (clear_fixed_this_step) + fixed_ch_pos = fixed_ch_pos > 0 ? fixed_ch_pos - 1 : 0; + while (NULL == matrix_[fixed_ch_pos].mtrx_nd_fixed && fixed_ch_pos > 0) + fixed_ch_pos--; + + fixed_lmas_ = 0; + fixed_hzs_ = 0; + if (fixed_ch_pos > 0) { + while (spl_start_[fixed_hzs_] < fixed_ch_pos) + fixed_hzs_++; + assert(spl_start_[fixed_hzs_] == fixed_ch_pos); + + while (lma_start_[fixed_lmas_] < fixed_hzs_) + fixed_lmas_++; + assert(lma_start_[fixed_lmas_] == fixed_hzs_); + } + + // Re-search the Pinyin string for the unlocked lemma + // which was previously fixed. + // + // Prepare mile stones of this step to clear. + MileStoneHandle *dict_handles_to_clear = NULL; + if (clear_dmi_this_step && ch_pos == fixed_ch_pos && + matrix_[fixed_ch_pos].dmi_num > 0) { + dict_handles_to_clear = dmi_pool_[matrix_[fixed_ch_pos].dmi_pos].dict_handles; + } + + // If there are more steps, and this step is not allowed to clear, find + // milestones of next step. + if (pys_decoded_len_ > fixed_ch_pos && !clear_dmi_this_step) { + dict_handles_to_clear = NULL; + if (matrix_[fixed_ch_pos + 1].dmi_num > 0) { + dict_handles_to_clear = + dmi_pool_[matrix_[fixed_ch_pos + 1].dmi_pos].dict_handles; + } + } + + if (NULL != dict_handles_to_clear) { + dict_trie_->reset_milestones(fixed_ch_pos, dict_handles_to_clear[0]); + if (NULL != user_dict_) + user_dict_->reset_milestones(fixed_ch_pos, dict_handles_to_clear[1]); + } + + + pys_decoded_len_ = fixed_ch_pos; + + if (clear_dmi_this_step && ch_pos == fixed_ch_pos) { + dmi_pool_used_ = matrix_[fixed_ch_pos - 1].dmi_pos + + matrix_[fixed_ch_pos - 1].dmi_num; + matrix_[fixed_ch_pos].dmi_num = 0; + } else { + dmi_pool_used_ = matrix_[fixed_ch_pos].dmi_pos + + matrix_[fixed_ch_pos].dmi_num; + } + + if (clear_mtrx_this_step && ch_pos == fixed_ch_pos) { + mtrx_nd_pool_used_ = matrix_[fixed_ch_pos - 1].mtrx_nd_pos + + matrix_[fixed_ch_pos - 1].mtrx_nd_num; + matrix_[fixed_ch_pos].mtrx_nd_num = 0; + } else { + mtrx_nd_pool_used_ = matrix_[fixed_ch_pos].mtrx_nd_pos + + matrix_[fixed_ch_pos].mtrx_nd_num; + } + + for (uint16 re_pos = fixed_ch_pos; re_pos < ch_pos; re_pos++) { + add_char(pys_[re_pos]); + } + } else if (fixed_hzs_ > 0 && kLemmaIdComposing == lma_id_[0]) { + for (uint16 subpos = 0; subpos < c_phrase_.sublma_num; subpos++) { + uint16 splpos_begin = c_phrase_.sublma_start[subpos]; + uint16 splpos_end = c_phrase_.sublma_start[subpos + 1]; + for (uint16 splpos = splpos_begin; splpos < splpos_end; splpos++) { + // If ch_pos is in this spelling + uint16 spl_start = c_phrase_.spl_start[splpos]; + uint16 spl_end = c_phrase_.spl_start[splpos + 1]; + if (ch_pos >= spl_start && ch_pos < spl_end) { + // Clear everything after this position + c_phrase_.chn_str[splpos] = static_cast('\0'); + c_phrase_.sublma_start[subpos + 1] = splpos; + c_phrase_.sublma_num = subpos + 1; + c_phrase_.length = splpos; + + if (splpos == splpos_begin) { + c_phrase_.sublma_num = subpos; + } + } + } + } + + // Extend the composing phrase. + reset_search0(); + dmi_c_phrase_ = true; + uint16 c_py_pos = 0; + while (c_py_pos < spl_start_[c_phrase_.length]) { + bool b_ac_tmp = add_char(pys_[c_py_pos]); + assert(b_ac_tmp); + c_py_pos++; + } + dmi_c_phrase_ = false; + + lma_id_num_ = 1; + fixed_lmas_ = 1; + fixed_lmas_no1_[0] = 0; // A composing string is always modified. + fixed_hzs_ = c_phrase_.length; + lma_start_[1] = fixed_hzs_; + lma_id_[0] = kLemmaIdComposing; + matrix_[spl_start_[fixed_hzs_]].mtrx_nd_fixed = mtrx_nd_pool_ + + matrix_[spl_start_[fixed_hzs_]].mtrx_nd_pos; + } + } + + return true; +} + +void MatrixSearch::del_in_pys(size_t start, size_t len) { + while (start < kMaxRowNum - len && '\0' != pys_[start]) { + pys_[start] = pys_[start + len]; + start++; + } +} + +size_t MatrixSearch::search(const char *py, size_t py_len) { + if (!inited_ || NULL == py) + return 0; + + // If the search Pinyin string is too long, it will be truncated. + if (py_len > kMaxRowNum - 1) + py_len = kMaxRowNum - 1; + + // Compare the new string with the previous one. Find their prefix to + // increase search efficiency. + size_t ch_pos = 0; + for (ch_pos = 0; ch_pos < pys_decoded_len_; ch_pos++) { + if ('\0' == py[ch_pos] || py[ch_pos] != pys_[ch_pos]) + break; + } + + bool clear_fix = true; + if (ch_pos == pys_decoded_len_) + clear_fix = false; + + reset_search(ch_pos, clear_fix, false, false); + + memcpy(pys_ + ch_pos, py + ch_pos, py_len - ch_pos); + pys_[py_len] = '\0'; + + while ('\0' != pys_[ch_pos]) { + if (!add_char(py[ch_pos])) { + pys_decoded_len_ = ch_pos; + break; + } + ch_pos++; + } + + // Get spelling ids and starting positions. + get_spl_start_id(); + + // If there are too many spellings, remove the last letter until the spelling + // number is acceptable. + while (spl_id_num_ > 9) { + py_len--; + reset_search(py_len, false, false, false); + pys_[py_len] = '\0'; + get_spl_start_id(); + } + + prepare_candidates(); + + if (kPrintDebug0) { + printf("--Matrix Node Pool Used: %d\n", mtrx_nd_pool_used_); + printf("--DMI Pool Used: %d\n", dmi_pool_used_); + + if (kPrintDebug1) { + for (PoolPosType pos = 0; pos < dmi_pool_used_; pos++) { + debug_print_dmi(pos, 1); + } + } + } + + return ch_pos; +} + +size_t MatrixSearch::delsearch(size_t pos, bool is_pos_in_splid, + bool clear_fixed_this_step) { + if (!inited_) + return 0; + + size_t reset_pos = pos; + + // Out of range for both Pinyin mode and Spelling id mode. + if (pys_decoded_len_ <= pos) { + del_in_pys(pos, 1); + + reset_pos = pys_decoded_len_; + // Decode the string after the un-decoded position + while ('\0' != pys_[reset_pos]) { + if (!add_char(pys_[reset_pos])) { + pys_decoded_len_ = reset_pos; + break; + } + reset_pos++; + } + get_spl_start_id(); + prepare_candidates(); + return pys_decoded_len_; + } + + // Spelling id mode, but out of range. + if (is_pos_in_splid && spl_id_num_ <= pos) + return pys_decoded_len_; + + // Begin to handle two modes respectively. + // Pinyin mode by default + size_t c_py_len = 0; // The length of composing phrase's Pinyin + size_t del_py_len = 1; + if (!is_pos_in_splid) { + // Pinyin mode is only allowed to delete beyond the fixed lemmas. + if (fixed_lmas_ > 0 && pos < spl_start_[lma_start_[fixed_lmas_]]) + return pys_decoded_len_; + + del_in_pys(pos, 1); + + // If the deleted character is just the one after the last fixed lemma + if (pos == spl_start_[lma_start_[fixed_lmas_]]) { + // If all fixed lemmas have been merged, and the caller of the function + // request to unlock the last fixed lemma. + if (kLemmaIdComposing == lma_id_[0] && clear_fixed_this_step) { + // Unlock the last sub lemma in the composing phrase. Because it is not + // easy to unlock it directly. Instead, we re-decode the modified + // composing phrase. + c_phrase_.sublma_num--; + c_phrase_.length = c_phrase_.sublma_start[c_phrase_.sublma_num]; + reset_pos = spl_start_[c_phrase_.length]; + c_py_len = reset_pos; + } + } + } else { + del_py_len = spl_start_[pos + 1] - spl_start_[pos]; + + del_in_pys(spl_start_[pos], del_py_len); + + if (pos >= lma_start_[fixed_lmas_]) { + c_py_len = 0; + reset_pos = spl_start_[pos + 1] - del_py_len; + } else { + c_py_len = spl_start_[lma_start_[fixed_lmas_]] - del_py_len; + reset_pos = c_py_len; + if (c_py_len > 0) + merge_fixed_lmas(pos); + } + } + + if (c_py_len > 0) { + assert(c_phrase_.length > 0 && c_py_len == + c_phrase_.spl_start[c_phrase_.sublma_start[c_phrase_.sublma_num]]); + // The composing phrase is valid, reset all search space, + // and begin a new search which will only extend the composing + // phrase. + reset_search0(); + + dmi_c_phrase_ = true; + // Extend the composing phrase. + uint16 c_py_pos = 0; + while (c_py_pos < c_py_len) { + bool b_ac_tmp = add_char(pys_[c_py_pos]); + assert(b_ac_tmp); + c_py_pos++; + } + dmi_c_phrase_ = false; + + // Fixd the composing phrase as the first choice. + lma_id_num_ = 1; + fixed_lmas_ = 1; + fixed_lmas_no1_[0] = 0; // A composing string is always modified. + fixed_hzs_ = c_phrase_.length; + lma_start_[1] = fixed_hzs_; + lma_id_[0] = kLemmaIdComposing; + matrix_[spl_start_[fixed_hzs_]].mtrx_nd_fixed = mtrx_nd_pool_ + + matrix_[spl_start_[fixed_hzs_]].mtrx_nd_pos; + } else { + // Reseting search only clear pys_decoded_len_, but the string is kept. + reset_search(reset_pos, clear_fixed_this_step, false, false); + } + + // Decode the string after the delete position. + while ('\0' != pys_[reset_pos]) { + if (!add_char(pys_[reset_pos])) { + pys_decoded_len_ = reset_pos; + break; + } + reset_pos++; + } + + get_spl_start_id(); + prepare_candidates(); + return pys_decoded_len_; +} + +size_t MatrixSearch::get_candidate_num() { + if (!inited_ || 0 == pys_decoded_len_ || + 0 == matrix_[pys_decoded_len_].mtrx_nd_num) + return 0; + + return 1 + lpi_total_; +} + +char16* MatrixSearch::get_candidate(size_t cand_id, char16 *cand_str, + size_t max_len) { + if (!inited_ || 0 == pys_decoded_len_ || NULL == cand_str) + return NULL; + + if (0 == cand_id) { + return get_candidate0(cand_str, max_len, NULL, false); + } else { + cand_id--; + } + + // For this case: the current sentence is a word only, and the user fixed it, + // so the result will be fixed to the sentence space, and + // lpi_total_ will be set to 0. + if (0 == lpi_total_) { + return get_candidate0(cand_str, max_len, NULL, false); + } + + LemmaIdType id = lpi_items_[cand_id].id; + char16 s[kMaxLemmaSize + 1]; + + uint16 s_len = lpi_items_[cand_id].lma_len; + if (s_len > 1) { + s_len = get_lemma_str(id, s, kMaxLemmaSize + 1); + } else { + // For a single character, Hanzi is ready. + s[0] = lpi_items_[cand_id].hanzi; + s[1] = static_cast(0); + } + + if (s_len > 0 && max_len > s_len) { + utf16_strncpy(cand_str, s, s_len); + cand_str[s_len] = (char16)'\0'; + return cand_str; + } + + return NULL; +} + +void MatrixSearch::update_dict_freq() { + if (NULL != user_dict_) { + // Update the total frequency of all lemmas, including system lemmas and + // user dictionary lemmas. + size_t total_freq = user_dict_->get_total_lemma_count(); + dict_trie_->set_total_lemma_count_of_others(total_freq); + } +} + +bool MatrixSearch::add_lma_to_userdict(uint16 lma_fr, uint16 lma_to, + float score) { + if (lma_to - lma_fr <= 1 || NULL == user_dict_) + return false; + + char16 word_str[kMaxLemmaSize + 1]; + uint16 spl_ids[kMaxLemmaSize]; + + uint16 spl_id_fr = 0; + + for (uint16 pos = lma_fr; pos < lma_to; pos++) { + LemmaIdType lma_id = lma_id_[pos]; + if (is_user_lemma(lma_id)) { + user_dict_->update_lemma(lma_id, 1, true); + } + uint16 lma_len = lma_start_[pos + 1] - lma_start_[pos]; + utf16_strncpy(spl_ids + spl_id_fr, spl_id_ + lma_start_[pos], lma_len); + + uint16 tmp = get_lemma_str(lma_id, word_str + spl_id_fr, + kMaxLemmaSize + 1 - spl_id_fr); + assert(tmp == lma_len); + + tmp = get_lemma_splids(lma_id, spl_ids + spl_id_fr, lma_len, true); + if (tmp != lma_len) { + return false; + } + + spl_id_fr += lma_len; + } + + assert(spl_id_fr <= kMaxLemmaSize); + + return user_dict_->put_lemma(static_cast(word_str), spl_ids, + spl_id_fr, 1); +} + +void MatrixSearch::debug_print_dmi(PoolPosType dmi_pos, uint16 nest_level) { + if (dmi_pos >= dmi_pool_used_) return; + + DictMatchInfo *dmi = dmi_pool_ + dmi_pos; + + if (1 == nest_level) { + printf("-----------------%d\'th DMI node begin----------->\n", dmi_pos); + } + if (dmi->dict_level > 1) { + debug_print_dmi(dmi->dmi_fr, nest_level + 1); + } + printf("---%d\n", dmi->dict_level); + printf(" MileStone: %x, %x\n", dmi->dict_handles[0], dmi->dict_handles[1]); + printf(" Spelling : %s, %d\n", SpellingTrie::get_instance(). + get_spelling_str(dmi->spl_id), dmi->spl_id); + printf(" Total Pinyin Len: %d\n", dmi->splstr_len); + if (1 == nest_level) { + printf("<----------------%d\'th DMI node end--------------\n\n", dmi_pos); + } +} + +bool MatrixSearch::try_add_cand0_to_userdict() { + size_t new_cand_num = get_candidate_num(); + if (fixed_hzs_ > 0 && 1 == new_cand_num) { + float score_from = 0; + uint16 lma_id_from = 0; + uint16 pos = 0; + bool modified = false; + while (pos < fixed_lmas_) { + if (lma_start_[pos + 1] - lma_start_[lma_id_from] > + static_cast(kMaxLemmaSize)) { + float score_to_add = + mtrx_nd_pool_[matrix_[spl_start_[lma_start_[pos]]] + .mtrx_nd_pos].score - score_from; + if (modified) { + score_to_add += 1.0; + if (score_to_add > NGram::kMaxScore) { + score_to_add = NGram::kMaxScore; + } + add_lma_to_userdict(lma_id_from, pos, score_to_add); + } + lma_id_from = pos; + score_from += score_to_add; + + // Clear the flag for next user lemma. + modified = false; + } + + if (0 == fixed_lmas_no1_[pos]) { + modified = true; + } + pos++; + } + + // Single-char word is not allowed to add to userdict. + if (lma_start_[pos] - lma_start_[lma_id_from] > 1) { + float score_to_add = + mtrx_nd_pool_[matrix_[spl_start_[lma_start_[pos]]] + .mtrx_nd_pos].score - score_from; + if (modified) { + score_to_add += 1.0; + if (score_to_add > NGram::kMaxScore) { + score_to_add = NGram::kMaxScore; + } + add_lma_to_userdict(lma_id_from, pos, score_to_add); + } + } + } + return true; +} + +// Choose a candidate, and give new candidates for next step. +// If user finishes selection, we will try to communicate with user dictionary +// to add new items or update score of some existing items. +// +// Basic rule: +// 1. If user selects the first choice: +// 1.1. If the first choice is not a sentence, instead, it is a lemma: +// 1.1.1. If the first choice is a user lemma, notify the user +// dictionary that a user lemma is hit, and add occuring count +// by 1. +// 1.1.2. If the first choice is a system lemma, do nothing. +// 1.2. If the first choice is a sentence containing more than one lemma: +// 1.2.1. The whole sentence will be added as a user lemma. If the +// sentence contains user lemmas, -> hit, and add occuring count +// by 1. +size_t MatrixSearch::choose(size_t cand_id) { + if (!inited_ || 0 == pys_decoded_len_) + return 0; + + if (0 == cand_id) { + fixed_hzs_ = spl_id_num_; + matrix_[spl_start_[fixed_hzs_]].mtrx_nd_fixed = mtrx_nd_pool_ + + matrix_[spl_start_[fixed_hzs_]].mtrx_nd_pos; + for (size_t pos = fixed_lmas_; pos < lma_id_num_; pos++) { + fixed_lmas_no1_[pos] = 1; + } + fixed_lmas_ = lma_id_num_; + lpi_total_ = 0; // Clean all other candidates. + + // 1. It is the first choice + if (1 == lma_id_num_) { + // 1.1. The first choice is not a sentence but a lemma + if (is_user_lemma(lma_id_[0])) { + // 1.1.1. The first choice is a user lemma, notify the user dictionary + // that it is hit. + if (NULL != user_dict_) + user_dict_->update_lemma(lma_id_[0], 1, true); + } else { + // 1.1.2. do thing for a system lemma. + } + } else { + // 1.2. The first choice is a sentence. + // 1.2.1 Try to add the whole sentence to user dictionary, the whole + // sentence may be splitted into many items. + if (NULL != user_dict_) { + try_add_cand0_to_userdict(); + } + } + update_dict_freq(); + return 1; + } else { + cand_id--; + } + + // 2. It is not the full sentence candidate. + // Find the length of the candidate. + LemmaIdType id_chosen = lpi_items_[cand_id].id; + LmaScoreType score_chosen = lpi_items_[cand_id].psb; + size_t cand_len = lpi_items_[cand_id].lma_len; + + assert(cand_len > 0); + + // Notify the atom dictionary that this item is hit. + if (is_user_lemma(id_chosen)) { + if (NULL != user_dict_) { + user_dict_->update_lemma(id_chosen, 1, true); + } + update_dict_freq(); + } + + // 3. Fixed the chosen item. + // 3.1 Get the steps number. + size_t step_fr = spl_start_[fixed_hzs_]; + size_t step_to = spl_start_[fixed_hzs_ + cand_len]; + + // 3.2 Save the length of the original string. + size_t pys_decoded_len = pys_decoded_len_; + + // 3.2 Reset the space of the fixed part. + reset_search(step_to, false, false, true); + + // 3.3 For the last character of the fixed part, the previous DMI + // information will be kept, while the MTRX information will be re-extended, + // and only one node will be extended. + matrix_[step_to].mtrx_nd_num = 0; + + LmaPsbItem lpi_item; + lpi_item.psb = score_chosen; + lpi_item.id = id_chosen; + + PoolPosType step_to_dmi_fr = match_dmi(step_to, + spl_id_ + fixed_hzs_, cand_len); + assert(step_to_dmi_fr != static_cast(-1)); + + extend_mtrx_nd(matrix_[step_fr].mtrx_nd_fixed, &lpi_item, 1, + step_to_dmi_fr, step_to); + + matrix_[step_to].mtrx_nd_fixed = mtrx_nd_pool_ + matrix_[step_to].mtrx_nd_pos; + mtrx_nd_pool_used_ = matrix_[step_to].mtrx_nd_pos + + matrix_[step_to].mtrx_nd_num; + + if (id_chosen == lma_id_[fixed_lmas_]) + fixed_lmas_no1_[fixed_lmas_] = 1; + else + fixed_lmas_no1_[fixed_lmas_] = 0; + lma_id_[fixed_lmas_] = id_chosen; + lma_start_[fixed_lmas_ + 1] = lma_start_[fixed_lmas_] + cand_len; + fixed_lmas_++; + fixed_hzs_ = fixed_hzs_ + cand_len; + + while (step_to != pys_decoded_len) { + bool b = add_char(pys_[step_to]); + assert(b); + step_to++; + } + + if (fixed_hzs_ < spl_id_num_) { + prepare_candidates(); + } else { + lpi_total_ = 0; + if (NULL != user_dict_) { + try_add_cand0_to_userdict(); + } + } + + return get_candidate_num(); +} + +size_t MatrixSearch::cancel_last_choice() { + if (!inited_ || 0 == pys_decoded_len_) + return 0; + + size_t step_start = 0; + if (fixed_hzs_ > 0) { + size_t step_end = spl_start_[fixed_hzs_]; + MatrixNode *end_node = matrix_[step_end].mtrx_nd_fixed; + assert(NULL != end_node); + + step_start = end_node->from->step; + + if (step_start > 0) { + DictMatchInfo *dmi = dmi_pool_ + end_node->dmi_fr; + fixed_hzs_ -= dmi->dict_level; + } else { + fixed_hzs_ = 0; + } + + reset_search(step_start, false, false, false); + + while (pys_[step_start] != '\0') { + bool b = add_char(pys_[step_start]); + assert(b); + step_start++; + } + + prepare_candidates(); + } + return get_candidate_num(); +} + +size_t MatrixSearch::get_fixedlen() { + if (!inited_ || 0 == pys_decoded_len_) + return 0; + return fixed_hzs_; +} + +bool MatrixSearch::prepare_add_char(char ch) { + if (pys_decoded_len_ >= kMaxRowNum - 1 || + (!spl_parser_->is_valid_to_parse(ch) && ch != '\'')) + return false; + + if (dmi_pool_used_ >= kDmiPoolSize) return false; + + pys_[pys_decoded_len_] = ch; + pys_decoded_len_++; + + MatrixRow *mtrx_this_row = matrix_ + pys_decoded_len_; + mtrx_this_row->mtrx_nd_pos = mtrx_nd_pool_used_; + mtrx_this_row->mtrx_nd_num = 0; + mtrx_this_row->dmi_pos = dmi_pool_used_; + mtrx_this_row->dmi_num = 0; + mtrx_this_row->dmi_has_full_id = 0; + + return true; +} + +bool MatrixSearch::is_split_at(uint16 pos) { + return !spl_parser_->is_valid_to_parse(pys_[pos - 1]); +} + +void MatrixSearch::fill_dmi(DictMatchInfo *dmi, MileStoneHandle *handles, + PoolPosType dmi_fr, uint16 spl_id, + uint16 node_num, unsigned char dict_level, + bool splid_end_split, unsigned char splstr_len, + unsigned char all_full_id) { + dmi->dict_handles[0] = handles[0]; + dmi->dict_handles[1] = handles[1]; + dmi->dmi_fr = dmi_fr; + dmi->spl_id = spl_id; + dmi->dict_level = dict_level; + dmi->splid_end_split = splid_end_split ? 1 : 0; + dmi->splstr_len = splstr_len; + dmi->all_full_id = all_full_id; + dmi->c_phrase = 0; +} + +bool MatrixSearch::add_char(char ch) { + if (!prepare_add_char(ch)) + return false; + return add_char_qwerty(); +} + +bool MatrixSearch::add_char_qwerty() { + matrix_[pys_decoded_len_].mtrx_nd_num = 0; + + bool spl_matched = false; + uint16 longest_ext = 0; + // Extend the search matrix, from the oldest unfixed row. ext_len means + // extending length. + for (uint16 ext_len = kMaxPinyinSize + 1; ext_len > 0; ext_len--) { + if (ext_len > pys_decoded_len_ - spl_start_[fixed_hzs_]) + continue; + + // Refer to the declaration of the variable dmi_has_full_id for the + // explanation of this piece of code. In one word, it is used to prevent + // from the unwise extending of "shoud ou" but allow the reasonable + // extending of "heng ao", "lang a", etc. + if (ext_len > 1 && 0 != longest_ext && + 0 == matrix_[pys_decoded_len_ - ext_len].dmi_has_full_id) { + if (xi_an_enabled_) + continue; + else + break; + } + + uint16 oldrow = pys_decoded_len_ - ext_len; + + // 0. If that row is before the last fixed step, ignore. + if (spl_start_[fixed_hzs_] > oldrow) + continue; + + // 1. Check if that old row has valid MatrixNode. If no, means that row is + // not a boundary, either a word boundary or a spelling boundary. + // If it is for extending composing phrase, it's OK to ignore the 0. + if (0 == matrix_[oldrow].mtrx_nd_num && !dmi_c_phrase_) + continue; + + // 2. Get spelling id(s) for the last ext_len chars. + uint16 spl_idx; + bool is_pre = false; + spl_idx = spl_parser_->get_splid_by_str(pys_ + oldrow, + ext_len, &is_pre); + if (is_pre) + spl_matched = true; + + if (0 == spl_idx) + continue; + + bool splid_end_split = is_split_at(oldrow + ext_len); + + // 3. Extend the DMI nodes of that old row + // + 1 is to extend an extra node from the root + for (PoolPosType dmi_pos = matrix_[oldrow].dmi_pos; + dmi_pos < matrix_[oldrow].dmi_pos + matrix_[oldrow].dmi_num + 1; + dmi_pos++) { + DictMatchInfo *dmi = dmi_pool_ + dmi_pos; + if (dmi_pos == matrix_[oldrow].dmi_pos + matrix_[oldrow].dmi_num) { + dmi = NULL; // The last one, NULL means extending from the root. + } else { + // If the dmi is covered by the fixed arrange, ignore it. + if (fixed_hzs_ > 0 && + pys_decoded_len_ - ext_len - dmi->splstr_len < + spl_start_[fixed_hzs_]) { + continue; + } + // If it is not in mode for composing phrase, and the source DMI node + // is marked for composing phrase, ignore this node. + if (dmi->c_phrase != 0 && !dmi_c_phrase_) { + continue; + } + } + + // For example, if "gao" is extended, "g ao" is not allowed. + // or "zh" has been passed, "z h" is not allowed. + // Both word and word-connection will be prevented. + if (longest_ext > ext_len) { + if (NULL == dmi && 0 == matrix_[oldrow].dmi_has_full_id) { + continue; + } + + // "z h" is not allowed. + if (NULL != dmi && spl_trie_->is_half_id(dmi->spl_id)) { + continue; + } + } + + dep_->splids_extended = 0; + if (NULL != dmi) { + uint16 prev_ids_num = dmi->dict_level; + if ((!dmi_c_phrase_ && prev_ids_num >= kMaxLemmaSize) || + (dmi_c_phrase_ && prev_ids_num >= kMaxRowNum)) { + continue; + } + + DictMatchInfo *d = dmi; + while (d) { + dep_->splids[--prev_ids_num] = d->spl_id; + if ((PoolPosType)-1 == d->dmi_fr) + break; + d = dmi_pool_ + d->dmi_fr; + } + assert(0 == prev_ids_num); + dep_->splids_extended = dmi->dict_level; + } + dep_->splids[dep_->splids_extended] = spl_idx; + dep_->ext_len = ext_len; + dep_->splid_end_split = splid_end_split; + + dep_->id_num = 1; + dep_->id_start = spl_idx; + if (spl_trie_->is_half_id(spl_idx)) { + // Get the full id list + dep_->id_num = spl_trie_->half_to_full(spl_idx, &(dep_->id_start)); + assert(dep_->id_num > 0); + } + + uint16 new_dmi_num; + + new_dmi_num = extend_dmi(dep_, dmi); + + if (new_dmi_num > 0) { + if (dmi_c_phrase_) { + dmi_pool_[dmi_pool_used_].c_phrase = 1; + } + matrix_[pys_decoded_len_].dmi_num += new_dmi_num; + dmi_pool_used_ += new_dmi_num; + + if (!spl_trie_->is_half_id(spl_idx)) + matrix_[pys_decoded_len_].dmi_has_full_id = 1; + } + + // If get candiate lemmas, try to extend the path + if (lpi_total_ > 0) { + uint16 fr_row; + if (NULL == dmi) { + fr_row = oldrow; + } else { + assert(oldrow >= dmi->splstr_len); + fr_row = oldrow - dmi->splstr_len; + } + for (PoolPosType mtrx_nd_pos = matrix_[fr_row].mtrx_nd_pos; + mtrx_nd_pos < matrix_[fr_row].mtrx_nd_pos + + matrix_[fr_row].mtrx_nd_num; + mtrx_nd_pos++) { + MatrixNode *mtrx_nd = mtrx_nd_pool_ + mtrx_nd_pos; + + extend_mtrx_nd(mtrx_nd, lpi_items_, lpi_total_, + dmi_pool_used_ - new_dmi_num, pys_decoded_len_); + if (longest_ext == 0) + longest_ext = ext_len; + } + } + } // for dmi_pos + } // for ext_len + mtrx_nd_pool_used_ += matrix_[pys_decoded_len_].mtrx_nd_num; + + if (dmi_c_phrase_) + return true; + + return (matrix_[pys_decoded_len_].mtrx_nd_num != 0 || spl_matched); +} + +void MatrixSearch::prepare_candidates() { + // Get candiates from the first un-fixed step. + uint16 lma_size_max = kMaxLemmaSize; + if (lma_size_max > spl_id_num_ - fixed_hzs_) + lma_size_max = spl_id_num_ - fixed_hzs_; + + uint16 lma_size = lma_size_max; + + // If the full sentense candidate's unfixed part may be the same with a normal + // lemma. Remove the lemma candidate in this case. + char16 fullsent[kMaxLemmaSize + 1]; + char16 *pfullsent = NULL; + uint16 sent_len; + pfullsent = get_candidate0(fullsent, kMaxLemmaSize + 1, &sent_len, true); + + // If the unfixed part contains more than one ids, it is not necessary to + // check whether a lemma's string is the same to the unfixed part of the full + // sentence candidate, so, set it to NULL; + if (sent_len > kMaxLemmaSize) + pfullsent = NULL; + + lpi_total_ = 0; + size_t lpi_num_full_match = 0; // Number of items which are fully-matched. + while (lma_size > 0) { + size_t lma_num; + lma_num = get_lpis(spl_id_ + fixed_hzs_, lma_size, + lpi_items_ + lpi_total_, + size_t(kMaxLmaPsbItems - lpi_total_), + pfullsent, lma_size == lma_size_max); + + if (lma_num > 0) { + lpi_total_ += lma_num; + // For next lemma candidates which are not the longest, it is not + // necessary to compare with the full sentence candiate. + pfullsent = NULL; + } + if (lma_size == lma_size_max) { + lpi_num_full_match = lpi_total_; + } + lma_size--; + } + + // Sort those partially-matched items by their unified scores. + myqsort(lpi_items_ + lpi_num_full_match, lpi_total_ - lpi_num_full_match, + sizeof(LmaPsbItem), cmp_lpi_with_unified_psb); + + if (kPrintDebug0) { + printf("-----Prepare candidates, score:\n"); + for (size_t a = 0; a < lpi_total_; a++) { + printf("[%03d]%d ", a, lpi_items_[a].psb); + if ((a + 1) % 6 == 0) printf("\n"); + } + printf("\n"); + } + + if (kPrintDebug0) { + printf("--- lpi_total_ = %d\n", lpi_total_); + } +} + +const char* MatrixSearch::get_pystr(size_t *decoded_len) { + if (!inited_ || NULL == decoded_len) + return NULL; + + *decoded_len = pys_decoded_len_; + return pys_; +} + +void MatrixSearch::merge_fixed_lmas(size_t del_spl_pos) { + if (fixed_lmas_ == 0) + return; + // Update spelling segmentation information first. + spl_id_num_ -= 1; + uint16 del_py_len = spl_start_[del_spl_pos + 1] - spl_start_[del_spl_pos]; + for (size_t pos = del_spl_pos; pos <= spl_id_num_; pos++) { + spl_start_[pos] = spl_start_[pos + 1] - del_py_len; + if (pos == spl_id_num_) + break; + spl_id_[pos] = spl_id_[pos + 1]; + } + + // Begin to merge. + uint16 phrase_len = 0; + + // Update the spelling ids to the composing phrase. + // We need to convert these ids into full id in the future. + memcpy(c_phrase_.spl_ids, spl_id_, spl_id_num_ * sizeof(uint16)); + memcpy(c_phrase_.spl_start, spl_start_, (spl_id_num_ + 1) * sizeof(uint16)); + + // If composing phrase has not been created, first merge all fixed + // lemmas into a composing phrase without deletion. + if (fixed_lmas_ > 1 || kLemmaIdComposing != lma_id_[0]) { + uint16 bp = 1; // Begin position of real fixed lemmas. + // There is no existing composing phrase. + if (kLemmaIdComposing != lma_id_[0]) { + c_phrase_.sublma_num = 0; + bp = 0; + } + + uint16 sub_num = c_phrase_.sublma_num; + for (uint16 pos = bp; pos <= fixed_lmas_; pos++) { + c_phrase_.sublma_start[sub_num + pos - bp] = lma_start_[pos]; + if (lma_start_[pos] > del_spl_pos) { + c_phrase_.sublma_start[sub_num + pos - bp] -= 1; + } + + if (pos == fixed_lmas_) + break; + + uint16 lma_len; + char16 *lma_str = c_phrase_.chn_str + + c_phrase_.sublma_start[sub_num] + phrase_len; + + lma_len = get_lemma_str(lma_id_[pos], lma_str, kMaxRowNum - phrase_len); + assert(lma_len == lma_start_[pos + 1] - lma_start_[pos]); + phrase_len += lma_len; + } + assert(phrase_len == lma_start_[fixed_lmas_]); + c_phrase_.length = phrase_len; // will be deleted by 1 + c_phrase_.sublma_num += fixed_lmas_ - bp; + } else { + for (uint16 pos = 0; pos <= c_phrase_.sublma_num; pos++) { + if (c_phrase_.sublma_start[pos] > del_spl_pos) { + c_phrase_.sublma_start[pos] -= 1; + } + } + phrase_len = c_phrase_.length; + } + + assert(phrase_len > 0); + if (1 == phrase_len) { + // After the only one is deleted, nothing will be left. + fixed_lmas_ = 0; + return; + } + + // Delete the Chinese character in the merged phrase. + // The corresponding elements in spl_ids and spl_start of the + // phrase have been deleted. + char16 *chn_str = c_phrase_.chn_str + del_spl_pos; + for (uint16 pos = 0; + pos < c_phrase_.sublma_start[c_phrase_.sublma_num] - del_spl_pos; + pos++) { + chn_str[pos] = chn_str[pos + 1]; + } + c_phrase_.length -= 1; + + // If the deleted spelling id is in a sub lemma which contains more than + // one id, del_a_sub will be false; but if the deleted id is in a sub lemma + // which only contains 1 id, the whole sub lemma needs to be deleted, so + // del_a_sub will be true. + bool del_a_sub = false; + for (uint16 pos = 1; pos <= c_phrase_.sublma_num; pos++) { + if (c_phrase_.sublma_start[pos - 1] == + c_phrase_.sublma_start[pos]) { + del_a_sub = true; + } + if (del_a_sub) { + c_phrase_.sublma_start[pos - 1] = + c_phrase_.sublma_start[pos]; + } + } + if (del_a_sub) + c_phrase_.sublma_num -= 1; + + return; +} + +void MatrixSearch::get_spl_start_id() { + lma_id_num_ = 0; + lma_start_[0] = 0; + + spl_id_num_ = 0; + spl_start_[0] = 0; + if (!inited_ || 0 == pys_decoded_len_ || + 0 == matrix_[pys_decoded_len_].mtrx_nd_num) + return; + + // Calculate number of lemmas and spellings + // Only scan those part which is not fixed. + lma_id_num_ = fixed_lmas_; + spl_id_num_ = fixed_hzs_; + + MatrixNode *mtrx_nd = mtrx_nd_pool_ + matrix_[pys_decoded_len_].mtrx_nd_pos; + while (mtrx_nd != mtrx_nd_pool_) { + if (fixed_hzs_ > 0) { + if (mtrx_nd->step <= spl_start_[fixed_hzs_]) + break; + } + + // Update the spelling segamentation information + unsigned char word_splstr_len = 0; + PoolPosType dmi_fr = mtrx_nd->dmi_fr; + if ((PoolPosType)-1 != dmi_fr) + word_splstr_len = dmi_pool_[dmi_fr].splstr_len; + + while ((PoolPosType)-1 != dmi_fr) { + spl_start_[spl_id_num_ + 1] = mtrx_nd->step - + (word_splstr_len - dmi_pool_[dmi_fr].splstr_len); + spl_id_[spl_id_num_] = dmi_pool_[dmi_fr].spl_id; + spl_id_num_++; + dmi_fr = dmi_pool_[dmi_fr].dmi_fr; + } + + // Update the lemma segmentation information + lma_start_[lma_id_num_ + 1] = spl_id_num_; + lma_id_[lma_id_num_] = mtrx_nd->id; + lma_id_num_++; + + mtrx_nd = mtrx_nd->from; + } + + // Reverse the result of spelling info + for (size_t pos = fixed_hzs_; + pos < fixed_hzs_ + (spl_id_num_ - fixed_hzs_ + 1) / 2; pos++) { + if (spl_id_num_ + fixed_hzs_ - pos != pos + 1) { + spl_start_[pos + 1] ^= spl_start_[spl_id_num_ - pos + fixed_hzs_]; + spl_start_[spl_id_num_ - pos + fixed_hzs_] ^= spl_start_[pos + 1]; + spl_start_[pos + 1] ^= spl_start_[spl_id_num_ - pos + fixed_hzs_]; + + spl_id_[pos] ^= spl_id_[spl_id_num_ + fixed_hzs_ - pos - 1]; + spl_id_[spl_id_num_ + fixed_hzs_- pos - 1] ^= spl_id_[pos]; + spl_id_[pos] ^= spl_id_[spl_id_num_ + fixed_hzs_- pos - 1]; + } + } + + // Reverse the result of lemma info + for (size_t pos = fixed_lmas_; + pos < fixed_lmas_ + (lma_id_num_ - fixed_lmas_ + 1) / 2; pos++) { + assert(lma_id_num_ + fixed_lmas_ - pos - 1 >= pos); + + if (lma_id_num_ + fixed_lmas_ - pos > pos + 1) { + lma_start_[pos + 1] ^= lma_start_[lma_id_num_ - pos + fixed_lmas_]; + lma_start_[lma_id_num_ - pos + fixed_lmas_] ^= lma_start_[pos + 1]; + lma_start_[pos + 1] ^= lma_start_[lma_id_num_ - pos + fixed_lmas_]; + + lma_id_[pos] ^= lma_id_[lma_id_num_ - 1 - pos + fixed_lmas_]; + lma_id_[lma_id_num_ - 1 - pos + fixed_lmas_] ^= lma_id_[pos]; + lma_id_[pos] ^= lma_id_[lma_id_num_ - 1 - pos + fixed_lmas_]; + } + } + + for (size_t pos = fixed_lmas_ + 1; pos <= lma_id_num_; pos++) { + if (pos < lma_id_num_) + lma_start_[pos] = lma_start_[pos - 1] + + (lma_start_[pos] - lma_start_[pos + 1]); + else + lma_start_[pos] = lma_start_[pos - 1] + lma_start_[pos] - + lma_start_[fixed_lmas_]; + } + + // Find the last fixed position + fixed_hzs_ = 0; + for (size_t pos = spl_id_num_; pos > 0; pos--) { + if (NULL != matrix_[spl_start_[pos]].mtrx_nd_fixed) { + fixed_hzs_ = pos; + break; + } + } + + return; +} + +size_t MatrixSearch::get_spl_start(const uint16 *&spl_start) { + get_spl_start_id(); + spl_start = spl_start_; + return spl_id_num_; +} + +size_t MatrixSearch::extend_dmi(DictExtPara *dep, DictMatchInfo *dmi_s) { + if (dmi_pool_used_ >= kDmiPoolSize) return 0; + + if (dmi_c_phrase_) + return extend_dmi_c(dep, dmi_s); + + LpiCache& lpi_cache = LpiCache::get_instance(); + uint16 splid = dep->splids[dep->splids_extended]; + + bool cached = false; + if (0 == dep->splids_extended) + cached = lpi_cache.is_cached(splid); + + // 1. If this is a half Id, get its corresponding full starting Id and + // number of full Id. + size_t ret_val = 0; + PoolPosType mtrx_dmi_fr = (PoolPosType)-1; // From which dmi node + + lpi_total_ = 0; + + MileStoneHandle from_h[3]; + from_h[0] = 0; + from_h[1] = 0; + + if (0 != dep->splids_extended) { + from_h[0] = dmi_s->dict_handles[0]; + from_h[1] = dmi_s->dict_handles[1]; + } + + // 2. Begin exgtending in the system dictionary + size_t lpi_num = 0; + MileStoneHandle handles[2]; + handles[0] = handles[1] = 0; + if (from_h[0] > 0 || NULL == dmi_s) { + handles[0] = dict_trie_->extend_dict(from_h[0], dep, lpi_items_, + kMaxLmaPsbItems, &lpi_num); + } + if (handles[0] > 0) + lpi_total_ = lpi_num; + + if (NULL == dmi_s) { // from root + assert(0 != handles[0]); + mtrx_dmi_fr = dmi_pool_used_; + } + + // 3. Begin extending in the user dictionary + if (NULL != user_dict_ && (from_h[1] > 0 || NULL == dmi_s)) { + handles[1] = user_dict_->extend_dict(from_h[1], dep, + lpi_items_ + lpi_total_, + kMaxLmaPsbItems - lpi_total_, + &lpi_num); + if (handles[1] > 0) { + if (kPrintDebug0) { + for (size_t t = 0; t < lpi_num; t++) { + printf("--Extend in user dict: uid:%d uscore:%d\n", lpi_items_[lpi_total_ + t].id, + lpi_items_[lpi_total_ + t].psb); + } + } + lpi_total_ += lpi_num; + } + } + + if (0 != handles[0] || 0 != handles[1]) { + if (dmi_pool_used_ >= kDmiPoolSize) return 0; + + DictMatchInfo *dmi_add = dmi_pool_ + dmi_pool_used_; + if (NULL == dmi_s) { + fill_dmi(dmi_add, handles, + (PoolPosType)-1, splid, + 1, 1, dep->splid_end_split, dep->ext_len, + spl_trie_->is_half_id(splid) ? 0 : 1); + } else { + fill_dmi(dmi_add, handles, + dmi_s - dmi_pool_, splid, 1, + dmi_s->dict_level + 1, dep->splid_end_split, + dmi_s->splstr_len + dep->ext_len, + spl_trie_->is_half_id(splid) ? 0 : dmi_s->all_full_id); + } + + ret_val = 1; + } + + if (!cached) { + if (0 == lpi_total_) + return ret_val; + + if (kPrintDebug0) { + printf("--- lpi_total_ = %d\n", lpi_total_); + } + + myqsort(lpi_items_, lpi_total_, sizeof(LmaPsbItem), cmp_lpi_with_psb); + if (NULL == dmi_s && spl_trie_->is_half_id(splid)) + lpi_total_ = lpi_cache.put_cache(splid, lpi_items_, lpi_total_); + } else { + assert(spl_trie_->is_half_id(splid)); + lpi_total_ = lpi_cache.get_cache(splid, lpi_items_, kMaxLmaPsbItems); + } + + return ret_val; +} + +size_t MatrixSearch::extend_dmi_c(DictExtPara *dep, DictMatchInfo *dmi_s) { + lpi_total_ = 0; + + uint16 pos = dep->splids_extended; + assert(dmi_c_phrase_); + if (pos >= c_phrase_.length) + return 0; + + uint16 splid = dep->splids[pos]; + if (splid == c_phrase_.spl_ids[pos]) { + DictMatchInfo *dmi_add = dmi_pool_ + dmi_pool_used_; + MileStoneHandle handles[2]; // Actually never used. + if (NULL == dmi_s) + fill_dmi(dmi_add, handles, + (PoolPosType)-1, splid, + 1, 1, dep->splid_end_split, dep->ext_len, + spl_trie_->is_half_id(splid) ? 0 : 1); + else + fill_dmi(dmi_add, handles, + dmi_s - dmi_pool_, splid, 1, + dmi_s->dict_level + 1, dep->splid_end_split, + dmi_s->splstr_len + dep->ext_len, + spl_trie_->is_half_id(splid) ? 0 : dmi_s->all_full_id); + + if (pos == c_phrase_.length - 1) { + lpi_items_[0].id = kLemmaIdComposing; + lpi_items_[0].psb = 0; // 0 is bigger than normal lemma score. + lpi_total_ = 1; + } + return 1; + } + return 0; +} + +size_t MatrixSearch::extend_mtrx_nd(MatrixNode *mtrx_nd, LmaPsbItem lpi_items[], + size_t lpi_num, PoolPosType dmi_fr, + size_t res_row) { + assert(NULL != mtrx_nd); + matrix_[res_row].mtrx_nd_fixed = NULL; + + if (mtrx_nd_pool_used_ >= kMtrxNdPoolSize - kMaxNodeARow) + return 0; + + if (0 == mtrx_nd->step) { + // Because the list is sorted, if the source step is 0, it is only + // necessary to pick up the first kMaxNodeARow items. + if (lpi_num > kMaxNodeARow) + lpi_num = kMaxNodeARow; + } + + MatrixNode *mtrx_nd_res_min = mtrx_nd_pool_ + matrix_[res_row].mtrx_nd_pos; + for (size_t pos = 0; pos < lpi_num; pos++) { + float score = mtrx_nd->score + lpi_items[pos].psb; + if (pos > 0 && score - PRUMING_SCORE > mtrx_nd_res_min->score) + break; + + // Try to add a new node + size_t mtrx_nd_num = matrix_[res_row].mtrx_nd_num; + MatrixNode *mtrx_nd_res = mtrx_nd_res_min + mtrx_nd_num; + bool replace = false; + // Find its position + while (mtrx_nd_res > mtrx_nd_res_min && score < (mtrx_nd_res - 1)->score) { + if (static_cast(mtrx_nd_res - mtrx_nd_res_min) < kMaxNodeARow) + *mtrx_nd_res = *(mtrx_nd_res - 1); + mtrx_nd_res--; + replace = true; + } + if (replace || (mtrx_nd_num < kMaxNodeARow && + matrix_[res_row].mtrx_nd_pos + mtrx_nd_num < kMtrxNdPoolSize)) { + mtrx_nd_res->id = lpi_items[pos].id; + mtrx_nd_res->score = score; + mtrx_nd_res->from = mtrx_nd; + mtrx_nd_res->dmi_fr = dmi_fr; + mtrx_nd_res->step = res_row; + if (matrix_[res_row].mtrx_nd_num < kMaxNodeARow) + matrix_[res_row].mtrx_nd_num++; + } + } + return matrix_[res_row].mtrx_nd_num; +} + +PoolPosType MatrixSearch::match_dmi(size_t step_to, uint16 spl_ids[], + uint16 spl_id_num) { + if (pys_decoded_len_ < step_to || 0 == matrix_[step_to].dmi_num) { + return static_cast(-1); + } + + for (PoolPosType dmi_pos = 0; dmi_pos < matrix_[step_to].dmi_num; dmi_pos++) { + DictMatchInfo *dmi = dmi_pool_ + matrix_[step_to].dmi_pos + dmi_pos; + + if (dmi->dict_level != spl_id_num) + continue; + + bool matched = true; + for (uint16 spl_pos = 0; spl_pos < spl_id_num; spl_pos++) { + if (spl_ids[spl_id_num - spl_pos - 1] != dmi->spl_id) { + matched = false; + break; + } + + dmi = dmi_pool_ + dmi->dmi_fr; + } + if (matched) { + return matrix_[step_to].dmi_pos + dmi_pos; + } + } + + return static_cast(-1); +} + +char16* MatrixSearch::get_candidate0(char16 *cand_str, size_t max_len, + uint16 *retstr_len, + bool only_unfixed) { + if (pys_decoded_len_ == 0 || + matrix_[pys_decoded_len_].mtrx_nd_num == 0) + return NULL; + + LemmaIdType idxs[kMaxRowNum]; + size_t id_num = 0; + + MatrixNode *mtrx_nd = mtrx_nd_pool_ + matrix_[pys_decoded_len_].mtrx_nd_pos; + + if (kPrintDebug0) { + printf("--- sentence score: %f\n", mtrx_nd->score); + } + + if (kPrintDebug1) { + printf("==============Sentence DMI (reverse order) begin===========>>\n"); + } + + while (mtrx_nd != NULL) { + idxs[id_num] = mtrx_nd->id; + id_num++; + + if (kPrintDebug1) { + printf("---MatrixNode [step: %d, lma_idx: %d, total score:%.5f]\n", + mtrx_nd->step, mtrx_nd->id, mtrx_nd->score); + debug_print_dmi(mtrx_nd->dmi_fr, 1); + } + + mtrx_nd = mtrx_nd->from; + } + + if (kPrintDebug1) { + printf("<<==============Sentence DMI (reverse order) end=============\n"); + } + + size_t ret_pos = 0; + do { + id_num--; + if (0 == idxs[id_num]) + continue; + + char16 str[kMaxLemmaSize + 1]; + uint16 str_len = get_lemma_str(idxs[id_num], str, kMaxLemmaSize + 1); + if (str_len > 0 && ((!only_unfixed && max_len - ret_pos > str_len) || + (only_unfixed && max_len - ret_pos + fixed_hzs_ > str_len))) { + if (!only_unfixed) + utf16_strncpy(cand_str + ret_pos, str, str_len); + else if (ret_pos >= fixed_hzs_) + utf16_strncpy(cand_str + ret_pos - fixed_hzs_, str, str_len); + + ret_pos += str_len; + } else { + return NULL; + } + } while (id_num != 0); + + if (!only_unfixed) { + if (NULL != retstr_len) + *retstr_len = ret_pos; + cand_str[ret_pos] = (char16)'\0'; + } else { + if (NULL != retstr_len) + *retstr_len = ret_pos - fixed_hzs_; + cand_str[ret_pos - fixed_hzs_] = (char16)'\0'; + } + return cand_str; +} + +size_t MatrixSearch::get_lpis(const uint16* splid_str, size_t splid_str_len, + LmaPsbItem* lma_buf, size_t max_lma_buf, + const char16 *pfullsent, bool sort_by_psb) { + if (splid_str_len > kMaxLemmaSize) + return 0; + + size_t num1 = dict_trie_->get_lpis(splid_str, splid_str_len, + lma_buf, max_lma_buf); + size_t num2 = 0; + if (NULL != user_dict_) { + num2 = user_dict_->get_lpis(splid_str, splid_str_len, + lma_buf + num1, max_lma_buf - num1); + } + + size_t num = num1 + num2; + + if (0 == num) + return 0; + + // Remove repeated items. + if (splid_str_len > 1) { + LmaPsbStrItem *lpsis = reinterpret_cast(lma_buf + num); + size_t lpsi_num = (max_lma_buf - num) * sizeof(LmaPsbItem) / + sizeof(LmaPsbStrItem); + assert(lpsi_num > num); + if (num > lpsi_num) num = lpsi_num; + lpsi_num = num; + + for (size_t pos = 0; pos < lpsi_num; pos++) { + lpsis[pos].lpi = lma_buf[pos]; + get_lemma_str(lma_buf[pos].id, lpsis[pos].str, kMaxLemmaSize + 1); + } + + myqsort(lpsis, lpsi_num, sizeof(LmaPsbStrItem), cmp_lpsi_with_str); + + size_t remain_num = 0; + for (size_t pos = 0; pos < lpsi_num; pos++) { + if (pos > 0 && utf16_strcmp(lpsis[pos].str, lpsis[pos - 1].str) == 0) { + if (lpsis[pos].lpi.psb < lpsis[pos - 1].lpi.psb) { + assert(remain_num > 0); + lma_buf[remain_num - 1] = lpsis[pos].lpi; + } + continue; + } + if (NULL != pfullsent && utf16_strcmp(lpsis[pos].str, pfullsent) == 0) + continue; + + lma_buf[remain_num] = lpsis[pos].lpi; + remain_num++; + } + + // Update the result number + num = remain_num; + } else { + // For single character, some characters have more than one spelling, for + // example, "de" and "di" are all valid for a Chinese character, so when + // the user input "d", repeated items are generated. + // For single character lemmas, Hanzis will be gotten + for (size_t pos = 0; pos < num; pos++) { + char16 hanzis[2]; + get_lemma_str(lma_buf[pos].id, hanzis, 2); + lma_buf[pos].hanzi = hanzis[0]; + } + + myqsort(lma_buf, num, sizeof(LmaPsbItem), cmp_lpi_with_hanzi); + + size_t remain_num = 0; + for (size_t pos = 0; pos < num; pos++) { + if (pos > 0 && lma_buf[pos].hanzi == lma_buf[pos - 1].hanzi) { + if (NULL != pfullsent && + static_cast(0) == pfullsent[1] && + lma_buf[pos].hanzi == pfullsent[0]) + continue; + + if (lma_buf[pos].psb < lma_buf[pos - 1].psb) { + assert(remain_num > 0); + assert(lma_buf[remain_num - 1].hanzi == lma_buf[pos].hanzi); + lma_buf[remain_num - 1] = lma_buf[pos]; + } + continue; + } + if (NULL != pfullsent && + static_cast(0) == pfullsent[1] && + lma_buf[pos].hanzi == pfullsent[0]) + continue; + + lma_buf[remain_num] = lma_buf[pos]; + remain_num++; + } + + num = remain_num; + } + + if (sort_by_psb) { + myqsort(lma_buf, num, sizeof(LmaPsbItem), cmp_lpi_with_psb); + } + return num; +} + +uint16 MatrixSearch::get_lemma_str(LemmaIdType id_lemma, char16 *str_buf, + uint16 str_max) { + uint16 str_len = 0; + + if (is_system_lemma(id_lemma)) { + str_len = dict_trie_->get_lemma_str(id_lemma, str_buf, str_max); + } else if (is_user_lemma(id_lemma)) { + if (NULL != user_dict_) { + str_len = user_dict_->get_lemma_str(id_lemma, str_buf, str_max); + } else { + str_len = 0; + str_buf[0] = static_cast('\0'); + } + } else if (is_composing_lemma(id_lemma)) { + if (str_max <= 1) + return 0; + str_len = c_phrase_.sublma_start[c_phrase_.sublma_num]; + if (str_len > str_max - 1) + str_len = str_max - 1; + utf16_strncpy(str_buf, c_phrase_.chn_str, str_len); + str_buf[str_len] = (char16)'\0'; + return str_len; + } + + return str_len; +} + +uint16 MatrixSearch::get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, + uint16 splids_max, bool arg_valid) { + uint16 splid_num = 0; + + if (arg_valid) { + for (splid_num = 0; splid_num < splids_max; splid_num++) { + if (spl_trie_->is_half_id(splids[splid_num])) + break; + } + if (splid_num == splids_max) + return splid_num; + } + + if (is_system_lemma(id_lemma)) { + splid_num = dict_trie_->get_lemma_splids(id_lemma, splids, splids_max, + arg_valid); + } else if (is_user_lemma(id_lemma)) { + if (NULL != user_dict_) { + splid_num = user_dict_->get_lemma_splids(id_lemma, splids, splids_max, + arg_valid); + } else { + splid_num = 0; + } + } else if (is_composing_lemma(id_lemma)) { + if (c_phrase_.length > splids_max) { + return 0; + } + for (uint16 pos = 0; pos < c_phrase_.length; pos++) { + splids[pos] = c_phrase_.spl_ids[pos]; + if (spl_trie_->is_half_id(splids[pos])) { + return 0; + } + } + } + return splid_num; +} + +size_t MatrixSearch::inner_predict(const char16 *fixed_buf, uint16 fixed_len, + char16 predict_buf[][kMaxPredictSize + 1], + size_t buf_len) { + size_t res_total = 0; + memset(npre_items_, 0, sizeof(NPredictItem) * npre_items_len_); + // In order to shorten the comments, j-character candidates predicted by + // i-character prefix are called P(i,j). All candiates predicted by + // i-character prefix are called P(i,*) + // Step 1. Get P(kMaxPredictSize, *) and sort them, here + // P(kMaxPredictSize, *) == P(kMaxPredictSize, 1) + for (size_t len = fixed_len; len >0; len--) { + // How many blank items are available + size_t this_max = npre_items_len_ - res_total; + size_t res_this; + // If the history is longer than 1, and we can not get prediction from + // lemmas longer than 2, in this case, we will add lemmas with + // highest scores as the prediction result. + if (fixed_len > 1 && 1 == len && 0 == res_total) { + // Try to find if recent n (n>1) characters can be a valid lemma in system + // dictionary. + bool nearest_n_word = false; + for (size_t nlen = 2; nlen <= fixed_len; nlen++) { + if (dict_trie_->get_lemma_id(fixed_buf + fixed_len - nlen, nlen) > 0) { + nearest_n_word = true; + break; + } + } + res_this = dict_trie_->predict_top_lmas(nearest_n_word ? len : 0, + npre_items_ + res_total, + this_max, res_total); + res_total += res_this; + } + + // How many blank items are available + this_max = npre_items_len_ - res_total; + res_this = 0; + if (!kOnlyUserDictPredict) { + res_this = + dict_trie_->predict(fixed_buf + fixed_len - len, len, + npre_items_ + res_total, this_max, + res_total); + } + + if (NULL != user_dict_) { + res_this = res_this + + user_dict_->predict(fixed_buf + fixed_len - len, len, + npre_items_ + res_total + res_this, + this_max - res_this, res_total + res_this); + } + + if (kPredictLimitGt1) { + myqsort(npre_items_ + res_total, res_this, sizeof(NPredictItem), + cmp_npre_by_score); + + if (len > 3) { + if (res_this > kMaxPredictNumByGt3) + res_this = kMaxPredictNumByGt3; + } else if (3 == len) { + if (res_this > kMaxPredictNumBy3) + res_this = kMaxPredictNumBy3; + } else if (2 == len) { + if (res_this > kMaxPredictNumBy2) + res_this = kMaxPredictNumBy2; + } + } + + res_total += res_this; + } + + res_total = remove_duplicate_npre(npre_items_, res_total); + + if (kPreferLongHistoryPredict) { + myqsort(npre_items_, res_total, sizeof(NPredictItem), + cmp_npre_by_hislen_score); + } else { + myqsort(npre_items_, res_total, sizeof(NPredictItem), + cmp_npre_by_score); + } + + if (buf_len < res_total) { + res_total = buf_len; + } + + if (kPrintDebug2) { + printf("/////////////////Predicted Items Begin////////////////////>>\n"); + for (size_t i = 0; i < res_total; i++) { + printf("---"); + for (size_t j = 0; j < kMaxPredictSize; j++) { + printf("%d ", npre_items_[i].pre_hzs[j]); + } + printf("\n"); + } + printf("< kMaxPredictSize || 0 == buf_len) + return 0; + + return inner_predict(fixed_buf, fixed_len, predict_buf, buf_len); +} + +} // namespace ime_pinyin diff --git a/jni/share/mystdlib.cpp b/jni/share/mystdlib.cpp new file mode 100644 index 0000000..93bbcc9 --- /dev/null +++ b/jni/share/mystdlib.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace ime_pinyin { + +// For debug purpose. You can add a fixed version of qsort and bsearch functions +// here so that the output will be totally the same under different platforms. + +void myqsort(void *p, size_t n, size_t es, + int (*cmp)(const void *, const void *)) { + qsort(p,n, es, cmp); +} + +void *mybsearch(const void *k, const void *b, + size_t n, size_t es, + int (*cmp)(const void *, const void *)) { + return bsearch(k, b, n, es, cmp); +} +} // namespace ime_pinyin diff --git a/jni/share/ngram.cpp b/jni/share/ngram.cpp new file mode 100644 index 0000000..d95477a --- /dev/null +++ b/jni/share/ngram.cpp @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "../include/mystdlib.h" +#include "../include/ngram.h" + +namespace ime_pinyin { + +#define ADD_COUNT 0.3 + +int comp_double(const void *p1, const void *p2) { + if (*static_cast(p1) < *static_cast(p2)) + return -1; + if (*static_cast(p1) > *static_cast(p2)) + return 1; + return 0; +} + +inline double distance(double freq, double code) { + // return fabs(freq - code); + return freq * fabs(log(freq) - log(code)); +} + +// Find the index of the code value which is nearest to the given freq +int qsearch_nearest(double code_book[], double freq, int start, int end) { + if (start == end) + return start; + + if (start + 1 == end) { + if (distance(freq, code_book[end]) > distance(freq, code_book[start])) + return start; + return end; + } + + int mid = (start + end) / 2; + + if (code_book[mid] > freq) + return qsearch_nearest(code_book, freq, start, mid); + else + return qsearch_nearest(code_book, freq, mid, end); +} + +size_t update_code_idx(double freqs[], size_t num, double code_book[], + CODEBOOK_TYPE *code_idx) { + size_t changed = 0; + for (size_t pos = 0; pos < num; pos++) { + CODEBOOK_TYPE idx; + idx = qsearch_nearest(code_book, freqs[pos], 0, kCodeBookSize - 1); + if (idx != code_idx[pos]) + changed++; + code_idx[pos] = idx; + } + return changed; +} + +double recalculate_kernel(double freqs[], size_t num, double code_book[], + CODEBOOK_TYPE *code_idx) { + double ret = 0; + + size_t *item_num = new size_t[kCodeBookSize]; + assert(item_num); + memset(item_num, 0, sizeof(size_t) * kCodeBookSize); + + double *cb_new = new double[kCodeBookSize]; + assert(cb_new); + memset(cb_new, 0, sizeof(double) * kCodeBookSize); + + for (size_t pos = 0; pos < num; pos++) { + ret += distance(freqs[pos], code_book[code_idx[pos]]); + + cb_new[code_idx[pos]] += freqs[pos]; + item_num[code_idx[pos]] += 1; + } + + for (size_t code = 0; code < kCodeBookSize; code++) { + assert(item_num[code] > 0); + code_book[code] = cb_new[code] / item_num[code]; + } + + delete [] item_num; + delete [] cb_new; + + return ret; +} + +void iterate_codes(double freqs[], size_t num, double code_book[], + CODEBOOK_TYPE *code_idx) { + size_t iter_num = 0; + double delta_last = 0; + do { + size_t changed = update_code_idx(freqs, num, code_book, code_idx); + + double delta = recalculate_kernel(freqs, num, code_book, code_idx); + + if (kPrintDebug0) { + printf("---Unigram codebook iteration: %d : %d, %.9f\n", + iter_num, changed, delta); + } + iter_num++; + + if (iter_num > 1 && + (delta == 0 || fabs(delta_last - delta)/fabs(delta) < 0.000000001)) + break; + delta_last = delta; + } while (true); +} + + +NGram* NGram::instance_ = NULL; + +NGram::NGram() { + initialized_ = false; + idx_num_ = 0; + lma_freq_idx_ = NULL; + sys_score_compensation_ = 0; + +#ifdef ___BUILD_MODEL___ + freq_codes_df_ = NULL; +#endif + freq_codes_ = NULL; +} + +NGram::~NGram() { + if (NULL != lma_freq_idx_) + free(lma_freq_idx_); + +#ifdef ___BUILD_MODEL___ + if (NULL != freq_codes_df_) + free(freq_codes_df_); +#endif + + if (NULL != freq_codes_) + free(freq_codes_); +} + +NGram& NGram::get_instance() { + if (NULL == instance_) + instance_ = new NGram(); + return *instance_; +} + +bool NGram::save_ngram(FILE *fp) { + if (!initialized_ || NULL == fp) + return false; + + if (0 == idx_num_ || NULL == freq_codes_ || NULL == lma_freq_idx_) + return false; + + if (fwrite(&idx_num_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(freq_codes_, sizeof(LmaScoreType), kCodeBookSize, fp) != + kCodeBookSize) + return false; + + if (fwrite(lma_freq_idx_, sizeof(CODEBOOK_TYPE), idx_num_, fp) != idx_num_) + return false; + + return true; +} + +bool NGram::load_ngram(FILE *fp) { + if (NULL == fp) + return false; + + initialized_ = false; + + if (fread(&idx_num_, sizeof(size_t), 1, fp) != 1 ) + return false; + + if (NULL != lma_freq_idx_) + free(lma_freq_idx_); + + if (NULL != freq_codes_) + free(freq_codes_); + + lma_freq_idx_ = static_cast + (malloc(idx_num_ * sizeof(CODEBOOK_TYPE))); + freq_codes_ = static_cast + (malloc(kCodeBookSize * sizeof(LmaScoreType))); + + if (NULL == lma_freq_idx_ || NULL == freq_codes_) + return false; + + if (fread(freq_codes_, sizeof(LmaScoreType), kCodeBookSize, fp) != + kCodeBookSize) + return false; + + if (fread(lma_freq_idx_, sizeof(CODEBOOK_TYPE), idx_num_, fp) != idx_num_) + return false; + + initialized_ = true; + + total_freq_none_sys_ = 0; + return true; +} + +void NGram::set_total_freq_none_sys(size_t freq_none_sys) { + total_freq_none_sys_ = freq_none_sys; + if (0 == total_freq_none_sys_) { + sys_score_compensation_ = 0; + } else { + double factor = static_cast(total_freq_none_sys_) / ( + kSysDictTotalFreq + total_freq_none_sys_); + sys_score_compensation_ = static_cast( + log(factor) * kLogValueAmplifier); + } +} + +// The caller makes sure this oject is initialized. +float NGram::get_uni_psb(LemmaIdType lma_id) { + return static_cast(freq_codes_[lma_freq_idx_[lma_id]]) + + sys_score_compensation_; +} + +float NGram::convert_psb_to_score(double psb) { + float score = static_cast( + log(psb) * static_cast(kLogValueAmplifier)); + if (score > static_cast(kMaxScore)) { + score = static_cast(kMaxScore); + } + return score; +} + +#ifdef ___BUILD_MODEL___ +bool NGram::build_unigram(LemmaEntry *lemma_arr, size_t lemma_num, + LemmaIdType next_idx_unused) { + if (NULL == lemma_arr || 0 == lemma_num || next_idx_unused <= 1) + return false; + + double total_freq = 0; + double *freqs = new double[next_idx_unused]; + if (NULL == freqs) + return false; + + freqs[0] = ADD_COUNT; + total_freq += freqs[0]; + LemmaIdType idx_now = 0; + for (size_t pos = 0; pos < lemma_num; pos++) { + if (lemma_arr[pos].idx_by_hz == idx_now) + continue; + idx_now++; + + assert(lemma_arr[pos].idx_by_hz == idx_now); + + freqs[idx_now] = lemma_arr[pos].freq; + if (freqs[idx_now] <= 0) + freqs[idx_now] = 0.3; + + total_freq += freqs[idx_now]; + } + + double max_freq = 0; + idx_num_ = idx_now + 1; + assert(idx_now + 1 == next_idx_unused); + + for (size_t pos = 0; pos < idx_num_; pos++) { + freqs[pos] = freqs[pos] / total_freq; + assert(freqs[pos] > 0); + if (freqs[pos] > max_freq) + max_freq = freqs[pos]; + } + + // calculate the code book + if (NULL == freq_codes_df_) + freq_codes_df_ = new double[kCodeBookSize]; + assert(freq_codes_df_); + memset(freq_codes_df_, 0, sizeof(double) * kCodeBookSize); + + if (NULL == freq_codes_) + freq_codes_ = new LmaScoreType[kCodeBookSize]; + assert(freq_codes_); + memset(freq_codes_, 0, sizeof(LmaScoreType) * kCodeBookSize); + + size_t freq_pos = 0; + for (size_t code_pos = 0; code_pos < kCodeBookSize; code_pos++) { + bool found = true; + + while (found) { + found = false; + double cand = freqs[freq_pos]; + for (size_t i = 0; i < code_pos; i++) + if (freq_codes_df_[i] == cand) { + found = true; + break; + } + if (found) + freq_pos++; + } + + freq_codes_df_[code_pos] = freqs[freq_pos]; + freq_pos++; + } + + myqsort(freq_codes_df_, kCodeBookSize, sizeof(double), comp_double); + + if (NULL == lma_freq_idx_) + lma_freq_idx_ = new CODEBOOK_TYPE[idx_num_]; + assert(lma_freq_idx_); + + iterate_codes(freqs, idx_num_, freq_codes_df_, lma_freq_idx_); + + delete [] freqs; + + if (kPrintDebug0) { + printf("\n------Language Model Unigram Codebook------\n"); + } + + for (size_t code_pos = 0; code_pos < kCodeBookSize; code_pos++) { + double log_score = log(freq_codes_df_[code_pos]); + float final_score = convert_psb_to_score(freq_codes_df_[code_pos]); + if (kPrintDebug0) { + printf("code:%d, probability:%.9f, log score:%.3f, final score: %.3f\n", + code_pos, freq_codes_df_[code_pos], log_score, final_score); + } + freq_codes_[code_pos] = static_cast(final_score); + } + + initialized_ = true; + return true; +} +#endif + +} // namespace ime_pinyin diff --git a/jni/share/pinyinime.cpp b/jni/share/pinyinime.cpp new file mode 100644 index 0000000..550da7b --- /dev/null +++ b/jni/share/pinyinime.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "../include/pinyinime.h" +#include "../include/dicttrie.h" +#include "../include/matrixsearch.h" +#include "../include/spellingtrie.h" + +#ifdef __cplusplus +extern "C" { +#endif + + using namespace ime_pinyin; + + // The maximum number of the prediction items. + static const size_t kMaxPredictNum = 500; + + // Used to search Pinyin string and give the best candidate. + MatrixSearch* matrix_search = NULL; + + char16 predict_buf[kMaxPredictNum][kMaxPredictSize + 1]; + + bool im_open_decoder(const char *fn_sys_dict, const char *fn_usr_dict) { + if (NULL != matrix_search) + delete matrix_search; + + matrix_search = new MatrixSearch(); + if (NULL == matrix_search) { + return false; + } + + return matrix_search->init(fn_sys_dict, fn_usr_dict); + } + + bool im_open_decoder_fd(int sys_fd, long start_offset, long length, + const char *fn_usr_dict) { + if (NULL != matrix_search) + delete matrix_search; + + matrix_search = new MatrixSearch(); + if (NULL == matrix_search) + return false; + + return matrix_search->init_fd(sys_fd, start_offset, length, fn_usr_dict); + } + + void im_close_decoder() { + if (NULL != matrix_search) { + matrix_search->close(); + delete matrix_search; + } + matrix_search = NULL; + } + + void im_set_max_lens(size_t max_sps_len, size_t max_hzs_len) { + if (NULL != matrix_search) { + matrix_search->set_max_lens(max_sps_len, max_hzs_len); + } + } + + void im_flush_cache() { + if (NULL != matrix_search) + matrix_search->flush_cache(); + } + + // To be updated. + size_t im_search(const char* pybuf, size_t pylen) { + if (NULL == matrix_search) + return 0; + + matrix_search->search(pybuf, pylen); + return matrix_search->get_candidate_num(); + } + + size_t im_delsearch(size_t pos, bool is_pos_in_splid, + bool clear_fixed_this_step) { + if (NULL == matrix_search) + return 0; + matrix_search->delsearch(pos, is_pos_in_splid, clear_fixed_this_step); + return matrix_search->get_candidate_num(); + } + + void im_reset_search() { + if (NULL == matrix_search) + return; + + matrix_search->reset_search(); + } + + // To be removed + size_t im_add_letter(char ch) { + return 0; + } + + const char* im_get_sps_str(size_t *decoded_len) { + if (NULL == matrix_search) + return NULL; + + return matrix_search->get_pystr(decoded_len); + } + + char16* im_get_candidate(size_t cand_id, char16* cand_str, + size_t max_len) { + if (NULL == matrix_search) + return NULL; + + return matrix_search->get_candidate(cand_id, cand_str, max_len); + } + + size_t im_get_spl_start_pos(const uint16 *&spl_start) { + if (NULL == matrix_search) + return 0; + + return matrix_search->get_spl_start(spl_start); + } + + size_t im_choose(size_t choice_id) { + if (NULL == matrix_search) + return 0; + + return matrix_search->choose(choice_id); + } + + size_t im_cancel_last_choice() { + if (NULL == matrix_search) + return 0; + + return matrix_search->cancel_last_choice(); + } + + size_t im_get_fixed_len() { + if (NULL == matrix_search) + return 0; + + return matrix_search->get_fixedlen(); + } + + // To be removed + bool im_cancel_input() { + return true; + } + + + size_t im_get_predicts(const char16 *his_buf, + char16 (*&pre_buf)[kMaxPredictSize + 1]) { + if (NULL == his_buf) + return 0; + + size_t fixed_len = utf16_strlen(his_buf); + const char16 *fixed_ptr = his_buf; + if (fixed_len > kMaxPredictSize) { + fixed_ptr += fixed_len - kMaxPredictSize; + fixed_len = kMaxPredictSize; + } + + pre_buf = predict_buf; + return matrix_search->get_predicts(his_buf, pre_buf, kMaxPredictNum); + } + + void im_enable_shm_as_szm(bool enable) { + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + spl_trie.szm_enable_shm(enable); + } + + void im_enable_ym_as_szm(bool enable) { + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + spl_trie.szm_enable_ym(enable); + } + +#ifdef __cplusplus +} +#endif diff --git a/jni/share/searchutility.cpp b/jni/share/searchutility.cpp new file mode 100644 index 0000000..281da38 --- /dev/null +++ b/jni/share/searchutility.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "../include/mystdlib.h" +#include "../include/searchutility.h" + +namespace ime_pinyin { + +bool is_system_lemma(LemmaIdType lma_id) { + return (0 < lma_id && lma_id <= kSysDictIdEnd); +} + +bool is_user_lemma(LemmaIdType lma_id) { + return (kUserDictIdStart <= lma_id && lma_id <= kUserDictIdEnd); +} + +bool is_composing_lemma(LemmaIdType lma_id) { + return (kLemmaIdComposing == lma_id); +} + +int cmp_lpi_with_psb(const void *p1, const void *p2) { + if ((static_cast(p1))->psb > + (static_cast(p2))->psb) + return 1; + if ((static_cast(p1))->psb < + (static_cast(p2))->psb) + return -1; + return 0; +} + +int cmp_lpi_with_unified_psb(const void *p1, const void *p2) { + const LmaPsbItem *item1 = static_cast(p1); + const LmaPsbItem *item2 = static_cast(p2); + + // The real unified psb is psb1 / lma_len1 and psb2 * lma_len2 + // But we use psb1 * lma_len2 and psb2 * lma_len1 to get better + // precision. + size_t up1 = item1->psb * (item2->lma_len); + size_t up2 = item2->psb * (item1->lma_len); + if (up1 < up2) { + return -1; + } + if (up1 > up2) { + return 1; + } + return 0; +} + +int cmp_lpi_with_id(const void *p1, const void *p2) { + if ((static_cast(p1))->id < + (static_cast(p2))->id) + return -1; + if ((static_cast(p1))->id > + (static_cast(p2))->id) + return 1; + return 0; +} + +int cmp_lpi_with_hanzi(const void *p1, const void *p2) { + if ((static_cast(p1))->hanzi < + (static_cast(p2))->hanzi) + return -1; + if ((static_cast(p1))->hanzi > + (static_cast(p2))->hanzi) + return 1; + + return 0; +} + +int cmp_lpsi_with_str(const void *p1, const void *p2) { + return utf16_strcmp((static_cast(p1))->str, + (static_cast(p2))->str); +} + + +int cmp_hanzis_1(const void *p1, const void *p2) { + if (*static_cast(p1) < + *static_cast(p2)) + return -1; + + if (*static_cast(p1) > + *static_cast(p2)) + return 1; + return 0; +} + +int cmp_hanzis_2(const void *p1, const void *p2) { + return utf16_strncmp(static_cast(p1), + static_cast(p2), 2); +} + +int cmp_hanzis_3(const void *p1, const void *p2) { + return utf16_strncmp(static_cast(p1), + static_cast(p2), 3); +} + +int cmp_hanzis_4(const void *p1, const void *p2) { + return utf16_strncmp(static_cast(p1), + static_cast(p2), 4); +} + +int cmp_hanzis_5(const void *p1, const void *p2) { + return utf16_strncmp(static_cast(p1), + static_cast(p2), 5); +} + +int cmp_hanzis_6(const void *p1, const void *p2) { + return utf16_strncmp(static_cast(p1), + static_cast(p2), 6); +} + +int cmp_hanzis_7(const void *p1, const void *p2) { + return utf16_strncmp(static_cast(p1), + static_cast(p2), 7); +} + +int cmp_hanzis_8(const void *p1, const void *p2) { + return utf16_strncmp(static_cast(p1), + static_cast(p2), 8); +} + +int cmp_npre_by_score(const void *p1, const void *p2) { + if ((static_cast(p1))->psb > + (static_cast(p2))->psb) + return 1; + + if ((static_cast(p1))->psb < + (static_cast(p2))->psb) + return -1; + + return 0; +} + +int cmp_npre_by_hislen_score(const void *p1, const void *p2) { + if ((static_cast(p1))->his_len < + (static_cast(p2))->his_len) + return 1; + + if ((static_cast(p1))->his_len > + (static_cast(p2))->his_len) + return -1; + + if ((static_cast(p1))->psb > + (static_cast(p2))->psb) + return 1; + + if ((static_cast(p1))->psb < + (static_cast(p2))->psb) + return -1; + + return 0; +} + +int cmp_npre_by_hanzi_score(const void *p1, const void *p2) { + int ret_v = (utf16_strncmp((static_cast(p1))->pre_hzs, + (static_cast(p2))->pre_hzs, kMaxPredictSize)); + if (0 != ret_v) + return ret_v; + + if ((static_cast(p1))->psb > + (static_cast(p2))->psb) + return 1; + + if ((static_cast(p1))->psb < + (static_cast(p2))->psb) + return -1; + + return 0; +} + +size_t remove_duplicate_npre(NPredictItem *npre_items, size_t npre_num) { + if (NULL == npre_items || 0 == npre_num) + return 0; + + myqsort(npre_items, npre_num, sizeof(NPredictItem), cmp_npre_by_hanzi_score); + + size_t remain_num = 1; // The first one is reserved. + for (size_t pos = 1; pos < npre_num; pos++) { + if (utf16_strncmp(npre_items[pos].pre_hzs, + npre_items[remain_num - 1].pre_hzs, + kMaxPredictSize) != 0) { + if (remain_num != pos) { + npre_items[remain_num] = npre_items[pos]; + } + remain_num++; + } + } + return remain_num; +} + +size_t align_to_size_t(size_t size) { + size_t s = sizeof(size_t); + return (size + s -1) / s * s; +} + +} // namespace ime_pinyin diff --git a/jni/share/spellingtable.cpp b/jni/share/spellingtable.cpp new file mode 100644 index 0000000..6005e20 --- /dev/null +++ b/jni/share/spellingtable.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "../include/spellingtable.h" + +namespace ime_pinyin { + +#ifdef ___BUILD_MODEL___ + +const char SpellingTable:: + kNotSupportList[kNotSupportNum][kMaxSpellingSize + 1] = {"HM", "HNG", "NG"}; + +// "" is the biggest, so that all empty strings will be moved to the end +// _eb mean empty is biggest +int compare_raw_spl_eb(const void* p1, const void* p2) { + if ('\0' == (static_cast(p1))->str[0]) + return 1; + + if ('\0' == (static_cast(p2))->str[0]) + return -1; + + return strcmp((static_cast(p1))->str, + (static_cast(p2))->str); +} + +size_t get_odd_next(size_t value) { + size_t v_next = value; + while (true) { + size_t v_next_sqrt = (size_t)sqrt(v_next); + + bool is_odd = true; + for (size_t v_dv = 2; v_dv < v_next_sqrt + 1; v_dv++) { + if (v_next % v_dv == 0) { + is_odd = false; + break; + } + } + + if (is_odd) + return v_next; + + v_next++; + } + + // never reach here + return 0; +} + +SpellingTable::SpellingTable() { + need_score_ = false; + raw_spellings_ = NULL; + spelling_buf_ = NULL; + spelling_num_ = 0; + total_freq_ = 0; + frozen_ = true; +} + +SpellingTable::~SpellingTable() { + free_resource(); +} + +size_t SpellingTable::get_hash_pos(const char* spelling_str) { + size_t hash_pos = 0; + for (size_t pos = 0; pos < spelling_size_; pos++) { + if ('\0' == spelling_str[pos]) + break; + hash_pos += (size_t)spelling_str[pos]; + } + + hash_pos = hash_pos % spelling_max_num_; + return hash_pos; +} + +size_t SpellingTable::hash_pos_next(size_t hash_pos) { + hash_pos += 123; + hash_pos = hash_pos % spelling_max_num_; + return hash_pos; +} + +void SpellingTable::free_resource() { + if (NULL != raw_spellings_) + delete [] raw_spellings_; + raw_spellings_ = NULL; + + if (NULL != spelling_buf_) + delete [] spelling_buf_; + spelling_buf_ = NULL; +} + +bool SpellingTable::init_table(size_t pure_spl_size, size_t spl_max_num, + bool need_score) { + if (pure_spl_size == 0 || spl_max_num ==0) + return false; + + need_score_ = need_score; + + free_resource(); + + spelling_size_ = pure_spl_size + 1; + if (need_score) + spelling_size_ += 1; + spelling_max_num_ = get_odd_next(spl_max_num); + spelling_num_ = 0; + + raw_spellings_ = new RawSpelling[spelling_max_num_]; + spelling_buf_ = new char[spelling_max_num_ * (spelling_size_)]; + if (NULL == raw_spellings_ || NULL == spelling_buf_) { + free_resource(); + return false; + } + + memset(raw_spellings_, 0, spelling_max_num_ * sizeof(RawSpelling)); + memset(spelling_buf_, 0, spelling_max_num_ * (spelling_size_)); + frozen_ = false; + total_freq_ = 0; + return true; +} + +bool SpellingTable::put_spelling(const char* spelling_str, double freq) { + if (frozen_ || NULL == spelling_str) + return false; + + for (size_t pos = 0; pos < kNotSupportNum; pos++) { + if (strcmp(spelling_str, kNotSupportList[pos]) == 0) { + return false; + } + } + + total_freq_ += freq; + + size_t hash_pos = get_hash_pos(spelling_str); + + raw_spellings_[hash_pos].str[spelling_size_ - 1] = '\0'; + + if (strncmp(raw_spellings_[hash_pos].str, spelling_str, + spelling_size_ - 1) == 0) { + raw_spellings_[hash_pos].freq += freq; + return true; + } + + size_t hash_pos_ori = hash_pos; + + while (true) { + if (strncmp(raw_spellings_[hash_pos].str, + spelling_str, spelling_size_ - 1) == 0) { + raw_spellings_[hash_pos].freq += freq; + return true; + } + + if ('\0' == raw_spellings_[hash_pos].str[0]) { + raw_spellings_[hash_pos].freq += freq; + strncpy(raw_spellings_[hash_pos].str, spelling_str, spelling_size_ - 1); + raw_spellings_[hash_pos].str[spelling_size_ - 1] = '\0'; + spelling_num_++; + return true; + } + + hash_pos = hash_pos_next(hash_pos); + if (hash_pos_ori == hash_pos) + return false; + } + + // never reach here + return false; +} + +bool SpellingTable::contain(const char* spelling_str) { + if (NULL == spelling_str || NULL == spelling_buf_ || frozen_) + return false; + + size_t hash_pos = get_hash_pos(spelling_str); + + if ('\0' == raw_spellings_[hash_pos].str[0]) + return false; + + if (strncmp(raw_spellings_[hash_pos].str, spelling_str, spelling_size_ - 1) + == 0) + return true; + + size_t hash_pos_ori = hash_pos; + + while (true) { + hash_pos = hash_pos_next(hash_pos); + if (hash_pos_ori == hash_pos) + return false; + + if ('\0' == raw_spellings_[hash_pos].str[0]) + return false; + + if (strncmp(raw_spellings_[hash_pos].str, spelling_str, spelling_size_ - 1) + == 0) + return true; + } + + // never reach here + return false; +} + +const char* SpellingTable::arrange(size_t *item_size, size_t *spl_num) { + if (NULL == raw_spellings_ || NULL == spelling_buf_ || + NULL == item_size || NULL == spl_num) + return NULL; + + qsort(raw_spellings_, spelling_max_num_, sizeof(RawSpelling), + compare_raw_spl_eb); + + // After sorting, only the first spelling_num_ items are valid. + // Copy them to the destination buffer. + for (size_t pos = 0; pos < spelling_num_; pos++) { + strncpy(spelling_buf_ + pos * spelling_size_, raw_spellings_[pos].str, + spelling_size_); + } + + if (need_score_) { + if (kPrintDebug0) + printf("------------Spelling Possiblities--------------\n"); + + double max_score = 0; + double min_score = 0; + + // After sorting, only the first spelling_num_ items are valid. + for (size_t pos = 0; pos < spelling_num_; pos++) { + raw_spellings_[pos].freq /= total_freq_; + if (need_score_) { + if (0 == pos) { + max_score = raw_spellings_[0].freq; + min_score = max_score; + } else { + if (raw_spellings_[pos].freq > max_score) + max_score = raw_spellings_[pos].freq; + if (raw_spellings_[pos].freq < min_score) + min_score = raw_spellings_[pos].freq; + } + } + } + + if (kPrintDebug0) + printf("-----max psb: %f, min psb: %f\n", max_score, min_score); + + max_score = log(max_score); + min_score = log(min_score); + + if (kPrintDebug0) + printf("-----max log value: %f, min log value: %f\n", + max_score, min_score); + + // The absolute value of min_score is bigger than that of max_score because + // both of them are negative after log function. + score_amplifier_ = 1.0 * 255 / min_score; + + double average_score = 0; + for (size_t pos = 0; pos < spelling_num_; pos++) { + double score = log(raw_spellings_[pos].freq) * score_amplifier_; + assert(score >= 0); + + average_score += score; + + // Because of calculation precision issue, score might be a little bigger + // than 255 after being amplified. + if (score > 255) + score = 255; + char *this_spl_buf = spelling_buf_ + pos * spelling_size_; + this_spl_buf[spelling_size_ - 1] = + static_cast((unsigned char)score); + + if (kPrintDebug0) { + printf("---pos:%d, %s, psb:%d\n", pos, this_spl_buf, + (unsigned char)this_spl_buf[spelling_size_ -1]); + } + } + average_score /= spelling_num_; + assert(average_score <= 255); + average_score_ = static_cast(average_score); + + if (kPrintDebug0) + printf("\n----Score Amplifier: %f, Average Score: %d\n", score_amplifier_, + average_score_); + } + + *item_size = spelling_size_; + *spl_num = spelling_num_; + frozen_ = true; + return spelling_buf_; +} + +float SpellingTable::get_score_amplifier() { + return static_cast(score_amplifier_); +} + +unsigned char SpellingTable::get_average_score() { + return average_score_; +} + +#endif // ___BUILD_MODEL___ +} // namespace ime_pinyin diff --git a/jni/share/spellingtrie.cpp b/jni/share/spellingtrie.cpp new file mode 100644 index 0000000..85be46b --- /dev/null +++ b/jni/share/spellingtrie.cpp @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "../include/dictdef.h" + +#ifdef ___BUILD_MODEL___ +#include "../include/spellingtable.h" +#endif + +#include "../include/spellingtrie.h" + +namespace ime_pinyin { + +SpellingTrie* SpellingTrie::instance_ = NULL; + +// z/c/s is for Zh/Ch/Sh +const char SpellingTrie::kHalfId2Sc_[kFullSplIdStart + 1] = + "0ABCcDEFGHIJKLMNOPQRSsTUVWXYZz"; + +// Bit 0 : is it a Shengmu char? +// Bit 1 : is it a Yunmu char? (one char is a Yunmu) +// Bit 2 : is it enabled in ShouZiMu(first char) mode? +unsigned char SpellingTrie::char_flags_[] = { + // a b c d e f g + 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, + // h i j k l m n + 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + // o p q r s t + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + // u v w x y z + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01 +}; + +int compare_spl(const void* p1, const void* p2) { + return strcmp((const char*)(p1), (const char*)(p2)); +} + +SpellingTrie::SpellingTrie() { + spelling_buf_ = NULL; + spelling_size_ = 0; + spelling_num_ = 0; + spl_ym_ids_ = NULL; + splstr_queried_ = NULL; + splstr16_queried_ = NULL; + root_ = NULL; + dumb_node_ = NULL; + splitter_node_ = NULL; + instance_ = NULL; + ym_buf_ = NULL; + f2h_ = NULL; + + szm_enable_shm(true); + szm_enable_ym(true); + +#ifdef ___BUILD_MODEL___ + node_num_ = 0; +#endif +} + +SpellingTrie::~SpellingTrie() { + if (NULL != spelling_buf_) + delete [] spelling_buf_; + + if (NULL != splstr_queried_) + delete [] splstr_queried_; + + if (NULL != splstr16_queried_) + delete [] splstr16_queried_; + + if (NULL != spl_ym_ids_) + delete [] spl_ym_ids_; + + if (NULL != root_) { + free_son_trie(root_); + delete root_; + } + + if (NULL != dumb_node_) { + delete [] dumb_node_; + } + + if (NULL != splitter_node_) { + delete [] splitter_node_; + } + + if (NULL != instance_) { + delete instance_; + instance_ = NULL; + } + + if (NULL != ym_buf_) + delete [] ym_buf_; + + if (NULL != f2h_) + delete [] f2h_; +} + +bool SpellingTrie::if_valid_id_update(uint16 *splid) const { + if (NULL == splid || 0 == *splid) + return false; + + if (*splid >= kFullSplIdStart) + return true; + if (*splid < kFullSplIdStart) { + char ch = kHalfId2Sc_[*splid]; + if (ch > 'Z') { + return true; + } else { + if (szm_is_enabled(ch)) { + return true; + } else if (is_yunmu_char(ch)) { + assert(h2f_num_[*splid] > 0); + *splid = h2f_start_[*splid]; + return true; + } + } + } + return false; +} + +bool SpellingTrie::is_half_id(uint16 splid) const { + if (0 == splid || splid >= kFullSplIdStart) + return false; + + return true; +} + +bool SpellingTrie::is_full_id(uint16 splid) const { + if (splid < kFullSplIdStart || splid >= kFullSplIdStart + spelling_num_) + return false; + return true; +} + +bool SpellingTrie::half_full_compatible(uint16 half_id, uint16 full_id) const { + uint16 half_fr_full = full_to_half(full_id); + + if (half_fr_full == half_id) + return true; + + // &~0x20 is used to conver the char to upper case. + // So that Zh/Ch/Sh(whose char is z/c/s) can be matched with Z/C/S. + char ch_f = (kHalfId2Sc_[half_fr_full] & (~0x20)); + char ch_h = kHalfId2Sc_[half_id]; + if (ch_f == ch_h) + return true; + + return false; +} + +bool SpellingTrie::is_half_id_yunmu(uint16 splid) const { + if (0 == splid || splid >= kFullSplIdStart) + return false; + + char ch = kHalfId2Sc_[splid]; + // If ch >= 'a', that means the half id is one of Zh/Ch/Sh + if (ch >= 'a') { + return false; + } + + return char_flags_[ch - 'A'] & kHalfIdYunmuMask; +} + +bool SpellingTrie::is_shengmu_char(char ch) const { + return char_flags_[ch - 'A'] & kHalfIdShengmuMask; +} + +bool SpellingTrie::is_yunmu_char(char ch) const { + return char_flags_[ch - 'A'] & kHalfIdYunmuMask; +} + +bool SpellingTrie::is_szm_char(char ch) const { + return is_shengmu_char(ch) || is_yunmu_char(ch); +} + +bool SpellingTrie::szm_is_enabled(char ch) const { + return char_flags_[ch - 'A'] & kHalfIdSzmMask; +} + +void SpellingTrie::szm_enable_shm(bool enable) { + if (enable) { + for (char ch = 'A'; ch <= 'Z'; ch++) { + if (is_shengmu_char(ch)) + char_flags_[ch - 'A'] = char_flags_[ch - 'A'] | kHalfIdSzmMask; + } + } else { + for (char ch = 'A'; ch <= 'Z'; ch++) { + if (is_shengmu_char(ch)) + char_flags_[ch - 'A'] = char_flags_[ch - 'A'] & (kHalfIdSzmMask ^ 0xff); + } + } +} + +void SpellingTrie::szm_enable_ym(bool enable) { + if (enable) { + for (char ch = 'A'; ch <= 'Z'; ch++) { + if (is_yunmu_char(ch)) + char_flags_[ch - 'A'] = char_flags_[ch - 'A'] | kHalfIdSzmMask; + } + } else { + for (char ch = 'A'; ch <= 'Z'; ch++) { + if (is_yunmu_char(ch)) + char_flags_[ch - 'A'] = char_flags_[ch - 'A'] & (kHalfIdSzmMask ^ 0xff); + } + } +} + +bool SpellingTrie::is_szm_enabled(char ch) const { + return char_flags_[ch - 'A'] & kHalfIdSzmMask; +} + +const SpellingTrie* SpellingTrie::get_cpinstance() { + return &get_instance(); +} + +SpellingTrie& SpellingTrie::get_instance() { + if (NULL == instance_) + instance_ = new SpellingTrie(); + + return *instance_; +} + +uint16 SpellingTrie::half2full_num(uint16 half_id) const { + if (NULL == root_ || half_id >= kFullSplIdStart) + return 0; + return h2f_num_[half_id]; +} + +uint16 SpellingTrie::half_to_full(uint16 half_id, uint16 *spl_id_start) const { + if (NULL == spl_id_start || NULL == root_ || half_id >= kFullSplIdStart) + return 0; + + *spl_id_start = h2f_start_[half_id]; + return h2f_num_[half_id]; +} + +uint16 SpellingTrie::full_to_half(uint16 full_id) const { + if (NULL == root_ || full_id < kFullSplIdStart || + full_id > spelling_num_ + kFullSplIdStart) + return 0; + + return f2h_[full_id - kFullSplIdStart]; +} + +void SpellingTrie::free_son_trie(SpellingNode* node) { + if (NULL == node) + return; + + for (size_t pos = 0; pos < node->num_of_son; pos++) { + free_son_trie(node->first_son + pos); + } + + if (NULL != node->first_son) + delete [] node->first_son; +} + +bool SpellingTrie::construct(const char* spelling_arr, size_t item_size, + size_t item_num, float score_amplifier, + unsigned char average_score) { + if (spelling_arr == NULL) + return false; + + memset(h2f_start_, 0, sizeof(uint16) * kFullSplIdStart); + memset(h2f_num_, 0, sizeof(uint16) * kFullSplIdStart); + + // If the arr is the same as the buf, means this function is called by + // load_table(), the table data are ready; otherwise the array should be + // saved. + if (spelling_arr != spelling_buf_) { + if (NULL != spelling_buf_) + delete [] spelling_buf_; + spelling_buf_ = new char[item_size * item_num]; + if (NULL == spelling_buf_) + return false; + memcpy(spelling_buf_, spelling_arr, sizeof(char) * item_size * item_num); + } + + spelling_size_ = item_size; + spelling_num_ = item_num; + + score_amplifier_ = score_amplifier; + average_score_ = average_score; + + if (NULL != splstr_queried_) + delete [] splstr_queried_; + splstr_queried_ = new char[spelling_size_]; + if (NULL == splstr_queried_) + return false; + + if (NULL != splstr16_queried_) + delete [] splstr16_queried_; + splstr16_queried_ = new char16[spelling_size_]; + if (NULL == splstr16_queried_) + return false; + + // First, sort the buf to ensure they are in ascendant order + qsort(spelling_buf_, spelling_num_, spelling_size_, compare_spl); + +#ifdef ___BUILD_MODEL___ + node_num_ = 1; +#endif + + root_ = new SpellingNode(); + memset(root_, 0, sizeof(SpellingNode)); + + dumb_node_ = new SpellingNode(); + memset(dumb_node_, 0, sizeof(SpellingNode)); + dumb_node_->score = average_score_; + + splitter_node_ = new SpellingNode(); + memset(splitter_node_, 0, sizeof(SpellingNode)); + splitter_node_->score = average_score_; + + memset(level1_sons_, 0, sizeof(SpellingNode*) * kValidSplCharNum); + + root_->first_son = construct_spellings_subset(0, spelling_num_, 0, root_); + + // Root's score should be cleared. + root_->score = 0; + + if (NULL == root_->first_son) + return false; + + h2f_start_[0] = h2f_num_[0] = 0; + + if (!build_f2h()) + return false; + +#ifdef ___BUILD_MODEL___ + if (kPrintDebug0) { + printf("---SpellingTrie Nodes: %d\n", node_num_); + } + return build_ym_info(); +#else + return true; +#endif +} + +#ifdef ___BUILD_MODEL___ +const char* SpellingTrie::get_ym_str(const char *spl_str) { + bool start_ZCS = false; + if (is_shengmu_char(*spl_str)) { + if ('Z' == *spl_str || 'C' == *spl_str || 'S' == *spl_str) + start_ZCS = true; + spl_str += 1; + if (start_ZCS && 'h' == *spl_str) + spl_str += 1; + } + return spl_str; +} + +bool SpellingTrie::build_ym_info() { + bool sucess; + SpellingTable *spl_table = new SpellingTable(); + + sucess = spl_table->init_table(kMaxPinyinSize - 1, 2 * kMaxYmNum, false); + assert(sucess); + + for (uint16 pos = 0; pos < spelling_num_; pos++) { + const char *spl_str = spelling_buf_ + spelling_size_ * pos; + spl_str = get_ym_str(spl_str); + if ('\0' != spl_str[0]) { + sucess = spl_table->put_spelling(spl_str, 0); + assert(sucess); + } + } + + size_t ym_item_size; // '\0' is included + size_t ym_num; + const char* ym_buf; + ym_buf = spl_table->arrange(&ym_item_size, &ym_num); + + if (NULL != ym_buf_) + delete [] ym_buf_; + ym_buf_ = new char[ym_item_size * ym_num]; + if (NULL == ym_buf_) { + delete spl_table; + return false; + } + + memcpy(ym_buf_, ym_buf, sizeof(char) * ym_item_size * ym_num); + ym_size_ = ym_item_size; + ym_num_ = ym_num; + + delete spl_table; + + // Generate the maping from the spelling ids to the Yunmu ids. + if (spl_ym_ids_) + delete spl_ym_ids_; + spl_ym_ids_ = new uint8[spelling_num_ + kFullSplIdStart]; + if (NULL == spl_ym_ids_) + return false; + + memset(spl_ym_ids_, 0, sizeof(uint8) * (spelling_num_ + kFullSplIdStart)); + + for (uint16 id = 1; id < spelling_num_ + kFullSplIdStart; id++) { + const char *str = get_spelling_str(id); + + str = get_ym_str(str); + if ('\0' != str[0]) { + uint8 ym_id = get_ym_id(str); + spl_ym_ids_[id] = ym_id; + assert(ym_id > 0); + } else { + spl_ym_ids_[id] = 0; + } + } + return true; +} +#endif + +SpellingNode* SpellingTrie::construct_spellings_subset( + size_t item_start, size_t item_end, size_t level, SpellingNode* parent) { + if (level >= spelling_size_ || item_end <= item_start || NULL == parent) + return NULL; + + SpellingNode *first_son = NULL; + uint16 num_of_son = 0; + unsigned char min_son_score = 255; + + const char *spelling_last_start = spelling_buf_ + spelling_size_ * item_start; + char char_for_node = spelling_last_start[level]; + assert(char_for_node >= 'A' && char_for_node <= 'Z' || + 'h' == char_for_node); + + // Scan the array to find how many sons + for (size_t i = item_start + 1; i < item_end; i++) { + const char *spelling_current = spelling_buf_ + spelling_size_ * i; + char char_current = spelling_current[level]; + if (char_current != char_for_node) { + num_of_son++; + char_for_node = char_current; + } + } + num_of_son++; + + // Allocate memory +#ifdef ___BUILD_MODEL___ + node_num_ += num_of_son; +#endif + first_son = new SpellingNode[num_of_son]; + memset(first_son, 0, sizeof(SpellingNode)*num_of_son); + + // Now begin construct tree + size_t son_pos = 0; + + spelling_last_start = spelling_buf_ + spelling_size_ * item_start; + char_for_node = spelling_last_start[level]; + + bool spelling_endable = true; + if (spelling_last_start[level + 1] != '\0') + spelling_endable = false; + + size_t item_start_next = item_start; + + for (size_t i = item_start + 1; i < item_end; i++) { + const char *spelling_current = spelling_buf_ + spelling_size_ * i; + char char_current = spelling_current[level]; + assert(is_valid_spl_char(char_current)); + + if (char_current != char_for_node) { + // Construct a node + SpellingNode *node_current = first_son + son_pos; + node_current->char_this_node = char_for_node; + + // For quick search in the first level + if (0 == level) + level1_sons_[char_for_node - 'A'] = node_current; + + if (spelling_endable) { + node_current->spelling_idx = kFullSplIdStart + item_start_next; + } + + if (spelling_last_start[level + 1] != '\0' || i - item_start_next > 1) { + size_t real_start = item_start_next; + if (spelling_last_start[level + 1] == '\0') + real_start++; + + node_current->first_son = + construct_spellings_subset(real_start, i, level + 1, + node_current); + + if (real_start == item_start_next + 1) { + uint16 score_this = static_cast( + spelling_last_start[spelling_size_ - 1]); + if (score_this < node_current->score) + node_current->score = score_this; + } + } else { + node_current->first_son = NULL; + node_current->score = static_cast( + spelling_last_start[spelling_size_ - 1]); + } + + if (node_current->score < min_son_score) + min_son_score = node_current->score; + + bool is_half = false; + if (level == 0 && is_szm_char(char_for_node)) { + node_current->spelling_idx = + static_cast(char_for_node - 'A' + 1); + + if (char_for_node > 'C') + node_current->spelling_idx++; + if (char_for_node > 'S') + node_current->spelling_idx++; + + h2f_num_[node_current->spelling_idx] = i - item_start_next; + is_half = true; + } else if (level == 1 && char_for_node == 'h') { + char ch_level0 = spelling_last_start[0]; + uint16 part_id = 0; + if (ch_level0 == 'C') + part_id = 'C' - 'A' + 1 + 1; + else if (ch_level0 == 'S') + part_id = 'S' - 'A' + 1 + 2; + else if (ch_level0 == 'Z') + part_id = 'Z' - 'A' + 1 + 3; + if (0 != part_id) { + node_current->spelling_idx = part_id; + h2f_num_[node_current->spelling_idx] = i - item_start_next; + is_half = true; + } + } + + if (is_half) { + if (h2f_num_[node_current->spelling_idx] > 0) + h2f_start_[node_current->spelling_idx] = + item_start_next + kFullSplIdStart; + else + h2f_start_[node_current->spelling_idx] = 0; + } + + // for next sibling + spelling_last_start = spelling_current; + char_for_node = char_current; + item_start_next = i; + spelling_endable = true; + if (spelling_current[level + 1] != '\0') + spelling_endable = false; + + son_pos++; + } + } + + // the last one + SpellingNode *node_current = first_son + son_pos; + node_current->char_this_node = char_for_node; + + // For quick search in the first level + if (0 == level) + level1_sons_[char_for_node - 'A'] = node_current; + + if (spelling_endable) { + node_current->spelling_idx = kFullSplIdStart + item_start_next; + } + + if (spelling_last_start[level + 1] != '\0' || + item_end - item_start_next > 1) { + size_t real_start = item_start_next; + if (spelling_last_start[level + 1] == '\0') + real_start++; + + node_current->first_son = + construct_spellings_subset(real_start, item_end, level + 1, + node_current); + + if (real_start == item_start_next + 1) { + uint16 score_this = static_cast( + spelling_last_start[spelling_size_ - 1]); + if (score_this < node_current->score) + node_current->score = score_this; + } + } else { + node_current->first_son = NULL; + node_current->score = static_cast( + spelling_last_start[spelling_size_ - 1]); + } + + if (node_current->score < min_son_score) + min_son_score = node_current->score; + + assert(son_pos + 1 == num_of_son); + + bool is_half = false; + if (level == 0 && szm_is_enabled(char_for_node)) { + node_current->spelling_idx = static_cast(char_for_node - 'A' + 1); + + if (char_for_node > 'C') + node_current->spelling_idx++; + if (char_for_node > 'S') + node_current->spelling_idx++; + + h2f_num_[node_current->spelling_idx] = item_end - item_start_next; + is_half = true; + } else if (level == 1 && char_for_node == 'h') { + char ch_level0 = spelling_last_start[0]; + uint16 part_id = 0; + if (ch_level0 == 'C') + part_id = 'C' - 'A' + 1 + 1; + else if (ch_level0 == 'S') + part_id = 'S' - 'A' + 1 + 2; + else if (ch_level0 == 'Z') + part_id = 'Z' - 'A' + 1 + 3; + if (0 != part_id) { + node_current->spelling_idx = part_id; + h2f_num_[node_current->spelling_idx] = item_end - item_start_next; + is_half = true; + } + } + if (is_half) { + if (h2f_num_[node_current->spelling_idx] > 0) + h2f_start_[node_current->spelling_idx] = + item_start_next + kFullSplIdStart; + else + h2f_start_[node_current->spelling_idx] = 0; + } + + parent->num_of_son = num_of_son; + parent->score = min_son_score; + return first_son; +} + +bool SpellingTrie::save_spl_trie(FILE *fp) { + if (NULL == fp || NULL == spelling_buf_) + return false; + + if (fwrite(&spelling_size_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(&spelling_num_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fwrite(&score_amplifier_, sizeof(float), 1, fp) != 1) + return false; + + if (fwrite(&average_score_, sizeof(unsigned char), 1, fp) != 1) + return false; + + if (fwrite(spelling_buf_, sizeof(char) * spelling_size_, + spelling_num_, fp) != spelling_num_) + return false; + + return true; +} + +bool SpellingTrie::load_spl_trie(FILE *fp) { + if (NULL == fp) + return false; + + if (fread(&spelling_size_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fread(&spelling_num_, sizeof(size_t), 1, fp) != 1) + return false; + + if (fread(&score_amplifier_, sizeof(float), 1, fp) != 1) + return false; + + if (fread(&average_score_, sizeof(unsigned char), 1, fp) != 1) + return false; + + if (NULL != spelling_buf_) + delete [] spelling_buf_; + + spelling_buf_ = new char[spelling_size_ * spelling_num_]; + if (NULL == spelling_buf_) + return false; + + if (fread(spelling_buf_, sizeof(char) * spelling_size_, + spelling_num_, fp) != spelling_num_) + return false; + + return construct(spelling_buf_, spelling_size_, spelling_num_, + score_amplifier_, average_score_); +} + +bool SpellingTrie::build_f2h() { + if (NULL != f2h_) + delete [] f2h_; + f2h_ = new uint16[spelling_num_]; + if (NULL == f2h_) + return false; + + for (uint16 hid = 0; hid < kFullSplIdStart; hid++) { + for (uint16 fid = h2f_start_[hid]; + fid < h2f_start_[hid] + h2f_num_[hid]; fid++) + f2h_[fid - kFullSplIdStart] = hid; + } + + return true; +} + +size_t SpellingTrie::get_spelling_num() { + return spelling_num_; +} + +uint8 SpellingTrie::get_ym_id(const char *ym_str) { + if (NULL == ym_str || NULL == ym_buf_) + return 0; + + for (uint8 pos = 0; pos < ym_num_; pos++) + if (strcmp(ym_buf_ + ym_size_ * pos, ym_str) == 0) + return pos + 1; + + return 0; +} + +const char* SpellingTrie::get_spelling_str(uint16 splid) { + splstr_queried_[0] = '\0'; + + if (splid >= kFullSplIdStart) { + splid -= kFullSplIdStart; + snprintf(splstr_queried_, spelling_size_, "%s", + spelling_buf_ + splid * spelling_size_); + } else { + if (splid == 'C' - 'A' + 1 + 1) { + snprintf(splstr_queried_, spelling_size_, "%s", "Ch"); + } else if (splid == 'S' - 'A' + 1 + 2) { + snprintf(splstr_queried_, spelling_size_, "%s", "Sh"); + } else if (splid == 'Z' - 'A' + 1 + 3) { + snprintf(splstr_queried_, spelling_size_, "%s", "Zh"); + } else { + if (splid > 'C' - 'A' + 1) + splid--; + if (splid > 'S' - 'A' + 1) + splid--; + splstr_queried_[0] = 'A' + splid - 1; + splstr_queried_[1] = '\0'; + } + } + return splstr_queried_; +} + +const char16* SpellingTrie::get_spelling_str16(uint16 splid) { + splstr16_queried_[0] = '\0'; + + if (splid >= kFullSplIdStart) { + splid -= kFullSplIdStart; + for (size_t pos = 0; pos < spelling_size_; pos++) { + splstr16_queried_[pos] = static_cast + (spelling_buf_[splid * spelling_size_ + pos]); + } + } else { + if (splid == 'C' - 'A' + 1 + 1) { + splstr16_queried_[0] = static_cast('C'); + splstr16_queried_[1] = static_cast('h'); + splstr16_queried_[2] = static_cast('\0'); + } else if (splid == 'S' - 'A' + 1 + 2) { + splstr16_queried_[0] = static_cast('S'); + splstr16_queried_[1] = static_cast('h'); + splstr16_queried_[2] = static_cast('\0'); + } else if (splid == 'Z' - 'A' + 1 + 3) { + splstr16_queried_[0] = static_cast('Z'); + splstr16_queried_[1] = static_cast('h'); + splstr16_queried_[2] = static_cast('\0'); + } else { + if (splid > 'C' - 'A' + 1) + splid--; + if (splid > 'S' - 'A' + 1) + splid--; + splstr16_queried_[0] = 'A' + splid - 1; + splstr16_queried_[1] = '\0'; + } + } + return splstr16_queried_; +} + +size_t SpellingTrie::get_spelling_str16(uint16 splid, char16 *splstr16, + size_t splstr16_len) { + if (NULL == splstr16 || splstr16_len < kMaxPinyinSize + 1) return 0; + + if (splid >= kFullSplIdStart) { + splid -= kFullSplIdStart; + for (size_t pos = 0; pos <= kMaxPinyinSize; pos++) { + splstr16[pos] = static_cast + (spelling_buf_[splid * spelling_size_ + pos]); + if (static_cast('\0') == splstr16[pos]) { + return pos; + } + } + } else { + if (splid == 'C' - 'A' + 1 + 1) { + splstr16[0] = static_cast('C'); + splstr16[1] = static_cast('h'); + splstr16[2] = static_cast('\0'); + return 2; + } else if (splid == 'S' - 'A' + 1 + 2) { + splstr16[0] = static_cast('S'); + splstr16[1] = static_cast('h'); + splstr16[2] = static_cast('\0'); + return 2; + } else if (splid == 'Z' - 'A' + 1 + 3) { + splstr16[0] = static_cast('Z'); + splstr16[1] = static_cast('h'); + splstr16[2] = static_cast('\0'); + return 2; + } else { + if (splid > 'C' - 'A' + 1) + splid--; + if (splid > 'S' - 'A' + 1) + splid--; + splstr16[0] = 'A' + splid - 1; + splstr16[1] = '\0'; + return 1; + } + } + + // Not reachable. + return 0; +} + +} // namespace ime_pinyin diff --git a/jni/share/splparser.cpp b/jni/share/splparser.cpp new file mode 100644 index 0000000..d75aec6 --- /dev/null +++ b/jni/share/splparser.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "../include/splparser.h" + +namespace ime_pinyin { + +SpellingParser::SpellingParser() { + spl_trie_ = SpellingTrie::get_cpinstance(); +} + +bool SpellingParser::is_valid_to_parse(char ch) { + return SpellingTrie::is_valid_spl_char(ch); +} + +uint16 SpellingParser::splstr_to_idxs(const char *splstr, uint16 str_len, + uint16 spl_idx[], uint16 start_pos[], + uint16 max_size, bool &last_is_pre) { + if (NULL == splstr || 0 == max_size || 0 == str_len) + return 0; + + if (!SpellingTrie::is_valid_spl_char(splstr[0])) + return 0; + + last_is_pre = false; + + const SpellingNode *node_this = spl_trie_->root_; + + uint16 str_pos = 0; + uint16 idx_num = 0; + if (NULL != start_pos) + start_pos[0] = 0; + bool last_is_splitter = false; + + while (str_pos < str_len) { + char char_this = splstr[str_pos]; + // all characters outside of [a, z] are considered as splitters + if (!SpellingTrie::is_valid_spl_char(char_this)) { + // test if the current node is endable + uint16 id_this = node_this->spelling_idx; + if (spl_trie_->if_valid_id_update(&id_this)) { + spl_idx[idx_num] = id_this; + + idx_num++; + str_pos++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + if (idx_num >= max_size) + return idx_num; + + node_this = spl_trie_->root_; + last_is_splitter = true; + continue; + } else { + if (last_is_splitter) { + str_pos++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + continue; + } else { + return idx_num; + } + } + } + + last_is_splitter = false; + + SpellingNode *found_son = NULL; + + if (0 == str_pos) { + if (char_this >= 'a') + found_son = spl_trie_->level1_sons_[char_this - 'a']; + else + found_son = spl_trie_->level1_sons_[char_this - 'A']; + } else { + SpellingNode *first_son = node_this->first_son; + // Because for Zh/Ch/Sh nodes, they are the last in the buffer and + // frequently used, so we scan from the end. + for (int i = 0; i < node_this->num_of_son; i++) { + SpellingNode *this_son = first_son + i; + if (SpellingTrie::is_same_spl_char( + this_son->char_this_node, char_this)) { + found_son = this_son; + break; + } + } + } + + // found, just move the current node pointer to the the son + if (NULL != found_son) { + node_this = found_son; + } else { + // not found, test if it is endable + uint16 id_this = node_this->spelling_idx; + if (spl_trie_->if_valid_id_update(&id_this)) { + // endable, remember the index + spl_idx[idx_num] = id_this; + + idx_num++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + if (idx_num >= max_size) + return idx_num; + node_this = spl_trie_->root_; + continue; + } else { + return idx_num; + } + } + + str_pos++; + } + + uint16 id_this = node_this->spelling_idx; + if (spl_trie_->if_valid_id_update(&id_this)) { + // endable, remember the index + spl_idx[idx_num] = id_this; + + idx_num++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + } + + last_is_pre = !last_is_splitter; + + return idx_num; +} + +uint16 SpellingParser::splstr_to_idxs_f(const char *splstr, uint16 str_len, + uint16 spl_idx[], uint16 start_pos[], + uint16 max_size, bool &last_is_pre) { + uint16 idx_num = splstr_to_idxs(splstr, str_len, spl_idx, start_pos, + max_size, last_is_pre); + for (uint16 pos = 0; pos < idx_num; pos++) { + if (spl_trie_->is_half_id_yunmu(spl_idx[pos])) { + spl_trie_->half_to_full(spl_idx[pos], spl_idx + pos); + if (pos == idx_num - 1) { + last_is_pre = false; + } + } + } + return idx_num; +} + +uint16 SpellingParser::splstr16_to_idxs(const char16 *splstr, uint16 str_len, + uint16 spl_idx[], uint16 start_pos[], + uint16 max_size, bool &last_is_pre) { + if (NULL == splstr || 0 == max_size || 0 == str_len) + return 0; + + if (!SpellingTrie::is_valid_spl_char(splstr[0])) + return 0; + + last_is_pre = false; + + const SpellingNode *node_this = spl_trie_->root_; + + uint16 str_pos = 0; + uint16 idx_num = 0; + if (NULL != start_pos) + start_pos[0] = 0; + bool last_is_splitter = false; + + while (str_pos < str_len) { + char16 char_this = splstr[str_pos]; + // all characters outside of [a, z] are considered as splitters + if (!SpellingTrie::is_valid_spl_char(char_this)) { + // test if the current node is endable + uint16 id_this = node_this->spelling_idx; + if (spl_trie_->if_valid_id_update(&id_this)) { + spl_idx[idx_num] = id_this; + + idx_num++; + str_pos++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + if (idx_num >= max_size) + return idx_num; + + node_this = spl_trie_->root_; + last_is_splitter = true; + continue; + } else { + if (last_is_splitter) { + str_pos++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + continue; + } else { + return idx_num; + } + } + } + + last_is_splitter = false; + + SpellingNode *found_son = NULL; + + if (0 == str_pos) { + if (char_this >= 'a') + found_son = spl_trie_->level1_sons_[char_this - 'a']; + else + found_son = spl_trie_->level1_sons_[char_this - 'A']; + } else { + SpellingNode *first_son = node_this->first_son; + // Because for Zh/Ch/Sh nodes, they are the last in the buffer and + // frequently used, so we scan from the end. + for (int i = 0; i < node_this->num_of_son; i++) { + SpellingNode *this_son = first_son + i; + if (SpellingTrie::is_same_spl_char( + this_son->char_this_node, char_this)) { + found_son = this_son; + break; + } + } + } + + // found, just move the current node pointer to the the son + if (NULL != found_son) { + node_this = found_son; + } else { + // not found, test if it is endable + uint16 id_this = node_this->spelling_idx; + if (spl_trie_->if_valid_id_update(&id_this)) { + // endable, remember the index + spl_idx[idx_num] = id_this; + + idx_num++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + if (idx_num >= max_size) + return idx_num; + node_this = spl_trie_->root_; + continue; + } else { + return idx_num; + } + } + + str_pos++; + } + + uint16 id_this = node_this->spelling_idx; + if (spl_trie_->if_valid_id_update(&id_this)) { + // endable, remember the index + spl_idx[idx_num] = id_this; + + idx_num++; + if (NULL != start_pos) + start_pos[idx_num] = str_pos; + } + + last_is_pre = !last_is_splitter; + + return idx_num; +} + +uint16 SpellingParser::splstr16_to_idxs_f(const char16 *splstr, uint16 str_len, + uint16 spl_idx[], uint16 start_pos[], + uint16 max_size, bool &last_is_pre) { + uint16 idx_num = splstr16_to_idxs(splstr, str_len, spl_idx, start_pos, + max_size, last_is_pre); + for (uint16 pos = 0; pos < idx_num; pos++) { + if (spl_trie_->is_half_id_yunmu(spl_idx[pos])) { + spl_trie_->half_to_full(spl_idx[pos], spl_idx + pos); + if (pos == idx_num - 1) { + last_is_pre = false; + } + } + } + return idx_num; +} + +uint16 SpellingParser::get_splid_by_str(const char *splstr, uint16 str_len, + bool *is_pre) { + if (NULL == is_pre) + return 0; + + uint16 spl_idx[2]; + uint16 start_pos[3]; + + if (splstr_to_idxs(splstr, str_len, spl_idx, start_pos, 2, *is_pre) != 1) + return 0; + + if (start_pos[1] != str_len) + return 0; + return spl_idx[0]; +} + +uint16 SpellingParser::get_splid_by_str_f(const char *splstr, uint16 str_len, + bool *is_pre) { + if (NULL == is_pre) + return 0; + + uint16 spl_idx[2]; + uint16 start_pos[3]; + + if (splstr_to_idxs(splstr, str_len, spl_idx, start_pos, 2, *is_pre) != 1) + return 0; + + if (start_pos[1] != str_len) + return 0; + if (spl_trie_->is_half_id_yunmu(spl_idx[0])) { + spl_trie_->half_to_full(spl_idx[0], spl_idx); + *is_pre = false; + } + + return spl_idx[0]; +} + +uint16 SpellingParser::get_splids_parallel(const char *splstr, uint16 str_len, + uint16 splidx[], uint16 max_size, + uint16 &full_id_num, bool &is_pre) { + if (max_size <= 0 || !is_valid_to_parse(splstr[0])) + return 0; + + splidx[0] = get_splid_by_str(splstr, str_len, &is_pre); + full_id_num = 0; + if (0 != splidx[0]) { + if (splidx[0] >= kFullSplIdStart) + full_id_num = 1; + return 1; + } + return 0; +} + +} // namespace ime_pinyin diff --git a/jni/share/sync.cpp b/jni/share/sync.cpp new file mode 100644 index 0000000..91e27b8 --- /dev/null +++ b/jni/share/sync.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../include/sync.h" +#include +#include + +#ifdef ___SYNC_ENABLED___ + +namespace ime_pinyin { + +Sync::Sync() + : userdict_(NULL), + dictfile_(NULL), + last_count_(0) { +}; + +Sync::~Sync() { +} + + +bool Sync::begin(const char * filename) { + if (userdict_) { + finish(); + } + + if (!filename) { + return false; + } + + dictfile_ = strdup(filename); + if (!dictfile_) { + return false; + } + + userdict_ = new UserDict(); + if (!userdict_) { + free(dictfile_); + dictfile_ = NULL; + return false; + } + + if (userdict_->load_dict((const char*)dictfile_, kUserDictIdStart, + kUserDictIdEnd) == false) { + delete userdict_; + userdict_ = NULL; + free(dictfile_); + dictfile_ = NULL; + return false; + } + + userdict_->set_limit(kUserDictMaxLemmaCount, kUserDictMaxLemmaSize, kUserDictRatio); + + return true; +} + +int Sync::put_lemmas(char16 * lemmas, int len) { + return userdict_->put_lemmas_no_sync_from_utf16le_string(lemmas, len); +} + +int Sync::get_lemmas(char16 * str, int size) { + return userdict_->get_sync_lemmas_in_utf16le_string_from_beginning(str, size, &last_count_); +} + +int Sync::get_last_got_count() { + return last_count_; +} + +int Sync::get_total_count() { + return userdict_->get_sync_count(); +} + +void Sync::clear_last_got() { + if (last_count_ < 0) { + return; + } + userdict_->clear_sync_lemmas(0, last_count_); + last_count_ = 0; +} + +void Sync::finish() { + if (userdict_) { + userdict_->close_dict(); + delete userdict_; + userdict_ = NULL; + free(dictfile_); + dictfile_ = NULL; + last_count_ = 0; + } +} + +int Sync::get_capacity() { + UserDict::UserDictStat stat; + userdict_->state(&stat); + return stat.limit_lemma_count - stat.lemma_count; +} + +} +#endif diff --git a/jni/share/userdict.cpp b/jni/share/userdict.cpp new file mode 100644 index 0000000..5064296 --- /dev/null +++ b/jni/share/userdict.cpp @@ -0,0 +1,2262 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../include/userdict.h" +#include "../include/splparser.h" +#include "../include/ngram.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ime_pinyin { + +#ifdef ___DEBUG_PERF___ +static uint64 _ellapse_ = 0; +static struct timeval _tv_start_, _tv_end_; +#define DEBUG_PERF_BEGIN \ + do { \ + gettimeofday(&_tv_start_, NULL); \ + } while(0) +#define DEBUG_PERF_END \ + do { \ + gettimeofday(&_tv_end_, NULL); \ + _ellapse_ = (_tv_end_.tv_sec - _tv_start_.tv_sec) * 1000000 + \ + (_tv_end_.tv_usec - _tv_start_.tv_usec); \ + } while(0) +#define LOGD_PERF(message) \ + LOGD("PERFORMANCE[%s] %llu usec.", message, _ellapse_); +#else +#define DEBUG_PERF_BEGIN +#define DEBUG_PERF_END +#define LOGD_PERF(message) +#endif + +// XXX File load and write are thread-safe by g_mutex_ +static pthread_mutex_t g_mutex_ = PTHREAD_MUTEX_INITIALIZER; +static struct timeval g_last_update_ = {0, 0}; + +inline uint32 UserDict::get_dict_file_size(UserDictInfo * info) { + return (4 + info->lemma_size + (info->lemma_count << 3) +#ifdef ___PREDICT_ENABLED___ + + (info->lemma_count << 2) +#endif +#ifdef ___SYNC_ENABLED___ + + (info->sync_count << 2) +#endif + + sizeof(*info)); +} + +inline LmaScoreType UserDict::translate_score(int raw_score) { + // 1) ori_freq: original user frequency + uint32 ori_freq = extract_score_freq(raw_score); + // 2) lmt_off: lmt index (week offset for example) + uint64 lmt_off = ((raw_score & 0xffff0000) >> 16); + if (kUserDictLMTBitWidth < 16) { + uint64 mask = ~(1 << kUserDictLMTBitWidth); + lmt_off &= mask; + } + // 3) now_off: current time index (current week offset for example) + // assuming load_time_ is around current time + uint64 now_off = load_time_.tv_sec; + now_off = (now_off - kUserDictLMTSince) / kUserDictLMTGranularity; + now_off = (now_off << (64 - kUserDictLMTBitWidth)); + now_off = (now_off >> (64 - kUserDictLMTBitWidth)); + // 4) factor: decide expand-factor + int delta = now_off - lmt_off; + if (delta > 4) + delta = 4; + int factor = 80 - (delta << 4); + + double tf = (double)(dict_info_.total_nfreq + total_other_nfreq_); + return (LmaScoreType)(log((double)factor * (double)ori_freq / tf) + * NGram::kLogValueAmplifier); +} + +inline int UserDict::extract_score_freq(int raw_score) { + // Frequence stored in lowest 16 bits + int freq = (raw_score & 0x0000ffff); + return freq; +} + +inline uint64 UserDict::extract_score_lmt(int raw_score) { + uint64 lmt = ((raw_score & 0xffff0000) >> 16); + if (kUserDictLMTBitWidth < 16) { + uint64 mask = ~(1 << kUserDictLMTBitWidth); + lmt &= mask; + } + lmt = lmt * kUserDictLMTGranularity + kUserDictLMTSince; + return lmt; +} + +inline int UserDict::build_score(uint64 lmt, int freq) { + lmt = (lmt - kUserDictLMTSince) / kUserDictLMTGranularity; + lmt = (lmt << (64 - kUserDictLMTBitWidth)); + lmt = (lmt >> (64 - kUserDictLMTBitWidth)); + uint16 lmt16 = (uint16)lmt; + int s = freq; + s &= 0x0000ffff; + s = (lmt16 << 16) | s; + return s; +} + +inline int64 UserDict::utf16le_atoll(uint16 *s, int len) { + int64 ret = 0; + if (len <= 0) + return ret; + + int flag = 1; + const uint16 * endp = s + len; + if (*s == '-') { + flag = -1; + s++; + } else if (*s == '+') { + s++; + } + + while (*s >= '0' && *s <= '9' && s < endp) { + ret += ret * 10 + (*s) - '0'; + s++; + } + return ret * flag; +} + +inline int UserDict::utf16le_lltoa(int64 v, uint16 *s, int size) { + if (!s || size <= 0) + return 0; + uint16 *endp = s + size; + int ret_len = 0; + if (v < 0) { + *(s++) = '-'; + ++ret_len; + v *= -1; + } + + uint16 *b = s; + while (s < endp && v != 0) { + *(s++) = '0' + (v % 10); + v = v / 10; + ++ret_len; + } + + if (v != 0) + return 0; + + --s; + + while (b < s) { + *b = *s; + ++b, --s; + } + + return ret_len; +} + +inline void UserDict::set_lemma_flag(uint32 offset, uint8 flag) { + offset &= kUserDictOffsetMask; + lemmas_[offset] |= flag; +} + +inline char UserDict::get_lemma_flag(uint32 offset) { + offset &= kUserDictOffsetMask; + return (char)(lemmas_[offset]); +} + +inline char UserDict::get_lemma_nchar(uint32 offset) { + offset &= kUserDictOffsetMask; + return (char)(lemmas_[offset + 1]); +} + +inline uint16 * UserDict::get_lemma_spell_ids(uint32 offset) { + offset &= kUserDictOffsetMask; + return (uint16 *)(lemmas_ + offset + 2); +} + +inline uint16 * UserDict::get_lemma_word(uint32 offset) { + offset &= kUserDictOffsetMask; + uint8 nchar = get_lemma_nchar(offset); + return (uint16 *)(lemmas_ + offset + 2 + (nchar << 1)); +} + +inline LemmaIdType UserDict::get_max_lemma_id() { + // When a lemma is deleted, we don't not claim its id back for + // simplicity and performance + return start_id_ + dict_info_.lemma_count - 1; +} + +inline bool UserDict::is_valid_lemma_id(LemmaIdType id) { + if (id >= start_id_ && id <= get_max_lemma_id()) + return true; + return false; +} + +inline bool UserDict::is_valid_state() { + if (state_ == USER_DICT_NONE) + return false; + return true; +} + +UserDict::UserDict() + : start_id_(0), + version_(0), + lemmas_(NULL), + offsets_(NULL), + scores_(NULL), + ids_(NULL), +#ifdef ___PREDICT_ENABLED___ + predicts_(NULL), +#endif +#ifdef ___SYNC_ENABLED___ + syncs_(NULL), + sync_count_size_(0), +#endif + offsets_by_id_(NULL), + lemma_count_left_(0), + lemma_size_left_(0), + dict_file_(NULL), + state_(USER_DICT_NONE) { + memset(&dict_info_, 0, sizeof(dict_info_)); + memset(&load_time_, 0, sizeof(load_time_)); +#ifdef ___CACHE_ENABLED___ + cache_init(); +#endif +} + +UserDict::~UserDict() { + close_dict(); +} + +bool UserDict::load_dict(const char *file_name, LemmaIdType start_id, + LemmaIdType end_id) { +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_BEGIN; +#endif + dict_file_ = strdup(file_name); + if (!dict_file_) + return false; + + start_id_ = start_id; + + if (false == validate(file_name) && false == reset(file_name)) { + goto error; + } + if (false == load(file_name, start_id)) { + goto error; + } + + state_ = USER_DICT_SYNC; + + gettimeofday(&load_time_, NULL); + +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_END; + LOGD_PERF("load_dict"); +#endif + return true; + error: + free((void*)dict_file_); + start_id_ = 0; + return false; +} + +bool UserDict::close_dict() { + if (state_ == USER_DICT_NONE) + return true; + if (state_ == USER_DICT_SYNC) + goto out; + + // If dictionary is written back by others, + // we can not simply write back here + // To do a safe flush, we have to discard all newly added + // lemmas and try to reload dict file. + pthread_mutex_lock(&g_mutex_); + if (load_time_.tv_sec > g_last_update_.tv_sec || + (load_time_.tv_sec == g_last_update_.tv_sec && + load_time_.tv_usec > g_last_update_.tv_usec)) { + write_back(); + gettimeofday(&g_last_update_, NULL); + } + pthread_mutex_unlock(&g_mutex_); + + out: + free((void*)dict_file_); + free(lemmas_); + free(offsets_); + free(offsets_by_id_); + free(scores_); + free(ids_); +#ifdef ___PREDICT_ENABLED___ + free(predicts_); +#endif + + version_ = 0; + dict_file_ = NULL; + lemmas_ = NULL; +#ifdef ___SYNC_ENABLED___ + syncs_ = NULL; + sync_count_size_ = 0; +#endif + offsets_ = NULL; + offsets_by_id_ = NULL; + scores_ = NULL; + ids_ = NULL; +#ifdef ___PREDICT_ENABLED___ + predicts_ = NULL; +#endif + + memset(&dict_info_, 0, sizeof(dict_info_)); + lemma_count_left_ = 0; + lemma_size_left_ = 0; + state_ = USER_DICT_NONE; + + return true; +} + +size_t UserDict::number_of_lemmas() { + return dict_info_.lemma_count; +} + +void UserDict::reset_milestones(uint16 from_step, MileStoneHandle from_handle) { + return; +} + +MileStoneHandle UserDict::extend_dict(MileStoneHandle from_handle, + const DictExtPara *dep, + LmaPsbItem *lpi_items, + size_t lpi_max, size_t *lpi_num) { + if (is_valid_state() == false) + return 0; + + bool need_extend = false; + +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_BEGIN; +#endif + *lpi_num = _get_lpis(dep->splids, dep->splids_extended + 1, + lpi_items, lpi_max, &need_extend); +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_END; + LOGD_PERF("extend_dict"); +#endif + return ((*lpi_num > 0 || need_extend) ? 1 : 0); +} + +int UserDict::is_fuzzy_prefix_spell_id( + const uint16 * id1, uint16 len1, const UserDictSearchable *searchable) { + if (len1 < searchable->splids_len) + return 0; + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + uint32 i = 0; + for (i = 0; i < searchable->splids_len; i++) { + const char py1 = *spl_trie.get_spelling_str(id1[i]); + uint16 off = 8 * (i % 4); + const char py2 = ((searchable->signature[i/4] & (0xff << off)) >> off); + if (py1 == py2) + continue; + return 0; + } + return 1; +} + +int UserDict::fuzzy_compare_spell_id( + const uint16 * id1, uint16 len1, const UserDictSearchable *searchable) { + if (len1 < searchable->splids_len) + return -1; + if (len1 > searchable->splids_len) + return 1; + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + uint32 i = 0; + for (i = 0; i < len1; i++) { + const char py1 = *spl_trie.get_spelling_str(id1[i]); + uint16 off = 8 * (i % 4); + const char py2 = ((searchable->signature[i/4] & (0xff << off)) >> off); + if (py1 == py2) + continue; + if (py1 > py2) + return 1; + return -1; + } + return 0; +} + +bool UserDict::is_prefix_spell_id( + const uint16 * fullids, uint16 fulllen, + const UserDictSearchable *searchable) { + if (fulllen < searchable->splids_len) + return false; + + uint32 i = 0; + for (; i < searchable->splids_len; i++) { + uint16 start_id = searchable->splid_start[i]; + uint16 count = searchable->splid_count[i]; + if (fullids[i] >= start_id && fullids[i] < start_id + count) + continue; + else + return false; + } + return true; +} + +bool UserDict::equal_spell_id( + const uint16 * fullids, uint16 fulllen, + const UserDictSearchable *searchable) { + if (fulllen != searchable->splids_len) + return false; + + uint32 i = 0; + for (; i < fulllen; i++) { + uint16 start_id = searchable->splid_start[i]; + uint16 count = searchable->splid_count[i]; + if (fullids[i] >= start_id && fullids[i] < start_id + count) + continue; + else + return false; + } + return true; +} + +int32 UserDict::locate_first_in_offsets(const UserDictSearchable * searchable) { + int32 begin = 0; + int32 end = dict_info_.lemma_count - 1; + int32 middle = -1; + + int32 first_prefix = middle; + int32 last_matched = middle; + + while (begin <= end) { + middle = (begin + end) >> 1; + uint32 offset = offsets_[middle]; + uint8 nchar = get_lemma_nchar(offset); + const uint16 * splids = get_lemma_spell_ids(offset); + int cmp = fuzzy_compare_spell_id(splids, nchar, searchable); + int pre = is_fuzzy_prefix_spell_id(splids, nchar, searchable); + + if (pre) + first_prefix = middle; + + if (cmp < 0) { + begin = middle + 1; + } else if (cmp > 0) { + end = middle - 1; + } else { + end = middle - 1; + last_matched = middle; + } + } + + return first_prefix; +} + +void UserDict::prepare_locate(UserDictSearchable *searchable, + const uint16 *splid_str, + uint16 splid_str_len) { + searchable->splids_len = splid_str_len; + memset(searchable->signature, 0, sizeof(searchable->signature)); + + SpellingTrie &spl_trie = SpellingTrie::get_instance(); + uint32 i = 0; + for (; i < splid_str_len; i++) { + if (spl_trie.is_half_id(splid_str[i])) { + searchable->splid_count[i] = + spl_trie.half_to_full(splid_str[i], + &(searchable->splid_start[i])); + } else { + searchable->splid_count[i] = 1; + searchable->splid_start[i] = splid_str[i]; + } + const unsigned char py = *spl_trie.get_spelling_str(splid_str[i]); + searchable->signature[i>>2] |= (py << (8 * (i % 4))); + } +} + +size_t UserDict::get_lpis(const uint16 *splid_str, uint16 splid_str_len, + LmaPsbItem *lpi_items, size_t lpi_max) { + return _get_lpis(splid_str, splid_str_len, lpi_items, lpi_max, NULL); +} + +size_t UserDict::_get_lpis(const uint16 *splid_str, + uint16 splid_str_len, LmaPsbItem *lpi_items, + size_t lpi_max, bool * need_extend) { + bool tmp_extend; + if (!need_extend) + need_extend = &tmp_extend; + + *need_extend = false; + + if (is_valid_state() == false) + return 0; + if (lpi_max <= 0) + return 0; + + if (0 == pthread_mutex_trylock(&g_mutex_)) { + if (load_time_.tv_sec < g_last_update_.tv_sec || + (load_time_.tv_sec == g_last_update_.tv_sec && + load_time_.tv_usec < g_last_update_.tv_usec)) { + // Others updated disk file, have to reload + pthread_mutex_unlock(&g_mutex_); + flush_cache(); + } else { + pthread_mutex_unlock(&g_mutex_); + } + } else { + } + + UserDictSearchable searchable; + prepare_locate(&searchable, splid_str, splid_str_len); + + uint32 max_off = dict_info_.lemma_count; +#ifdef ___CACHE_ENABLED___ + int32 middle; + uint32 start, count; + bool cached = cache_hit(&searchable, &start, &count); + if (cached) { + middle = start; + max_off = start + count; + } else { + middle = locate_first_in_offsets(&searchable); + start = middle; + } +#else + int32 middle = locate_first_in_offsets(&searchable); +#endif + + if (middle == -1) { +#ifdef ___CACHE_ENABLED___ + if (!cached) + cache_push(USER_DICT_MISS_CACHE, &searchable, 0, 0); +#endif + return 0; + } + + size_t lpi_current = 0; + + bool fuzzy_break = false; + bool prefix_break = false; + while ((size_t)middle < max_off && !fuzzy_break && !prefix_break) { + if (lpi_current >= lpi_max) + break; + uint32 offset = offsets_[middle]; + // Ignore deleted lemmas + if (offset & kUserDictOffsetFlagRemove) { + middle++; + continue; + } + uint8 nchar = get_lemma_nchar(offset); + uint16 * splids = get_lemma_spell_ids(offset); +#ifdef ___CACHE_ENABLED___ + if (!cached && 0 != fuzzy_compare_spell_id(splids, nchar, &searchable)) { +#else + if (0 != fuzzy_compare_spell_id(splids, nchar, &searchable)) { +#endif + fuzzy_break = true; + } + + if (prefix_break == false) { + if (is_fuzzy_prefix_spell_id(splids, nchar, &searchable)) { + if (*need_extend == false && + is_prefix_spell_id(splids, nchar, &searchable)) { + *need_extend = true; + } + } else { + prefix_break = true; + } + } + + if (equal_spell_id(splids, nchar, &searchable) == true) { + lpi_items[lpi_current].psb = translate_score(scores_[middle]); + lpi_items[lpi_current].id = ids_[middle]; + lpi_items[lpi_current].lma_len = nchar; + lpi_current++; + } + middle++; + } + +#ifdef ___CACHE_ENABLED___ + if (!cached) { + count = middle - start; + cache_push(USER_DICT_CACHE, &searchable, start, count); + } +#endif + + return lpi_current; +} + +uint16 UserDict::get_lemma_str(LemmaIdType id_lemma, char16* str_buf, + uint16 str_max) { + if (is_valid_state() == false) + return 0; + if (is_valid_lemma_id(id_lemma) == false) + return 0; + uint32 offset = offsets_by_id_[id_lemma - start_id_]; + uint8 nchar = get_lemma_nchar(offset); + char16 * str = get_lemma_word(offset); + uint16 m = nchar < str_max -1 ? nchar : str_max - 1; + int i = 0; + for (; i < m; i++) { + str_buf[i] = str[i]; + } + str_buf[i] = 0; + return m; +} + +uint16 UserDict::get_lemma_splids(LemmaIdType id_lemma, uint16 *splids, + uint16 splids_max, bool arg_valid) { + if (is_valid_lemma_id(id_lemma) == false) + return 0; + uint32 offset = offsets_by_id_[id_lemma - start_id_]; + uint8 nchar = get_lemma_nchar(offset); + const uint16 * ids = get_lemma_spell_ids(offset); + int i = 0; + for (; i < nchar && i < splids_max; i++) + splids[i] = ids[i]; + return i; +} + +size_t UserDict::predict(const char16 last_hzs[], uint16 hzs_len, + NPredictItem *npre_items, size_t npre_max, + size_t b4_used) { + uint32 new_added = 0; +#ifdef ___PREDICT_ENABLED___ + int32 end = dict_info_.lemma_count - 1; + int j = locate_first_in_predicts((const uint16*)last_hzs, hzs_len); + if (j == -1) + return 0; + + while (j <= end) { + uint32 offset = predicts_[j]; + // Ignore deleted lemmas + if (offset & kUserDictOffsetFlagRemove) { + j++; + continue; + } + uint32 nchar = get_lemma_nchar(offset); + uint16 * words = get_lemma_word(offset); + uint16 * splids = get_lemma_spell_ids(offset); + + if (nchar <= hzs_len) { + j++; + continue; + } + + if (memcmp(words, last_hzs, hzs_len << 1) == 0) { + if (new_added >= npre_max) { + return new_added; + } + uint32 cpy_len = + (nchar < kMaxPredictSize ? (nchar << 1) : (kMaxPredictSize << 1)) + - (hzs_len << 1); + npre_items[new_added].his_len = hzs_len; + npre_items[new_added].psb = get_lemma_score(words, splids, nchar); + memcpy(npre_items[new_added].pre_hzs, words + hzs_len, cpy_len); + if ((cpy_len >> 1) < kMaxPredictSize) { + npre_items[new_added].pre_hzs[cpy_len >> 1] = 0; + } + new_added++; + } else { + break; + } + + j++; + } +#endif + return new_added; +} + +int32 UserDict::locate_in_offsets(char16 lemma_str[], uint16 splid_str[], + uint16 lemma_len) { + int32 max_off = dict_info_.lemma_count; + + UserDictSearchable searchable; + prepare_locate(&searchable, splid_str, lemma_len); +#ifdef ___CACHE_ENABLED___ + int32 off; + uint32 start, count; + bool cached = load_cache(&searchable, &start, &count); + if (cached) { + off = start; + max_off = start + count; + } else { + off = locate_first_in_offsets(&searchable); + start = off; + } +#else + int32 off = locate_first_in_offsets(&searchable); +#endif + + if (off == -1) { + return off; + } + + while (off < max_off) { + uint32 offset = offsets_[off]; + if (offset & kUserDictOffsetFlagRemove) { + off++; + continue; + } + uint16 * splids = get_lemma_spell_ids(offset); +#ifdef ___CACHE_ENABLED___ + if (!cached && 0 != fuzzy_compare_spell_id(splids, lemma_len, &searchable)) + break; +#else + if (0 != fuzzy_compare_spell_id(splids, lemma_len, &searchable)) + break; +#endif + if (equal_spell_id(splids, lemma_len, &searchable) == true) { + uint16 * str = get_lemma_word(offset); + uint32 i = 0; + for (i = 0; i < lemma_len; i++) { + if (str[i] == lemma_str[i]) + continue; + break; + } + if (i < lemma_len) { + off++; + continue; + } +#ifdef ___CACHE_ENABLED___ + // No need to save_cache here, since current function is invoked by + // put_lemma. It's rarely possible for a user input same lemma twice. + // That means first time user type a new lemma, it is newly added into + // user dictionary, then it's possible that user type the same lemma + // again. + // Another reason save_cache can not be invoked here is this function + // aborts when lemma is found, and it never knows the count. +#endif + return off; + } + off++; + } + + return -1; +} + +#ifdef ___PREDICT_ENABLED___ +uint32 UserDict::locate_where_to_insert_in_predicts( + const uint16 * words, int lemma_len) { + int32 begin = 0; + int32 end = dict_info_.lemma_count - 1; + int32 middle = end; + + uint32 last_matched = middle; + + while (begin <= end) { + middle = (begin + end) >> 1; + uint32 offset = offsets_[middle]; + uint8 nchar = get_lemma_nchar(offset); + const uint16 * ws = get_lemma_word(offset); + + uint32 minl = nchar < lemma_len ? nchar : lemma_len; + uint32 k = 0; + int cmp = 0; + + for (; k < minl; k++) { + if (ws[k] < words[k]) { + cmp = -1; + break; + } else if (ws[k] > words[k]) { + cmp = 1; + break; + } + } + if (cmp == 0) { + if (nchar < lemma_len) + cmp = -1; + else if (nchar > lemma_len) + cmp = 1; + } + + if (cmp < 0) { + begin = middle + 1; + last_matched = middle; + } else if (cmp > 0) { + end = middle - 1; + } else { + end = middle - 1; + last_matched = middle; + } + } + + return last_matched; +} + +int32 UserDict::locate_first_in_predicts(const uint16 * words, int lemma_len) { + int32 begin = 0; + int32 end = dict_info_.lemma_count - 1; + int32 middle = -1; + + int32 last_matched = middle; + + while (begin <= end) { + middle = (begin + end) >> 1; + uint32 offset = offsets_[middle]; + uint8 nchar = get_lemma_nchar(offset); + const uint16 * ws = get_lemma_word(offset); + + uint32 minl = nchar < lemma_len ? nchar : lemma_len; + uint32 k = 0; + int cmp = 0; + + for (; k < minl; k++) { + if (ws[k] < words[k]) { + cmp = -1; + break; + } else if (ws[k] > words[k]) { + cmp = 1; + break; + } + } + if (cmp == 0) { + if (nchar >= lemma_len) + last_matched = middle; + if (nchar < lemma_len) + cmp = -1; + else if (nchar > lemma_len) + cmp = 1; + } + + if (cmp < 0) { + begin = middle + 1; + } else if (cmp > 0) { + end = middle - 1; + } else { + end = middle - 1; + } + } + + return last_matched; +} + +#endif + +LemmaIdType UserDict::get_lemma_id(char16 lemma_str[], uint16 splids[], + uint16 lemma_len) { + int32 off = locate_in_offsets(lemma_str, splids, lemma_len); + if (off == -1) { + return 0; + } + + return ids_[off]; +} + +LmaScoreType UserDict::get_lemma_score(LemmaIdType lemma_id) { + if (is_valid_state() == false) + return 0; + if (is_valid_lemma_id(lemma_id) == false) + return 0; + + return translate_score(_get_lemma_score(lemma_id)); +} + +LmaScoreType UserDict::get_lemma_score(char16 lemma_str[], uint16 splids[], + uint16 lemma_len) { + if (is_valid_state() == false) + return 0; + return translate_score(_get_lemma_score(lemma_str, splids, lemma_len)); +} + +int UserDict::_get_lemma_score(LemmaIdType lemma_id) { + if (is_valid_state() == false) + return 0; + if (is_valid_lemma_id(lemma_id) == false) + return 0; + + uint32 offset = offsets_by_id_[lemma_id - start_id_]; + + uint32 nchar = get_lemma_nchar(offset); + uint16 * spl = get_lemma_spell_ids(offset); + uint16 * wrd = get_lemma_word(offset); + + int32 off = locate_in_offsets(wrd, spl, nchar); + if (off == -1) { + return 0; + } + + return scores_[off]; +} + +int UserDict::_get_lemma_score(char16 lemma_str[], uint16 splids[], + uint16 lemma_len) { + if (is_valid_state() == false) + return 0; + + int32 off = locate_in_offsets(lemma_str, splids, lemma_len); + if (off == -1) { + return 0; + } + + return scores_[off]; +} + +#ifdef ___SYNC_ENABLED___ +void UserDict::remove_lemma_from_sync_list(uint32 offset) { + offset &= kUserDictOffsetMask; + uint32 i = 0; + for (; i < dict_info_.sync_count; i++) { + unsigned int off = (syncs_[i] & kUserDictOffsetMask); + if (off == offset) + break; + } + if (i < dict_info_.sync_count) { + syncs_[i] = syncs_[dict_info_.sync_count - 1]; + dict_info_.sync_count--; + } +} +#endif + +#ifdef ___PREDICT_ENABLED___ +void UserDict::remove_lemma_from_predict_list(uint32 offset) { + offset &= kUserDictOffsetMask; + uint32 i = 0; + for (; i < dict_info_.lemma_count; i++) { + unsigned int off = (predicts_[i] & kUserDictOffsetMask); + if (off == offset) { + predicts_[i] |= kUserDictOffsetFlagRemove; + break; + } + } +} +#endif + +bool UserDict::remove_lemma_by_offset_index(int offset_index) { + if (is_valid_state() == false) + return 0; + + int32 off = offset_index; + if (off == -1) { + return false; + } + + uint32 offset = offsets_[off]; + uint32 nchar = get_lemma_nchar(offset); + + offsets_[off] |= kUserDictOffsetFlagRemove; + +#ifdef ___SYNC_ENABLED___ + // Remove corresponding sync item + remove_lemma_from_sync_list(offset); +#endif + +#ifdef ___PREDICT_ENABLED___ + remove_lemma_from_predict_list(offset); +#endif + dict_info_.free_count++; + dict_info_.free_size += (2 + (nchar << 2)); + + if (state_ < USER_DICT_OFFSET_DIRTY) + state_ = USER_DICT_OFFSET_DIRTY; + return true; +} + +bool UserDict::remove_lemma(LemmaIdType lemma_id) { + if (is_valid_state() == false) + return 0; + if (is_valid_lemma_id(lemma_id) == false) + return false; + uint32 offset = offsets_by_id_[lemma_id - start_id_]; + + uint32 nchar = get_lemma_nchar(offset); + uint16 * spl = get_lemma_spell_ids(offset); + uint16 * wrd = get_lemma_word(offset); + + int32 off = locate_in_offsets(wrd, spl, nchar); + + return remove_lemma_by_offset_index(off); +} + +void UserDict::flush_cache() { + LemmaIdType start_id = start_id_; + const char * file = strdup(dict_file_); + if (!file) + return; + close_dict(); + load_dict(file, start_id, kUserDictIdEnd); + free((void*)file); +#ifdef ___CACHE_ENABLED___ + cache_init(); +#endif + return; +} + +bool UserDict::reset(const char *file) { + FILE *fp = fopen(file, "w+"); + if (!fp) { + return false; + } + uint32 version = kUserDictVersion; + size_t wred = fwrite(&version, 1, 4, fp); + UserDictInfo info; + memset(&info, 0, sizeof(info)); + // By default, no limitation for lemma count and size + // thereby, reclaim_ratio is never used + wred += fwrite(&info, 1, sizeof(info), fp); + if (wred != sizeof(info) + sizeof(version)) { + fclose(fp); + unlink(file); + return false; + } + fclose(fp); + return true; +} + +bool UserDict::validate(const char *file) { + // b is ignored in POSIX compatible os including Linux + // while b is important flag for Windows to specify binary mode + FILE *fp = fopen(file, "rb"); + if (!fp) { + return false; + } + + size_t size; + size_t readed; + uint32 version; + UserDictInfo dict_info; + + // validate + int err = fseek(fp, 0, SEEK_END); + if (err) { + goto error; + } + + size = ftell(fp); + if (size < 4 + sizeof(dict_info)) { + goto error; + } + + err = fseek(fp, 0, SEEK_SET); + if (err) { + goto error; + } + + readed = fread(&version, 1, sizeof(version), fp); + if (readed < sizeof(version)) { + goto error; + } + if (version != kUserDictVersion) { + goto error; + } + + err = fseek(fp, -1 * sizeof(dict_info), SEEK_END); + if (err) { + goto error; + } + + readed = fread(&dict_info, 1, sizeof(dict_info), fp); + if (readed != sizeof(dict_info)) { + goto error; + } + + if (size != get_dict_file_size(&dict_info)) { + goto error; + } + + fclose(fp); + return true; + + error: + fclose(fp); + return false; +} + +bool UserDict::load(const char *file, LemmaIdType start_id) { + if (0 != pthread_mutex_trylock(&g_mutex_)) { + return false; + } + // b is ignored in POSIX compatible os including Linux + // while b is important flag for Windows to specify binary mode + FILE *fp = fopen(file, "rb"); + if (!fp) { + pthread_mutex_unlock(&g_mutex_); + return false; + } + + size_t readed, toread; + UserDictInfo dict_info; + uint8 *lemmas = NULL; + uint32 *offsets = NULL; +#ifdef ___SYNC_ENABLED___ + uint32 *syncs = NULL; +#endif + uint32 *scores = NULL; + uint32 *ids = NULL; + uint32 *offsets_by_id = NULL; +#ifdef ___PREDICT_ENABLED___ + uint32 *predicts = NULL; +#endif + size_t i; + int err; + + err = fseek(fp, -1 * sizeof(dict_info), SEEK_END); + if (err) goto error; + + readed = fread(&dict_info, 1, sizeof(dict_info), fp); + if (readed != sizeof(dict_info)) goto error; + + lemmas = (uint8 *)malloc( + dict_info.lemma_size + + (kUserDictPreAlloc * (2 + (kUserDictAverageNchar << 2)))); + + if (!lemmas) goto error; + + offsets = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); + if (!offsets) goto error; + +#ifdef ___PREDICT_ENABLED___ + predicts = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); + if (!predicts) goto error; +#endif + +#ifdef ___SYNC_ENABLED___ + syncs = (uint32 *)malloc((dict_info.sync_count + kUserDictPreAlloc) << 2); + if (!syncs) goto error; +#endif + + scores = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); + if (!scores) goto error; + + ids = (uint32 *)malloc((dict_info.lemma_count + kUserDictPreAlloc) << 2); + if (!ids) goto error; + + offsets_by_id = (uint32 *)malloc( + (dict_info.lemma_count + kUserDictPreAlloc) << 2); + if (!offsets_by_id) goto error; + + err = fseek(fp, 4, SEEK_SET); + if (err) goto error; + + readed = 0; + while (readed < dict_info.lemma_size && !ferror(fp) && !feof(fp)) { + readed += fread(lemmas + readed, 1, dict_info.lemma_size - readed, fp); + } + if (readed < dict_info.lemma_size) + goto error; + + toread = (dict_info.lemma_count << 2); + readed = 0; + while (readed < toread && !ferror(fp) && !feof(fp)) { + readed += fread((((uint8*)offsets) + readed), 1, toread - readed, fp); + } + if (readed < toread) + goto error; + +#ifdef ___PREDICT_ENABLED___ + toread = (dict_info.lemma_count << 2); + readed = 0; + while (readed < toread && !ferror(fp) && !feof(fp)) { + readed += fread((((uint8*)predicts) + readed), 1, toread - readed, fp); + } + if (readed < toread) + goto error; +#endif + + readed = 0; + while (readed < toread && !ferror(fp) && !feof(fp)) { + readed += fread((((uint8*)scores) + readed), 1, toread - readed, fp); + } + if (readed < toread) + goto error; + +#ifdef ___SYNC_ENABLED___ + toread = (dict_info.sync_count << 2); + readed = 0; + while (readed < toread && !ferror(fp) && !feof(fp)) { + readed += fread((((uint8*)syncs) + readed), 1, toread - readed, fp); + } + if (readed < toread) + goto error; +#endif + + for (i = 0; i < dict_info.lemma_count; i++) { + ids[i] = start_id + i; + offsets_by_id[i] = offsets[i]; + } + + lemmas_ = lemmas; + offsets_ = offsets; +#ifdef ___SYNC_ENABLED___ + syncs_ = syncs; + sync_count_size_ = dict_info.sync_count + kUserDictPreAlloc; +#endif + offsets_by_id_ = offsets_by_id; + scores_ = scores; + ids_ = ids; +#ifdef ___PREDICT_ENABLED___ + predicts_ = predicts; +#endif + lemma_count_left_ = kUserDictPreAlloc; + lemma_size_left_ = kUserDictPreAlloc * (2 + (kUserDictAverageNchar << 2)); + memcpy(&dict_info_, &dict_info, sizeof(dict_info)); + state_ = USER_DICT_SYNC; + + fclose(fp); + + pthread_mutex_unlock(&g_mutex_); + return true; + + error: + if (lemmas) free(lemmas); + if (offsets) free(offsets); +#ifdef ___SYNC_ENABLED___ + if (syncs) free(syncs); +#endif + if (scores) free(scores); + if (ids) free(ids); + if (offsets_by_id) free(offsets_by_id); +#ifdef ___PREDICT_ENABLED___ + if (predicts) free(predicts); +#endif + fclose(fp); + pthread_mutex_unlock(&g_mutex_); + return false; +} + +void UserDict::write_back() { + // XXX write back is only allowed from close_dict due to thread-safe sake + if (state_ == USER_DICT_NONE || state_ == USER_DICT_SYNC) + return; + int fd = open(dict_file_, O_WRONLY); + if (fd == -1) + return; + switch (state_) { + case USER_DICT_DEFRAGMENTED: + write_back_all(fd); + break; + case USER_DICT_LEMMA_DIRTY: + write_back_lemma(fd); + break; + case USER_DICT_OFFSET_DIRTY: + write_back_offset(fd); + break; + case USER_DICT_SCORE_DIRTY: + write_back_score(fd); + break; +#ifdef ___SYNC_ENABLED___ + case USER_DICT_SYNC_DIRTY: + write_back_sync(fd); + break; +#endif + default: + break; + } + // It seems truncate is not need on Linux, Windows except Mac + // I am doing it here anyway for safety. + off_t cur = lseek(fd, 0, SEEK_CUR); + ftruncate(fd, cur); + close(fd); + state_ = USER_DICT_SYNC; +} + +#ifdef ___SYNC_ENABLED___ +void UserDict::write_back_sync(int fd) { + int err = lseek(fd, 4 + dict_info_.lemma_size + + (dict_info_.lemma_count << 3) +#ifdef ___PREDICT_ENABLED___ + + (dict_info_.lemma_count << 2) +#endif + , SEEK_SET); + if (err == -1) + return; + write(fd, syncs_, dict_info_.sync_count << 2); + write(fd, &dict_info_, sizeof(dict_info_)); +} +#endif + +void UserDict::write_back_offset(int fd) { + int err = lseek(fd, 4 + dict_info_.lemma_size, SEEK_SET); + if (err == -1) + return; + write(fd, offsets_, dict_info_.lemma_count << 2); +#ifdef ___PREDICT_ENABLED___ + write(fd, predicts_, dict_info_.lemma_count << 2); +#endif + write(fd, scores_, dict_info_.lemma_count << 2); +#ifdef ___SYNC_ENABLED___ + write(fd, syncs_, dict_info_.sync_count << 2); +#endif + write(fd, &dict_info_, sizeof(dict_info_)); +} + +void UserDict::write_back_score(int fd) { + int err = lseek(fd, 4 + dict_info_.lemma_size + + (dict_info_.lemma_count << 2) +#ifdef ___PREDICT_ENABLED___ + + (dict_info_.lemma_count << 2) +#endif + , SEEK_SET); + if (err == -1) + return; + write(fd, scores_, dict_info_.lemma_count << 2); +#ifdef ___SYNC_ENABLED___ + write(fd, syncs_, dict_info_.sync_count << 2); +#endif + write(fd, &dict_info_, sizeof(dict_info_)); +} + +void UserDict::write_back_lemma(int fd) { + int err = lseek(fd, 4, SEEK_SET); + if (err == -1) + return; + // New lemmas are always appended, no need to write whole lemma block + size_t need_write = kUserDictPreAlloc * + (2 + (kUserDictAverageNchar << 2)) - lemma_size_left_; + err = lseek(fd, dict_info_.lemma_size - need_write, SEEK_CUR); + if (err == -1) + return; + write(fd, lemmas_ + dict_info_.lemma_size - need_write, need_write); + + write(fd, offsets_, dict_info_.lemma_count << 2); +#ifdef ___PREDICT_ENABLED___ + write(fd, predicts_, dict_info_.lemma_count << 2); +#endif + write(fd, scores_, dict_info_.lemma_count << 2); +#ifdef ___SYNC_ENABLED___ + write(fd, syncs_, dict_info_.sync_count << 2); +#endif + write(fd, &dict_info_, sizeof(dict_info_)); +} + +void UserDict::write_back_all(int fd) { + // XXX lemma_size is handled differently in writeall + // and writelemma. I update lemma_size and lemma_count in different + // places for these two cases. Should fix it to make it consistent. + int err = lseek(fd, 4, SEEK_SET); + if (err == -1) + return; + write(fd, lemmas_, dict_info_.lemma_size); + write(fd, offsets_, dict_info_.lemma_count << 2); +#ifdef ___PREDICT_ENABLED___ + write(fd, predicts_, dict_info_.lemma_count << 2); +#endif + write(fd, scores_, dict_info_.lemma_count << 2); +#ifdef ___SYNC_ENABLED___ + write(fd, syncs_, dict_info_.sync_count << 2); +#endif + write(fd, &dict_info_, sizeof(dict_info_)); +} + +#ifdef ___CACHE_ENABLED___ +bool UserDict::load_cache(UserDictSearchable *searchable, + uint32 *offset, uint32 *length) { + UserDictCache *cache = &caches_[searchable->splids_len - 1]; + if (cache->head == cache->tail) + return false; + + uint16 j, sig_len = kMaxLemmaSize / 4; + uint16 i = cache->head; + while (1) { + j = 0; + for (; j < sig_len; j++) { + if (cache->signatures[i][j] != searchable->signature[j]) + break; + } + if (j < sig_len) { + i++; + if (i >= kUserDictCacheSize) + i -= kUserDictCacheSize; + if (i == cache->tail) + break; + continue; + } + *offset = cache->offsets[i]; + *length = cache->lengths[i]; + return true; + } + return false; +} + +void UserDict::save_cache(UserDictSearchable *searchable, + uint32 offset, uint32 length) { + UserDictCache *cache = &caches_[searchable->splids_len - 1]; + uint16 next = cache->tail; + + cache->offsets[next] = offset; + cache->lengths[next] = length; + uint16 sig_len = kMaxLemmaSize / 4; + uint16 j = 0; + for (; j < sig_len; j++) { + cache->signatures[next][j] = searchable->signature[j]; + } + + if (++next >= kUserDictCacheSize) { + next -= kUserDictCacheSize; + } + if (next == cache->head) { + cache->head++; + if (cache->head >= kUserDictCacheSize) { + cache->head -= kUserDictCacheSize; + } + } + cache->tail = next; +} + +void UserDict::reset_cache() { + memset(caches_, 0, sizeof(caches_)); +} + +bool UserDict::load_miss_cache(UserDictSearchable *searchable) { + UserDictMissCache *cache = &miss_caches_[searchable->splids_len - 1]; + if (cache->head == cache->tail) + return false; + + uint16 j, sig_len = kMaxLemmaSize / 4; + uint16 i = cache->head; + while (1) { + j = 0; + for (; j < sig_len; j++) { + if (cache->signatures[i][j] != searchable->signature[j]) + break; + } + if (j < sig_len) { + i++; + if (i >= kUserDictMissCacheSize) + i -= kUserDictMissCacheSize; + if (i == cache->tail) + break; + continue; + } + return true; + } + return false; +} + +void UserDict::save_miss_cache(UserDictSearchable *searchable) { + UserDictMissCache *cache = &miss_caches_[searchable->splids_len - 1]; + uint16 next = cache->tail; + + uint16 sig_len = kMaxLemmaSize / 4; + uint16 j = 0; + for (; j < sig_len; j++) { + cache->signatures[next][j] = searchable->signature[j]; + } + + if (++next >= kUserDictMissCacheSize) { + next -= kUserDictMissCacheSize; + } + if (next == cache->head) { + cache->head++; + if (cache->head >= kUserDictMissCacheSize) { + cache->head -= kUserDictMissCacheSize; + } + } + cache->tail = next; +} + +void UserDict::reset_miss_cache() { + memset(miss_caches_, 0, sizeof(miss_caches_)); +} + +void UserDict::cache_init() { + reset_cache(); + reset_miss_cache(); +} + +bool UserDict::cache_hit(UserDictSearchable *searchable, + uint32 *offset, uint32 *length) { + bool hit = load_miss_cache(searchable); + if (hit) { + *offset = 0; + *length = 0; + return true; + } + hit = load_cache(searchable, offset, length); + if (hit) { + return true; + } + return false; +} + +void UserDict::cache_push(UserDictCacheType type, + UserDictSearchable *searchable, + uint32 offset, uint32 length) { + switch (type) { + case USER_DICT_MISS_CACHE: + save_miss_cache(searchable); + break; + case USER_DICT_CACHE: + save_cache(searchable, offset, length); + break; + default: + break; + } +} + +#endif + +void UserDict::defragment(void) { +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_BEGIN; +#endif + if (is_valid_state() == false) + return; + // Fixup offsets_, set REMOVE flag to lemma's flag if needed + size_t first_freed = 0; + size_t first_inuse = 0; + while (first_freed < dict_info_.lemma_count) { + // Find first freed offset + while ((offsets_[first_freed] & kUserDictOffsetFlagRemove) == 0 && + first_freed < dict_info_.lemma_count) { + first_freed++; + } + if (first_freed < dict_info_.lemma_count) { + // Save REMOVE flag to lemma flag + int off = offsets_[first_freed]; + set_lemma_flag(off, kUserDictLemmaFlagRemove); + } else { + break; + } + // Find first inuse offse after first_freed + first_inuse = first_freed + 1; + while ((offsets_[first_inuse] & kUserDictOffsetFlagRemove) && + (first_inuse < dict_info_.lemma_count)) { + // Save REMOVE flag to lemma flag + int off = offsets_[first_inuse]; + set_lemma_flag(off, kUserDictLemmaFlagRemove); + first_inuse++; + } + if (first_inuse >= dict_info_.lemma_count) { + break; + } + // Swap offsets_ + int tmp = offsets_[first_inuse]; + offsets_[first_inuse] = offsets_[first_freed]; + offsets_[first_freed] = tmp; + // Move scores_, no need to swap + tmp = scores_[first_inuse]; + scores_[first_inuse] = scores_[first_freed]; + scores_[first_freed] = tmp; + // Swap ids_ + LemmaIdType tmpid = ids_[first_inuse]; + ids_[first_inuse] = ids_[first_freed]; + ids_[first_freed] = tmpid; + // Go on + first_freed++; + } +#ifdef ___PREDICT_ENABLED___ + // Fixup predicts_ + first_freed = 0; + first_inuse = 0; + while (first_freed < dict_info_.lemma_count) { + // Find first freed offset + while ((predicts_[first_freed] & kUserDictOffsetFlagRemove) == 0 && + first_freed < dict_info_.lemma_count) { + first_freed++; + } + if (first_freed >= dict_info_.lemma_count) + break; + // Find first inuse offse after first_freed + first_inuse = first_freed + 1; + while ((predicts_[first_inuse] & kUserDictOffsetFlagRemove) + && (first_inuse < dict_info_.lemma_count)) { + first_inuse++; + } + if (first_inuse >= dict_info_.lemma_count) { + break; + } + // Swap offsets_ + int tmp = predicts_[first_inuse]; + predicts_[first_inuse] = predicts_[first_freed]; + predicts_[first_freed] = tmp; + // Go on + first_freed++; + } +#endif + dict_info_.lemma_count = first_freed; + // Fixup lemmas_ + size_t begin = 0; + size_t end = 0; + size_t dst = 0; + int total_size = dict_info_.lemma_size + lemma_size_left_; + int total_count = dict_info_.lemma_count + lemma_count_left_; + size_t real_size = total_size - lemma_size_left_; + while (dst < real_size) { + unsigned char flag = get_lemma_flag(dst); + unsigned char nchr = get_lemma_nchar(dst); + if ((flag & kUserDictLemmaFlagRemove) == 0) { + dst += nchr * 4 + 2; + continue; + } + break; + } + if (dst >= real_size) + return; + + end = dst; + while (end < real_size) { + begin = end + get_lemma_nchar(end) * 4 + 2; + repeat: + // not used any more + if (begin >= real_size) + break; + unsigned char flag = get_lemma_flag(begin); + unsigned char nchr = get_lemma_nchar(begin); + if (flag & kUserDictLemmaFlagRemove) { + begin += nchr * 4 + 2; + goto repeat; + } + end = begin + nchr * 4 + 2; + while (end < real_size) { + unsigned char eflag = get_lemma_flag(end); + unsigned char enchr = get_lemma_nchar(end); + if ((eflag & kUserDictLemmaFlagRemove) == 0) { + end += enchr * 4 + 2; + continue; + } + break; + } + memmove(lemmas_ + dst, lemmas_ + begin, end - begin); + for (size_t j = 0; j < dict_info_.lemma_count; j++) { + if (offsets_[j] >= begin && offsets_[j] < end) { + offsets_[j] -= (begin - dst); + offsets_by_id_[ids_[j] - start_id_] = offsets_[j]; + } +#ifdef ___PREDICT_ENABLED___ + if (predicts_[j] >= begin && predicts_[j] < end) { + predicts_[j] -= (begin - dst); + } +#endif + } +#ifdef ___SYNC_ENABLED___ + for (size_t j = 0; j < dict_info_.sync_count; j++) { + if (syncs_[j] >= begin && syncs_[j] < end) { + syncs_[j] -= (begin - dst); + } + } +#endif + dst += (end - begin); + } + + dict_info_.free_count = 0; + dict_info_.free_size = 0; + dict_info_.lemma_size = dst; + lemma_size_left_ = total_size - dict_info_.lemma_size; + lemma_count_left_ = total_count - dict_info_.lemma_count; + + // XXX Without following code, + // offsets_by_id_ is not reordered. + // That's to say, all removed lemmas' ids are not collected back. + // There may not be room for addition of new lemmas due to + // offsests_by_id_ reason, although lemma_size_left_ is fixed. + // By default, we do want defrag as fast as possible, because + // during defrag procedure, other peers can not write new lemmas + // to user dictionary file. + // XXX If write-back is invoked immediately after + // this defragment, no need to fix up following in-mem data. + for (uint32 i = 0; i < dict_info_.lemma_count; i++) { + ids_[i] = start_id_ + i; + offsets_by_id_[i] = offsets_[i]; + } + + state_ = USER_DICT_DEFRAGMENTED; + +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_END; + LOGD_PERF("defragment"); +#endif +} + +#ifdef ___SYNC_ENABLED___ +void UserDict::clear_sync_lemmas(unsigned int start, unsigned int end) { + if (is_valid_state() == false) + return; + if (end > dict_info_.sync_count) + end = dict_info_.sync_count; + memmove(syncs_ + start, syncs_ + end, (dict_info_.sync_count - end) << 2); + dict_info_.sync_count -= (end - start); + if (state_ < USER_DICT_SYNC_DIRTY) + state_ = USER_DICT_SYNC_DIRTY; +} + +int UserDict::get_sync_count() { + if (is_valid_state() == false) + return 0; + return dict_info_.sync_count; +} + +LemmaIdType UserDict::put_lemma_no_sync(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count, uint64 lmt) { + int again = 0; + begin: + LemmaIdType id; + uint32 * syncs_bak = syncs_; + syncs_ = NULL; + id = _put_lemma(lemma_str, splids, lemma_len, count, lmt); + syncs_ = syncs_bak; + if (id == 0 && again == 0) { + if ((dict_info_.limit_lemma_count > 0 && + dict_info_.lemma_count >= dict_info_.limit_lemma_count) + || (dict_info_.limit_lemma_size > 0 && + dict_info_.lemma_size + (2 + (lemma_len << 2)) + > dict_info_.limit_lemma_size)) { + // XXX Always reclaim and defrag in sync code path + // sync thread is background thread and ok with heavy work + reclaim(); + defragment(); + flush_cache(); + again = 1; + goto begin; + } + } + return id; +} + +int UserDict::put_lemmas_no_sync_from_utf16le_string(char16 * lemmas, int len) { + int newly_added = 0; + + SpellingParser * spl_parser = new SpellingParser(); + if (!spl_parser) { + return 0; + } +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_BEGIN; +#endif + char16 *ptr = lemmas; + + // Extract pinyin,words,frequence,last_mod_time + char16 * p = ptr, * py16 = ptr; + char16 * hz16 = NULL; + int py16_len = 0; + uint16 * splid = NULL; + int splid_size = 0; + int splid_len = 0; + int hz16_len = 0; + char16 * fr16 = NULL; + int fr16_len = 0; + + while (p - ptr < len) { + // Pinyin + py16 = p; + splid_len = 0; + while (*p != 0x2c && (p - ptr) < len) { + if (*p == 0x20) + splid_len++; + p++; + } + splid_len++; + if (p - ptr == len) + break; + py16_len = p - py16; + if (splid_size < splid_len) { + char16 * tid = (char16*)realloc(splid, splid_len * 2); + if (!tid) + break; + splid = tid; + splid_size = splid_len * 2; + } + bool is_pre; + int splidl = spl_parser->splstr16_to_idxs_f( + py16, py16_len, splid, NULL, splid_size, is_pre); + if (splidl != splid_len) + break; + // Phrase + hz16 = ++p; + while (*p != 0x2c && (p - ptr) < len) { + p++; + } + hz16_len = p - hz16; + if (hz16_len != splid_len) + break; + // Frequency + fr16 = ++p; + fr16_len = 0; + while (*p != 0x2c && (p - ptr) < len) { + p++; + } + fr16_len = p - fr16; + uint32 intf = (uint32)utf16le_atoll(fr16, fr16_len); + // Last modified time + fr16 = ++p; + fr16_len = 0; + while (*p != 0x3b && (p - ptr) < len) { + p++; + } + fr16_len = p - fr16; + uint64 last_mod = utf16le_atoll(fr16, fr16_len); + + put_lemma_no_sync(hz16, splid, splid_len, intf, last_mod); + newly_added++; + + p++; + } + + if (splid) + free(splid); + +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_END; + LOGD_PERF("put_lemmas_no_sync_from_utf16le_string"); +#endif + return newly_added; +} + +int UserDict::get_sync_lemmas_in_utf16le_string_from_beginning( + char16 * str, int size, int * count) { + int len = 0; + *count = 0; + + int left_len = size; + + if (is_valid_state() == false) + return len; + + SpellingTrie * spl_trie = &SpellingTrie::get_instance(); + if (!spl_trie) { + return 0; + } + + uint32 i; + for (i = 0; i < dict_info_.sync_count; i++) { + int offset = syncs_[i]; + uint32 nchar = get_lemma_nchar(offset); + uint16 *spl = get_lemma_spell_ids(offset); + uint16 *wrd = get_lemma_word(offset); + int score = _get_lemma_score(wrd, spl, nchar); + + static char score_temp[32], *pscore_temp = score_temp; + static char16 temp[256], *ptemp = temp; + + pscore_temp = score_temp; + ptemp = temp; + + uint32 j; + // Add pinyin + for (j = 0; j < nchar; j++) { + int ret_len = spl_trie->get_spelling_str16( + spl[j], ptemp, temp + sizeof(temp) - ptemp); + if (ret_len <= 0) + break; + ptemp += ret_len; + if (ptemp < temp + sizeof(temp) - 1) { + *(ptemp++) = ' '; + } else { + j = 0; + break; + } + } + if (j < nchar) { + continue; + } + ptemp--; + if (ptemp < temp + sizeof(temp) - 1) { + *(ptemp++) = ','; + } else { + continue; + } + // Add phrase + for (j = 0; j < nchar; j++) { + if (ptemp < temp + sizeof(temp) - 1) { + *(ptemp++) = wrd[j]; + } else { + break; + } + } + if (j < nchar) { + continue; + } + if (ptemp < temp + sizeof(temp) - 1) { + *(ptemp++) = ','; + } else { + continue; + } + // Add frequency + uint32 intf = extract_score_freq(score); + int ret_len = utf16le_lltoa(intf, ptemp, temp + sizeof(temp) - ptemp); + if (ret_len <= 0) + continue; + ptemp += ret_len; + if (ptemp < temp + sizeof(temp) - 1) { + *(ptemp++) = ','; + } else { + continue; + } + // Add last modified time + uint64 last_mod = extract_score_lmt(score); + ret_len = utf16le_lltoa(last_mod, ptemp, temp + sizeof(temp) - ptemp); + if (ret_len <= 0) + continue; + ptemp += ret_len; + if (ptemp < temp + sizeof(temp) - 1) { + *(ptemp++) = ';'; + } else { + continue; + } + + // Write to string + int need_len = ptemp - temp; + if (need_len > left_len) + break; + memcpy(str + len, temp, need_len * 2); + left_len -= need_len; + + len += need_len; + (*count)++; + } + + if (len > 0) { + if (state_ < USER_DICT_SYNC_DIRTY) + state_ = USER_DICT_SYNC_DIRTY; + } + return len; +} + +#endif + +bool UserDict::state(UserDictStat * stat) { + if (is_valid_state() == false) + return false; + if (!stat) + return false; + stat->version = version_; + stat->file_name = dict_file_; + stat->load_time.tv_sec = load_time_.tv_sec; + stat->load_time.tv_usec = load_time_.tv_usec; + pthread_mutex_lock(&g_mutex_); + stat->last_update.tv_sec = g_last_update_.tv_sec; + stat->last_update.tv_usec = g_last_update_.tv_usec; + pthread_mutex_unlock(&g_mutex_); + stat->disk_size = get_dict_file_size(&dict_info_); + stat->lemma_count = dict_info_.lemma_count; + stat->lemma_size = dict_info_.lemma_size; + stat->delete_count = dict_info_.free_count; + stat->delete_size = dict_info_.free_size; +#ifdef ___SYNC_ENABLED___ + stat->sync_count = dict_info_.sync_count; +#endif + stat->limit_lemma_count = dict_info_.limit_lemma_count; + stat->limit_lemma_size = dict_info_.limit_lemma_size; + stat->reclaim_ratio = dict_info_.reclaim_ratio; + return true; +} + +void UserDict::set_limit(uint32 max_lemma_count, + uint32 max_lemma_size, uint32 reclaim_ratio) { + dict_info_.limit_lemma_count = max_lemma_count; + dict_info_.limit_lemma_size = max_lemma_size; + if (reclaim_ratio > 100) + reclaim_ratio = 100; + dict_info_.reclaim_ratio = reclaim_ratio; +} + +void UserDict::reclaim() { + if (is_valid_state() == false) + return; + + switch (dict_info_.reclaim_ratio) { + case 0: + return; + case 100: + // TODO: CLEAR to be implemented + assert(false); + return; + default: + break; + } + + // XXX Reclaim is only based on count, not size + uint32 count = dict_info_.lemma_count; + int rc = count * dict_info_.reclaim_ratio / 100; + + UserDictScoreOffsetPair * score_offset_pairs = NULL; + score_offset_pairs = (UserDictScoreOffsetPair *)malloc( + sizeof(UserDictScoreOffsetPair) * rc); + if (score_offset_pairs == NULL) { + return; + } + + for (int i = 0; i < rc; i++) { + int s = scores_[i]; + score_offset_pairs[i].score = s; + score_offset_pairs[i].offset_index = i; + } + + for (int i = (rc + 1) / 2; i >= 0; i--) + shift_down(score_offset_pairs, i, rc); + + for (uint32 i = rc; i < dict_info_.lemma_count; i++) { + int s = scores_[i]; + if (s < score_offset_pairs[0].score) { + score_offset_pairs[0].score = s; + score_offset_pairs[0].offset_index = i; + shift_down(score_offset_pairs, 0, rc); + } + } + + for (int i = 0; i < rc; i++) { + int off = score_offset_pairs[i].offset_index; + remove_lemma_by_offset_index(off); + } + if (rc > 0) { + if (state_ < USER_DICT_OFFSET_DIRTY) + state_ = USER_DICT_OFFSET_DIRTY; + } + + free(score_offset_pairs); +} + +inline void UserDict::swap(UserDictScoreOffsetPair * sop, int i, int j) { + int s = sop[i].score; + int p = sop[i].offset_index; + sop[i].score = sop[j].score; + sop[i].offset_index = sop[j].offset_index; + sop[j].score = s; + sop[j].offset_index = p; +} + +void UserDict::shift_down(UserDictScoreOffsetPair * sop, int i, int n) { + int par = i; + while (par < n) { + int left = par * 2 + 1; + int right = left + 1; + if (left >= n && right >= n) + break; + if (right >= n) { + if (sop[left].score > sop[par].score) { + swap(sop, left, par); + par = left; + continue; + } + } else if (sop[left].score > sop[right].score && + sop[left].score > sop[par].score) { + swap(sop, left, par); + par = left; + continue; + } else if (sop[right].score > sop[left].score && + sop[right].score > sop[par].score) { + swap(sop, right, par); + par = right; + continue; + } + break; + } +} + +LemmaIdType UserDict::put_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count) { + return _put_lemma(lemma_str, splids, lemma_len, count, time(NULL)); +} + +LemmaIdType UserDict::_put_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count, uint64 lmt) { +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_BEGIN; +#endif + if (is_valid_state() == false) + return 0; + int32 off = locate_in_offsets(lemma_str, splids, lemma_len); + if (off != -1) { + int delta_score = count - scores_[off]; + dict_info_.total_nfreq += delta_score; + scores_[off] = build_score(lmt, count); + if (state_ < USER_DICT_SCORE_DIRTY) + state_ = USER_DICT_SCORE_DIRTY; +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_END; + LOGD_PERF("_put_lemma(update)"); +#endif + return ids_[off]; + } else { + if ((dict_info_.limit_lemma_count > 0 && + dict_info_.lemma_count >= dict_info_.limit_lemma_count) + || (dict_info_.limit_lemma_size > 0 && + dict_info_.lemma_size + (2 + (lemma_len << 2)) + > dict_info_.limit_lemma_size)) { + // XXX Don't defragment here, it's too time-consuming. + return 0; + } + int flushed = 0; + if (lemma_count_left_ == 0 || + lemma_size_left_ < (size_t)(2 + (lemma_len << 2))) { + + // XXX When there is no space for new lemma, we flush to disk + // flush_cache() may be called by upper user + // and better place shoule be found instead of here + flush_cache(); + flushed = 1; + // Or simply return and do nothing + // return 0; + } +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_END; + LOGD_PERF(flushed ? "_put_lemma(flush+add)" : "_put_lemma(add)"); +#endif + LemmaIdType id = append_a_lemma(lemma_str, splids, lemma_len, count, lmt); +#ifdef ___SYNC_ENABLED___ + if (syncs_ && id != 0) { + queue_lemma_for_sync(id); + } +#endif + return id; + } + return 0; +} + +#ifdef ___SYNC_ENABLED___ +void UserDict::queue_lemma_for_sync(LemmaIdType id) { + if (dict_info_.sync_count < sync_count_size_) { + syncs_[dict_info_.sync_count++] = offsets_by_id_[id - start_id_]; + } else { + uint32 * syncs = (uint32*)realloc( + syncs_, (sync_count_size_ + kUserDictPreAlloc) << 2); + if (syncs) { + sync_count_size_ += kUserDictPreAlloc; + syncs_ = syncs; + syncs_[dict_info_.sync_count++] = offsets_by_id_[id - start_id_]; + } + } +} +#endif + +LemmaIdType UserDict::update_lemma(LemmaIdType lemma_id, int16 delta_count, + bool selected) { +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_BEGIN; +#endif + if (is_valid_state() == false) + return 0; + if (is_valid_lemma_id(lemma_id) == false) + return 0; + uint32 offset = offsets_by_id_[lemma_id - start_id_]; + uint8 lemma_len = get_lemma_nchar(offset); + char16 * lemma_str = get_lemma_word(offset); + uint16 * splids = get_lemma_spell_ids(offset); + + int32 off = locate_in_offsets(lemma_str, splids, lemma_len); + if (off != -1) { + int score = scores_[off]; + int count = extract_score_freq(score); + uint64 lmt = extract_score_lmt(score); + if (count + delta_count > kUserDictMaxFrequency || + count + delta_count < count) { + delta_count = kUserDictMaxFrequency - count; + } + count += delta_count; + dict_info_.total_nfreq += delta_count; + if (selected) { + lmt = time(NULL); + } + scores_[off] = build_score(lmt, count); + if (state_ < USER_DICT_SCORE_DIRTY) + state_ = USER_DICT_SCORE_DIRTY; +#ifdef ___DEBUG_PERF___ + DEBUG_PERF_END; + LOGD_PERF("update_lemma"); +#endif +#ifdef ___SYNC_ENABLED___ + queue_lemma_for_sync(ids_[off]); +#endif + return ids_[off]; + } + return 0; +} + +size_t UserDict::get_total_lemma_count() { + return dict_info_.total_nfreq; +} + +void UserDict::set_total_lemma_count_of_others(size_t count) { + total_other_nfreq_ = count; +} + +LemmaIdType UserDict::append_a_lemma(char16 lemma_str[], uint16 splids[], + uint16 lemma_len, uint16 count, uint64 lmt) { + LemmaIdType id = get_max_lemma_id() + 1; + size_t offset = dict_info_.lemma_size; + if (offset > kUserDictOffsetMask) + return 0; + + lemmas_[offset] = 0; + lemmas_[offset + 1] = (uint8)lemma_len; + for (size_t i = 0; i < lemma_len; i++) { + *((uint16*)&lemmas_[offset + 2 + (i << 1)]) = splids[i]; + *((char16*)&lemmas_[offset + 2 + (lemma_len << 1) + (i << 1)]) + = lemma_str[i]; + } + uint32 off = dict_info_.lemma_count; + offsets_[off] = offset; + scores_[off] = build_score(lmt, count); + ids_[off] = id; +#ifdef ___PREDICT_ENABLED___ + predicts_[off] = offset; +#endif + + offsets_by_id_[id - start_id_] = offset; + + dict_info_.lemma_count++; + dict_info_.lemma_size += (2 + (lemma_len << 2)); + lemma_count_left_--; + lemma_size_left_ -= (2 + (lemma_len << 2)); + + // Sort + + UserDictSearchable searchable; + prepare_locate(&searchable, splids, lemma_len); + + size_t i = 0; + while (i < off) { + offset = offsets_[i]; + uint32 nchar = get_lemma_nchar(offset); + uint16 * spl = get_lemma_spell_ids(offset); + + if (0 <= fuzzy_compare_spell_id(spl, nchar, &searchable)) + break; + i++; + } + if (i != off) { + uint32 temp = offsets_[off]; + memmove(offsets_ + i + 1, offsets_ + i, (off - i) << 2); + offsets_[i] = temp; + + temp = scores_[off]; + memmove(scores_ + i + 1, scores_ + i, (off - i) << 2); + scores_[i] = temp; + + temp = ids_[off]; + memmove(ids_ + i + 1, ids_ + i, (off - i) << 2); + ids_[i] = temp; + } + +#ifdef ___PREDICT_ENABLED___ + uint32 j = 0; + uint16 * words_new = get_lemma_word(predicts_[off]); + j = locate_where_to_insert_in_predicts(words_new, lemma_len); + if (j != off) { + uint32 temp = predicts_[off]; + memmove(predicts_ + j + 1, predicts_ + j, (off - j) << 2); + predicts_[j] = temp; + } +#endif + + if (state_ < USER_DICT_LEMMA_DIRTY) + state_ = USER_DICT_LEMMA_DIRTY; + +#ifdef ___CACHE_ENABLED___ + cache_init(); +#endif + + dict_info_.total_nfreq += count; + return id; +} +} diff --git a/jni/share/utf16char.cpp b/jni/share/utf16char.cpp new file mode 100644 index 0000000..fadb6cf --- /dev/null +++ b/jni/share/utf16char.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "../include/utf16char.h" + +namespace ime_pinyin { + +#ifdef __cplusplus +extern "C" { +#endif + + char16* utf16_strtok(char16 *utf16_str, size_t *token_size, + char16 **utf16_str_next) { + if (NULL == utf16_str || NULL == token_size || NULL == utf16_str_next) { + return NULL; + } + + // Skip the splitters + size_t pos = 0; + while ((char16)' ' == utf16_str[pos] || (char16)'\n' == utf16_str[pos] + || (char16)'\t' == utf16_str[pos]) + pos++; + + utf16_str += pos; + pos = 0; + + while ((char16)'\0' != utf16_str[pos] && (char16)' ' != utf16_str[pos] + && (char16)'\n' != utf16_str[pos] + && (char16)'\t' != utf16_str[pos]) { + pos++; + } + + char16 *ret_val = utf16_str; + if ((char16)'\0' == utf16_str[pos]) { + *utf16_str_next = NULL; + if (0 == pos) + return NULL; + } else { + *utf16_str_next = utf16_str + pos + 1; + } + + utf16_str[pos] = (char16)'\0'; + *token_size = pos; + + return ret_val; + } + + int utf16_atoi(const char16 *utf16_str) { + if (NULL == utf16_str) + return 0; + + int value = 0; + int sign = 1; + size_t pos = 0; + + if ((char16)'-' == utf16_str[pos]) { + sign = -1; + pos++; + } + + while ((char16)'0' <= utf16_str[pos] && + (char16)'9' >= utf16_str[pos]) { + value = value * 10 + static_cast(utf16_str[pos] - (char16)'0'); + pos++; + } + + return value*sign; + } + + float utf16_atof(const char16 *utf16_str) { + // A temporary implemetation. + char char8[256]; + if (utf16_strlen(utf16_str) >= 256) return 0; + + utf16_strcpy_tochar(char8, utf16_str); + return atof(char8); + } + + size_t utf16_strlen(const char16 *utf16_str) { + if (NULL == utf16_str) + return 0; + + size_t size = 0; + while ((char16)'\0' != utf16_str[size]) + size++; + return size; + } + + int utf16_strcmp(const char16* str1, const char16* str2) { + size_t pos = 0; + while (str1[pos] == str2[pos] && (char16)'\0' != str1[pos]) + pos++; + + return static_cast(str1[pos]) - static_cast(str2[pos]); + } + + int utf16_strncmp(const char16 *str1, const char16 *str2, size_t size) { + size_t pos = 0; + while (pos < size && str1[pos] == str2[pos] && (char16)'\0' != str1[pos]) + pos++; + + if (pos == size) + return 0; + + return static_cast(str1[pos]) - static_cast(str2[pos]); + } + + // we do not consider overlapping + char16* utf16_strcpy(char16 *dst, const char16 *src) { + if (NULL == src || NULL == dst) + return NULL; + + char16* cp = dst; + + while ((char16)'\0' != *src) { + *cp = *src; + cp++; + src++; + } + + *cp = *src; + + return dst; + } + + char16* utf16_strncpy(char16 *dst, const char16 *src, size_t size) { + if (NULL == src || NULL == dst || 0 == size) + return NULL; + + if (src == dst) + return dst; + + char16* cp = dst; + + if (dst < src || (dst > src && dst >= src + size)) { + while (size-- && (*cp++ = *src++)) + ; + } else { + cp += size - 1; + src += size - 1; + while (size-- && (*cp-- == *src--)) + ; + } + return dst; + } + + // We do not handle complicated cases like overlapping, because in this + // codebase, it is not necessary. + char* utf16_strcpy_tochar(char *dst, const char16 *src) { + if (NULL == src || NULL == dst) + return NULL; + + char* cp = dst; + + while ((char16)'\0' != *src) { + *cp = static_cast(*src); + cp++; + src++; + } + *cp = *src; + + return dst; + } + +#ifdef __cplusplus +} +#endif +} // namespace ime_pinyin diff --git a/jni/share/utf16reader.cpp b/jni/share/utf16reader.cpp new file mode 100644 index 0000000..d8e5de5 --- /dev/null +++ b/jni/share/utf16reader.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../include/utf16reader.h" + +namespace ime_pinyin { + +#define MIN_BUF_LEN 128 +#define MAX_BUF_LEN 65535 + +Utf16Reader::Utf16Reader() { + fp_ = NULL; + buffer_ = NULL; + buffer_total_len_ = 0; + buffer_next_pos_ = 0; + buffer_valid_len_ = 0; +} + +Utf16Reader::~Utf16Reader() { + if (NULL != fp_) + fclose(fp_); + + if (NULL != buffer_) + delete [] buffer_; +} + + +bool Utf16Reader::open(const char* filename, size_t buffer_len) { + if (filename == NULL) + return false; + + if (buffer_len < MIN_BUF_LEN) + buffer_len = MIN_BUF_LEN; + else if (buffer_len > MAX_BUF_LEN) + buffer_len = MAX_BUF_LEN; + + buffer_total_len_ = buffer_len; + + if (NULL != buffer_) + delete [] buffer_; + buffer_ = new char16[buffer_total_len_]; + if (NULL == buffer_) + return false; + + if ((fp_ = fopen(filename, "rb")) == NULL) + return false; + + // the UTF16 file header, skip + char16 header; + if (fread(&header, sizeof(header), 1, fp_) != 1 || header != 0xfeff) { + fclose(fp_); + fp_ = NULL; + return false; + } + + return true; +} + +char16* Utf16Reader::readline(char16* read_buf, size_t max_len) { + if (NULL == fp_ || NULL == read_buf || 0 == max_len) + return NULL; + + size_t ret_len = 0; + + do { + if (buffer_valid_len_ == 0) { + buffer_next_pos_ = 0; + buffer_valid_len_ = fread(buffer_, sizeof(char16), + buffer_total_len_, fp_); + if (buffer_valid_len_ == 0) { + if (0 == ret_len) + return NULL; + read_buf[ret_len] = (char16)'\0'; + return read_buf; + } + } + + for (size_t i = 0; i < buffer_valid_len_; i++) { + if (i == max_len - 1 || + buffer_[buffer_next_pos_ + i] == (char16)'\n') { + if (ret_len + i > 0 && read_buf[ret_len + i - 1] == (char16)'\r') { + read_buf[ret_len + i - 1] = (char16)'\0'; + } else { + read_buf[ret_len + i] = (char16)'\0'; + } + + i++; + buffer_next_pos_ += i; + buffer_valid_len_ -= i; + if (buffer_next_pos_ == buffer_total_len_) { + buffer_next_pos_ = 0; + buffer_valid_len_ = 0; + } + return read_buf; + } else { + read_buf[ret_len + i] = buffer_[buffer_next_pos_ + i]; + } + } + + ret_len += buffer_valid_len_; + buffer_valid_len_ = 0; + } while (true); + + // Never reach here + return NULL; +} + +bool Utf16Reader::close() { + if (NULL != fp_) + fclose(fp_); + fp_ = NULL; + + if (NULL != buffer_) + delete [] buffer_; + buffer_ = NULL; + return true; +} +} // namespace ime_pinyin diff --git a/lib/Android.mk b/lib/Android.mk new file mode 100644 index 0000000..f41193e --- /dev/null +++ b/lib/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(call all-subdir-java-files) \ + com/android/inputmethod/pinyin/IPinyinDecoderService.aidl + +LOCAL_MODULE := com.android.inputmethod.pinyin.lib + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl b/lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl new file mode 100644 index 0000000..59dd6cc --- /dev/null +++ b/lib/com/android/inputmethod/pinyin/IPinyinDecoderService.aidl @@ -0,0 +1,51 @@ +/* //com/andriod/inputmethod/pinyin/IPinyinDecoderService.aidl + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.inputmethod.pinyin; + +interface IPinyinDecoderService { + int getInt(); + void setMaxLens(int maxSpsLen, int maxHzsLen); + int imSearch(in byte[] pyBuf, int pyLen); + int imDelSearch(int pos, boolean is_pos_in_splid, boolean clear_fixed_this_step); + void imResetSearch(); + int imAddLetter(byte ch); + String imGetPyStr(boolean decoded); + int imGetPyStrLen(boolean decoded); + int[] imGetSplStart(); + String imGetChoice(int choiceId); + String imGetChoices(int choicesNum); + List imGetChoiceList(int choicesStart, int choicesNum, int sentFixedLen); + int imChoose(int choiceId); + int imCancelLastChoice(); + int imGetFixedLen(); + boolean imCancelInput(); + void imFlushCache(); + int imGetPredictsNum(in String fixedStr); + List imGetPredictList(int predictsStart, int predictsNum); + String imGetPredictItem(int predictNo); + + String syncUserDict(in String tomerge); + boolean syncBegin(); + void syncFinish(); + int syncPutLemmas(in String tomerge); + String syncGetLemmas(); + int syncGetLastCount(); + int syncGetTotalCount(); + void syncClearLastGot(); + int imSyncGetCapacity(); +} diff --git a/res/drawable/app_icon.png b/res/drawable/app_icon.png new file mode 100644 index 0000000..0c6eff3 Binary files /dev/null and b/res/drawable/app_icon.png differ diff --git a/res/drawable/arrow_bg.xml b/res/drawable/arrow_bg.xml new file mode 100644 index 0000000..9125232 --- /dev/null +++ b/res/drawable/arrow_bg.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/res/drawable/arrow_left.png b/res/drawable/arrow_left.png new file mode 100644 index 0000000..75b8daa Binary files /dev/null and b/res/drawable/arrow_left.png differ diff --git a/res/drawable/arrow_right.png b/res/drawable/arrow_right.png new file mode 100644 index 0000000..e4e6cd7 Binary files /dev/null and b/res/drawable/arrow_right.png differ diff --git a/res/drawable/candidate_balloon_bg.9.png b/res/drawable/candidate_balloon_bg.9.png new file mode 100644 index 0000000..ddc26f6 Binary files /dev/null and b/res/drawable/candidate_balloon_bg.9.png differ diff --git a/res/drawable/candidate_hl_bg.9.png b/res/drawable/candidate_hl_bg.9.png new file mode 100644 index 0000000..83d40a0 Binary files /dev/null and b/res/drawable/candidate_hl_bg.9.png differ diff --git a/res/drawable/candidates_area_bg.9.png b/res/drawable/candidates_area_bg.9.png new file mode 100644 index 0000000..3ff63f4 Binary files /dev/null and b/res/drawable/candidates_area_bg.9.png differ diff --git a/res/drawable/candidates_vertical_line.png b/res/drawable/candidates_vertical_line.png new file mode 100644 index 0000000..1edd335 Binary files /dev/null and b/res/drawable/candidates_vertical_line.png differ diff --git a/res/drawable/cands_container_bg.9.png b/res/drawable/cands_container_bg.9.png new file mode 100644 index 0000000..837ba9b Binary files /dev/null and b/res/drawable/cands_container_bg.9.png differ diff --git a/res/drawable/comma_full_icon.png b/res/drawable/comma_full_icon.png new file mode 100644 index 0000000..142fe1e Binary files /dev/null and b/res/drawable/comma_full_icon.png differ diff --git a/res/drawable/comma_full_popup_icon.png b/res/drawable/comma_full_popup_icon.png new file mode 100644 index 0000000..f63a5b5 Binary files /dev/null and b/res/drawable/comma_full_popup_icon.png differ diff --git a/res/drawable/composing_area_bg.9.png b/res/drawable/composing_area_bg.9.png new file mode 100644 index 0000000..31f6580 Binary files /dev/null and b/res/drawable/composing_area_bg.9.png differ diff --git a/res/drawable/composing_area_cursor.png b/res/drawable/composing_area_cursor.png new file mode 100644 index 0000000..ea3bbca Binary files /dev/null and b/res/drawable/composing_area_cursor.png differ diff --git a/res/drawable/composing_hl_bg.9.png b/res/drawable/composing_hl_bg.9.png new file mode 100644 index 0000000..b84719e Binary files /dev/null and b/res/drawable/composing_hl_bg.9.png differ diff --git a/res/drawable/delete_icon.png b/res/drawable/delete_icon.png new file mode 100644 index 0000000..f1f7c58 Binary files /dev/null and b/res/drawable/delete_icon.png differ diff --git a/res/drawable/delete_popup_icon.png b/res/drawable/delete_popup_icon.png new file mode 100644 index 0000000..3c90839 Binary files /dev/null and b/res/drawable/delete_popup_icon.png differ diff --git a/res/drawable/dun_icon.png b/res/drawable/dun_icon.png new file mode 100644 index 0000000..eaa6aa1 Binary files /dev/null and b/res/drawable/dun_icon.png differ diff --git a/res/drawable/dun_popup_icon.png b/res/drawable/dun_popup_icon.png new file mode 100644 index 0000000..5b7eb6a Binary files /dev/null and b/res/drawable/dun_popup_icon.png differ diff --git a/res/drawable/emotion_icon_00.png b/res/drawable/emotion_icon_00.png new file mode 100644 index 0000000..33f2945 Binary files /dev/null and b/res/drawable/emotion_icon_00.png differ diff --git a/res/drawable/emotion_icon_00_popup.png b/res/drawable/emotion_icon_00_popup.png new file mode 100644 index 0000000..1aaebdd Binary files /dev/null and b/res/drawable/emotion_icon_00_popup.png differ diff --git a/res/drawable/emotion_icon_01.png b/res/drawable/emotion_icon_01.png new file mode 100644 index 0000000..9165e2a Binary files /dev/null and b/res/drawable/emotion_icon_01.png differ diff --git a/res/drawable/emotion_icon_01_popup.png b/res/drawable/emotion_icon_01_popup.png new file mode 100644 index 0000000..baeebdc Binary files /dev/null and b/res/drawable/emotion_icon_01_popup.png differ diff --git a/res/drawable/emotion_icon_02.png b/res/drawable/emotion_icon_02.png new file mode 100644 index 0000000..e967603 Binary files /dev/null and b/res/drawable/emotion_icon_02.png differ diff --git a/res/drawable/emotion_icon_02_popup.png b/res/drawable/emotion_icon_02_popup.png new file mode 100644 index 0000000..3a27346 Binary files /dev/null and b/res/drawable/emotion_icon_02_popup.png differ diff --git a/res/drawable/emotion_icon_03.png b/res/drawable/emotion_icon_03.png new file mode 100644 index 0000000..3fc2585 Binary files /dev/null and b/res/drawable/emotion_icon_03.png differ diff --git a/res/drawable/emotion_icon_03_popup.png b/res/drawable/emotion_icon_03_popup.png new file mode 100644 index 0000000..2567632 Binary files /dev/null and b/res/drawable/emotion_icon_03_popup.png differ diff --git a/res/drawable/emotion_icon_04.png b/res/drawable/emotion_icon_04.png new file mode 100644 index 0000000..0e2d459 Binary files /dev/null and b/res/drawable/emotion_icon_04.png differ diff --git a/res/drawable/emotion_icon_04_popup.png b/res/drawable/emotion_icon_04_popup.png new file mode 100644 index 0000000..61f2d21 Binary files /dev/null and b/res/drawable/emotion_icon_04_popup.png differ diff --git a/res/drawable/emotion_icon_05.png b/res/drawable/emotion_icon_05.png new file mode 100644 index 0000000..29ac101 Binary files /dev/null and b/res/drawable/emotion_icon_05.png differ diff --git a/res/drawable/emotion_icon_05_popup.png b/res/drawable/emotion_icon_05_popup.png new file mode 100644 index 0000000..48fd0bc Binary files /dev/null and b/res/drawable/emotion_icon_05_popup.png differ diff --git a/res/drawable/emotion_icon_06.png b/res/drawable/emotion_icon_06.png new file mode 100644 index 0000000..02f47c6 Binary files /dev/null and b/res/drawable/emotion_icon_06.png differ diff --git a/res/drawable/emotion_icon_06_popup.png b/res/drawable/emotion_icon_06_popup.png new file mode 100644 index 0000000..5508cd2 Binary files /dev/null and b/res/drawable/emotion_icon_06_popup.png differ diff --git a/res/drawable/emotion_icon_10.png b/res/drawable/emotion_icon_10.png new file mode 100644 index 0000000..d9a0582 Binary files /dev/null and b/res/drawable/emotion_icon_10.png differ diff --git a/res/drawable/emotion_icon_10_popup.png b/res/drawable/emotion_icon_10_popup.png new file mode 100644 index 0000000..32dd180 Binary files /dev/null and b/res/drawable/emotion_icon_10_popup.png differ diff --git a/res/drawable/emotion_icon_11.png b/res/drawable/emotion_icon_11.png new file mode 100644 index 0000000..d1ca0a8 Binary files /dev/null and b/res/drawable/emotion_icon_11.png differ diff --git a/res/drawable/emotion_icon_11_popup.png b/res/drawable/emotion_icon_11_popup.png new file mode 100644 index 0000000..5e42d97 Binary files /dev/null and b/res/drawable/emotion_icon_11_popup.png differ diff --git a/res/drawable/emotion_icon_12.png b/res/drawable/emotion_icon_12.png new file mode 100644 index 0000000..ee88ef8 Binary files /dev/null and b/res/drawable/emotion_icon_12.png differ diff --git a/res/drawable/emotion_icon_12_popup.png b/res/drawable/emotion_icon_12_popup.png new file mode 100644 index 0000000..195600d Binary files /dev/null and b/res/drawable/emotion_icon_12_popup.png differ diff --git a/res/drawable/emotion_icon_13.png b/res/drawable/emotion_icon_13.png new file mode 100644 index 0000000..70b7334 Binary files /dev/null and b/res/drawable/emotion_icon_13.png differ diff --git a/res/drawable/emotion_icon_13_popup.png b/res/drawable/emotion_icon_13_popup.png new file mode 100644 index 0000000..1df73ec Binary files /dev/null and b/res/drawable/emotion_icon_13_popup.png differ diff --git a/res/drawable/emotion_icon_14.png b/res/drawable/emotion_icon_14.png new file mode 100644 index 0000000..852b7c1 Binary files /dev/null and b/res/drawable/emotion_icon_14.png differ diff --git a/res/drawable/emotion_icon_14_popup.png b/res/drawable/emotion_icon_14_popup.png new file mode 100644 index 0000000..800a240 Binary files /dev/null and b/res/drawable/emotion_icon_14_popup.png differ diff --git a/res/drawable/emotion_icon_15.png b/res/drawable/emotion_icon_15.png new file mode 100644 index 0000000..cfbbe16 Binary files /dev/null and b/res/drawable/emotion_icon_15.png differ diff --git a/res/drawable/emotion_icon_15_popup.png b/res/drawable/emotion_icon_15_popup.png new file mode 100644 index 0000000..7f175f5 Binary files /dev/null and b/res/drawable/emotion_icon_15_popup.png differ diff --git a/res/drawable/emotion_icon_16.png b/res/drawable/emotion_icon_16.png new file mode 100644 index 0000000..52368c0 Binary files /dev/null and b/res/drawable/emotion_icon_16.png differ diff --git a/res/drawable/emotion_icon_16_popup.png b/res/drawable/emotion_icon_16_popup.png new file mode 100644 index 0000000..2a5b815 Binary files /dev/null and b/res/drawable/emotion_icon_16_popup.png differ diff --git a/res/drawable/emotion_icon_20.png b/res/drawable/emotion_icon_20.png new file mode 100644 index 0000000..2681132 Binary files /dev/null and b/res/drawable/emotion_icon_20.png differ diff --git a/res/drawable/emotion_icon_20_popup.png b/res/drawable/emotion_icon_20_popup.png new file mode 100644 index 0000000..8887619 Binary files /dev/null and b/res/drawable/emotion_icon_20_popup.png differ diff --git a/res/drawable/emotion_icon_21.png b/res/drawable/emotion_icon_21.png new file mode 100644 index 0000000..ed425d8 Binary files /dev/null and b/res/drawable/emotion_icon_21.png differ diff --git a/res/drawable/emotion_icon_21_popup.png b/res/drawable/emotion_icon_21_popup.png new file mode 100644 index 0000000..48afe8a Binary files /dev/null and b/res/drawable/emotion_icon_21_popup.png differ diff --git a/res/drawable/emotion_icon_22.png b/res/drawable/emotion_icon_22.png new file mode 100644 index 0000000..88d37ec Binary files /dev/null and b/res/drawable/emotion_icon_22.png differ diff --git a/res/drawable/emotion_icon_22_popup.png b/res/drawable/emotion_icon_22_popup.png new file mode 100644 index 0000000..cb08d1b Binary files /dev/null and b/res/drawable/emotion_icon_22_popup.png differ diff --git a/res/drawable/emotion_icon_23.png b/res/drawable/emotion_icon_23.png new file mode 100644 index 0000000..75f592b Binary files /dev/null and b/res/drawable/emotion_icon_23.png differ diff --git a/res/drawable/emotion_icon_23_popup.png b/res/drawable/emotion_icon_23_popup.png new file mode 100644 index 0000000..e3ecfc3 Binary files /dev/null and b/res/drawable/emotion_icon_23_popup.png differ diff --git a/res/drawable/emotion_icon_24.png b/res/drawable/emotion_icon_24.png new file mode 100644 index 0000000..fdadf91 Binary files /dev/null and b/res/drawable/emotion_icon_24.png differ diff --git a/res/drawable/emotion_icon_24_popup.png b/res/drawable/emotion_icon_24_popup.png new file mode 100644 index 0000000..1766a5d Binary files /dev/null and b/res/drawable/emotion_icon_24_popup.png differ diff --git a/res/drawable/enter_icon.png b/res/drawable/enter_icon.png new file mode 100644 index 0000000..17f2574 Binary files /dev/null and b/res/drawable/enter_icon.png differ diff --git a/res/drawable/enter_popup_icon.png b/res/drawable/enter_popup_icon.png new file mode 100644 index 0000000..03d9c9b Binary files /dev/null and b/res/drawable/enter_popup_icon.png differ diff --git a/res/drawable/ime_en.png b/res/drawable/ime_en.png new file mode 100644 index 0000000..652d7d7 Binary files /dev/null and b/res/drawable/ime_en.png differ diff --git a/res/drawable/ime_pinyin.png b/res/drawable/ime_pinyin.png new file mode 100644 index 0000000..b398d89 Binary files /dev/null and b/res/drawable/ime_pinyin.png differ diff --git a/res/drawable/key_balloon_bg.9.png b/res/drawable/key_balloon_bg.9.png new file mode 100644 index 0000000..1789b34 Binary files /dev/null and b/res/drawable/key_balloon_bg.9.png differ diff --git a/res/drawable/light_key_bg.9.png b/res/drawable/light_key_bg.9.png new file mode 100644 index 0000000..bda9b83 Binary files /dev/null and b/res/drawable/light_key_bg.9.png differ diff --git a/res/drawable/light_key_hl_bg.9.png b/res/drawable/light_key_hl_bg.9.png new file mode 100644 index 0000000..bdcf06e Binary files /dev/null and b/res/drawable/light_key_hl_bg.9.png differ diff --git a/res/drawable/light_key_up_bg.9.png b/res/drawable/light_key_up_bg.9.png new file mode 100644 index 0000000..0c16ed5 Binary files /dev/null and b/res/drawable/light_key_up_bg.9.png differ diff --git a/res/drawable/light_key_up_hl_bg.9.png b/res/drawable/light_key_up_hl_bg.9.png new file mode 100644 index 0000000..79621a9 Binary files /dev/null and b/res/drawable/light_key_up_hl_bg.9.png differ diff --git a/res/drawable/miniskb_bg.9.png b/res/drawable/miniskb_bg.9.png new file mode 100644 index 0000000..74fddc8 Binary files /dev/null and b/res/drawable/miniskb_bg.9.png differ diff --git a/res/drawable/normal_key_bg.9.png b/res/drawable/normal_key_bg.9.png new file mode 100644 index 0000000..7ba18dd Binary files /dev/null and b/res/drawable/normal_key_bg.9.png differ diff --git a/res/drawable/normal_key_hl_bg.9.png b/res/drawable/normal_key_hl_bg.9.png new file mode 100644 index 0000000..39b9314 Binary files /dev/null and b/res/drawable/normal_key_hl_bg.9.png differ diff --git a/res/drawable/num0.png b/res/drawable/num0.png new file mode 100644 index 0000000..e7007c8 Binary files /dev/null and b/res/drawable/num0.png differ diff --git a/res/drawable/num1.png b/res/drawable/num1.png new file mode 100644 index 0000000..aaac11b Binary files /dev/null and b/res/drawable/num1.png differ diff --git a/res/drawable/num2.png b/res/drawable/num2.png new file mode 100644 index 0000000..4372eb8 Binary files /dev/null and b/res/drawable/num2.png differ diff --git a/res/drawable/num3.png b/res/drawable/num3.png new file mode 100644 index 0000000..6f54c85 Binary files /dev/null and b/res/drawable/num3.png differ diff --git a/res/drawable/num4.png b/res/drawable/num4.png new file mode 100644 index 0000000..3e50bb9 Binary files /dev/null and b/res/drawable/num4.png differ diff --git a/res/drawable/num5.png b/res/drawable/num5.png new file mode 100644 index 0000000..c39ef44 Binary files /dev/null and b/res/drawable/num5.png differ diff --git a/res/drawable/num6.png b/res/drawable/num6.png new file mode 100644 index 0000000..ea88ceb Binary files /dev/null and b/res/drawable/num6.png differ diff --git a/res/drawable/num7.png b/res/drawable/num7.png new file mode 100644 index 0000000..4d75583 Binary files /dev/null and b/res/drawable/num7.png differ diff --git a/res/drawable/num8.png b/res/drawable/num8.png new file mode 100644 index 0000000..1a8ff94 Binary files /dev/null and b/res/drawable/num8.png differ diff --git a/res/drawable/num9.png b/res/drawable/num9.png new file mode 100644 index 0000000..8b344c0 Binary files /dev/null and b/res/drawable/num9.png differ diff --git a/res/drawable/numalt.png b/res/drawable/numalt.png new file mode 100644 index 0000000..32a2cf3 Binary files /dev/null and b/res/drawable/numalt.png differ diff --git a/res/drawable/numpound.png b/res/drawable/numpound.png new file mode 100644 index 0000000..b2419d9 Binary files /dev/null and b/res/drawable/numpound.png differ diff --git a/res/drawable/numstar.png b/res/drawable/numstar.png new file mode 100644 index 0000000..cb66f96 Binary files /dev/null and b/res/drawable/numstar.png differ diff --git a/res/drawable/period_full_icon.png b/res/drawable/period_full_icon.png new file mode 100644 index 0000000..1d7c6c7 Binary files /dev/null and b/res/drawable/period_full_icon.png differ diff --git a/res/drawable/period_full_popup_icon.png b/res/drawable/period_full_popup_icon.png new file mode 100644 index 0000000..07ee288 Binary files /dev/null and b/res/drawable/period_full_popup_icon.png differ diff --git a/res/drawable/period_icon.png b/res/drawable/period_icon.png new file mode 100644 index 0000000..6a8bd86 Binary files /dev/null and b/res/drawable/period_icon.png differ diff --git a/res/drawable/period_popup_icon.png b/res/drawable/period_popup_icon.png new file mode 100644 index 0000000..3e18e03 Binary files /dev/null and b/res/drawable/period_popup_icon.png differ diff --git a/res/drawable/search_icon.png b/res/drawable/search_icon.png new file mode 100644 index 0000000..127755d Binary files /dev/null and b/res/drawable/search_icon.png differ diff --git a/res/drawable/search_popup_icon.png b/res/drawable/search_popup_icon.png new file mode 100644 index 0000000..f4af341 Binary files /dev/null and b/res/drawable/search_popup_icon.png differ diff --git a/res/drawable/shift_off_icon.png b/res/drawable/shift_off_icon.png new file mode 100644 index 0000000..0566e5a Binary files /dev/null and b/res/drawable/shift_off_icon.png differ diff --git a/res/drawable/shift_off_popup_icon.png b/res/drawable/shift_off_popup_icon.png new file mode 100644 index 0000000..97f4661 Binary files /dev/null and b/res/drawable/shift_off_popup_icon.png differ diff --git a/res/drawable/shift_on_icon.png b/res/drawable/shift_on_icon.png new file mode 100644 index 0000000..ccaf05d Binary files /dev/null and b/res/drawable/shift_on_icon.png differ diff --git a/res/drawable/shift_on_popup_icon.png b/res/drawable/shift_on_popup_icon.png new file mode 100644 index 0000000..7194b30 Binary files /dev/null and b/res/drawable/shift_on_popup_icon.png differ diff --git a/res/drawable/skb_bg.png b/res/drawable/skb_bg.png new file mode 100644 index 0000000..f582462 Binary files /dev/null and b/res/drawable/skb_bg.png differ diff --git a/res/drawable/skb_container_bg.9.png b/res/drawable/skb_container_bg.9.png new file mode 100644 index 0000000..d6d7537 Binary files /dev/null and b/res/drawable/skb_container_bg.9.png differ diff --git a/res/drawable/smiley_icon.png b/res/drawable/smiley_icon.png new file mode 100644 index 0000000..daa2dc1 Binary files /dev/null and b/res/drawable/smiley_icon.png differ diff --git a/res/drawable/smiley_popup_icon.png b/res/drawable/smiley_popup_icon.png new file mode 100644 index 0000000..5d50370 Binary files /dev/null and b/res/drawable/smiley_popup_icon.png differ diff --git a/res/drawable/space_icon.png b/res/drawable/space_icon.png new file mode 100644 index 0000000..4e6273b Binary files /dev/null and b/res/drawable/space_icon.png differ diff --git a/res/drawable/space_popup_icon.png b/res/drawable/space_popup_icon.png new file mode 100644 index 0000000..739db68 Binary files /dev/null and b/res/drawable/space_popup_icon.png differ diff --git a/res/layout/candidates_container.xml b/res/layout/candidates_container.xml new file mode 100644 index 0000000..eda7e28 --- /dev/null +++ b/res/layout/candidates_container.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + diff --git a/res/layout/floating_container.xml b/res/layout/floating_container.xml new file mode 100644 index 0000000..ad9f3fd --- /dev/null +++ b/res/layout/floating_container.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/res/layout/skb_container.xml b/res/layout/skb_container.xml new file mode 100644 index 0000000..5a20aab --- /dev/null +++ b/res/layout/skb_container.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/res/raw/dict_pinyin.dat b/res/raw/dict_pinyin.dat new file mode 100644 index 0000000..1be3f9c Binary files /dev/null and b/res/raw/dict_pinyin.dat differ diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml new file mode 100644 index 0000000..3ee7751 --- /dev/null +++ b/res/values-land/dimens.xml @@ -0,0 +1,19 @@ + + + + 10dip + 16dip + diff --git a/res/values-port/dimens.xml b/res/values-port/dimens.xml new file mode 100644 index 0000000..47a3a53 --- /dev/null +++ b/res/values-port/dimens.xml @@ -0,0 +1,19 @@ + + + + 3dip + 18dip + diff --git a/res/values-zh/bools.xml b/res/values-zh/bools.xml new file mode 100644 index 0000000..c3291be --- /dev/null +++ b/res/values-zh/bools.xml @@ -0,0 +1,18 @@ + + + + true + diff --git a/res/values/colors.xml b/res/values/colors.xml new file mode 100644 index 0000000..e66a1ce --- /dev/null +++ b/res/values/colors.xml @@ -0,0 +1,27 @@ + + + + #ffffffff + #ffffffff + #ff000000 + #ff000000 + #ff000000 + #ffe35900 + #ff343233 + #ff000000 + #ffffffff + #ff777777 + diff --git a/res/values/dimens.xml b/res/values/dimens.xml new file mode 100644 index 0000000..0c8519e --- /dev/null +++ b/res/values/dimens.xml @@ -0,0 +1,18 @@ + + + + 0dip + diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000..09d64db --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,61 @@ + + + + 谷歌拼音输入法 + 谷歌拼音输入法设置 + + 1.0.0 + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 3 + + + setting_sound_key + setting_vibrate_key + setting_prediction_key + setting_switch_key + setting_advanced_key + + 谷歌拼音输入法设置 + 按键声音 + 按键震动 + 联想输入 + 中英文切换 + Shift-space + + 开启 + 关闭 + + 其它设置 + 词典同步等 + + diff --git a/res/xml/method.xml b/res/xml/method.xml new file mode 100644 index 0000000..2ff16b8 --- /dev/null +++ b/res/xml/method.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/res/xml/settings.xml b/res/xml/settings.xml new file mode 100644 index 0000000..b46e621 --- /dev/null +++ b/res/xml/settings.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + diff --git a/res/xml/skb_phone.xml b/res/xml/skb_phone.xml new file mode 100644 index 0000000..80a44fb --- /dev/null +++ b/res/xml/skb_phone.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/skb_qwerty.xml b/res/xml/skb_qwerty.xml new file mode 100644 index 0000000..57b8838 --- /dev/null +++ b/res/xml/skb_qwerty.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/skb_smiley.xml b/res/xml/skb_smiley.xml new file mode 100644 index 0000000..ec48b12 --- /dev/null +++ b/res/xml/skb_smiley.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/skb_sym1.xml b/res/xml/skb_sym1.xml new file mode 100644 index 0000000..918fd33 --- /dev/null +++ b/res/xml/skb_sym1.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/skb_sym2.xml b/res/xml/skb_sym2.xml new file mode 100644 index 0000000..a55f91e --- /dev/null +++ b/res/xml/skb_sym2.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/skb_template1.xml b/res/xml/skb_template1.xml new file mode 100644 index 0000000..16338eb --- /dev/null +++ b/res/xml/skb_template1.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/android/inputmethod/pinyin/BalloonHint.java b/src/com/android/inputmethod/pinyin/BalloonHint.java new file mode 100644 index 0000000..919dbf1 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/BalloonHint.java @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Paint.FontMetricsInt; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.view.Gravity; +import android.view.View; +import android.view.View.MeasureSpec; +import android.widget.PopupWindow; + +/** + * Subclass of PopupWindow used as the feedback when user presses on a soft key + * or a candidate. + */ +public class BalloonHint extends PopupWindow { + /** + * Delayed time to show the balloon hint. + */ + public static final int TIME_DELAY_SHOW = 0; + + /** + * Delayed time to dismiss the balloon hint. + */ + public static final int TIME_DELAY_DISMISS = 200; + + /** + * The padding information of the balloon. Because PopupWindow's background + * can not be changed unless it is dismissed and shown again, we set the + * real background drawable to the content view, and make the PopupWindow's + * background transparent. So actually this padding information is for the + * content view. + */ + private Rect mPaddingRect = new Rect(); + + /** + * The context used to create this balloon hint object. + */ + private Context mContext; + + /** + * Parent used to show the balloon window. + */ + private View mParent; + + /** + * The content view of the balloon. + */ + BalloonView mBalloonView; + + /** + * The measuring specification used to determine its size. Key-press + * balloons and candidates balloons have different measuring specifications. + */ + private int mMeasureSpecMode; + + /** + * Used to indicate whether the balloon needs to be dismissed forcibly. + */ + private boolean mForceDismiss; + + /** + * Timer used to show/dismiss the balloon window with some time delay. + */ + private BalloonTimer mBalloonTimer; + + private int mParentLocationInWindow[] = new int[2]; + + public BalloonHint(Context context, View parent, int measureSpecMode) { + super(context); + mParent = parent; + mMeasureSpecMode = measureSpecMode; + + setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + setTouchable(false); + setBackgroundDrawable(new ColorDrawable(0)); + + mBalloonView = new BalloonView(context); + mBalloonView.setClickable(false); + setContentView(mBalloonView); + + mBalloonTimer = new BalloonTimer(); + } + + public Context getContext() { + return mContext; + } + + public Rect getPadding() { + return mPaddingRect; + } + + public void setBalloonBackground(Drawable drawable) { + // We usually pick up a background from a soft keyboard template, + // and the object may has been set to this balloon before. + if (mBalloonView.getBackground() == drawable) return; + mBalloonView.setBackgroundDrawable(drawable); + + if (null != drawable) { + drawable.getPadding(mPaddingRect); + } else { + mPaddingRect.set(0, 0, 0, 0); + } + } + + /** + * Set configurations to show text label in this balloon. + * + * @param label The text label to show in the balloon. + * @param textSize The text size used to show label. + * @param textBold Used to indicate whether the label should be bold. + * @param textColor The text color used to show label. + * @param width The desired width of the balloon. The real width is + * determined by the desired width and balloon's measuring + * specification. + * @param height The desired width of the balloon. The real width is + * determined by the desired width and balloon's measuring + * specification. + */ + public void setBalloonConfig(String label, float textSize, + boolean textBold, int textColor, int width, int height) { + mBalloonView.setTextConfig(label, textSize, textBold, textColor); + setBalloonSize(width, height); + } + + /** + * Set configurations to show text label in this balloon. + * + * @param icon The icon used to shown in this balloon. + * @param width The desired width of the balloon. The real width is + * determined by the desired width and balloon's measuring + * specification. + * @param height The desired width of the balloon. The real width is + * determined by the desired width and balloon's measuring + * specification. + */ + public void setBalloonConfig(Drawable icon, int width, int height) { + mBalloonView.setIcon(icon); + setBalloonSize(width, height); + } + + + public boolean needForceDismiss() { + return mForceDismiss; + } + + public int getPaddingLeft() { + return mPaddingRect.left; + } + + public int getPaddingTop() { + return mPaddingRect.top; + } + + public int getPaddingRight() { + return mPaddingRect.right; + } + + public int getPaddingBottom() { + return mPaddingRect.bottom; + } + + public void delayedShow(long delay, int locationInParent[]) { + if (mBalloonTimer.isPending()) { + mBalloonTimer.removeTimer(); + } + if (delay <= 0) { + mParent.getLocationInWindow(mParentLocationInWindow); + showAtLocation(mParent, Gravity.LEFT | Gravity.TOP, + locationInParent[0], locationInParent[1] + + mParentLocationInWindow[1]); + } else { + mBalloonTimer.startTimer(delay, BalloonTimer.ACTION_SHOW, + locationInParent, -1, -1); + } + } + + public void delayedUpdate(long delay, int locationInParent[], + int width, int height) { + mBalloonView.invalidate(); + if (mBalloonTimer.isPending()) { + mBalloonTimer.removeTimer(); + } + if (delay <= 0) { + mParent.getLocationInWindow(mParentLocationInWindow); + update(locationInParent[0], locationInParent[1] + + mParentLocationInWindow[1], width, height); + } else { + mBalloonTimer.startTimer(delay, BalloonTimer.ACTION_UPDATE, + locationInParent, width, height); + } + } + + public void delayedDismiss(long delay) { + if (mBalloonTimer.isPending()) { + mBalloonTimer.removeTimer(); + int pendingAction = mBalloonTimer.getAction(); + if (0 != delay && BalloonTimer.ACTION_HIDE != pendingAction) { + mBalloonTimer.run(); + } + } + if (delay <= 0) { + dismiss(); + } else { + mBalloonTimer.startTimer(delay, BalloonTimer.ACTION_HIDE, null, -1, + -1); + } + } + + public void removeTimer() { + if (mBalloonTimer.isPending()) { + mBalloonTimer.removeTimer(); + } + } + + private void setBalloonSize(int width, int height) { + int widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, + mMeasureSpecMode); + int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, + mMeasureSpecMode); + mBalloonView.measure(widthMeasureSpec, heightMeasureSpec); + + int oldWidth = getWidth(); + int oldHeight = getHeight(); + int newWidth = mBalloonView.getMeasuredWidth() + getPaddingLeft() + + getPaddingRight(); + int newHeight = mBalloonView.getMeasuredHeight() + getPaddingTop() + + getPaddingBottom(); + setWidth(newWidth); + setHeight(newHeight); + + // If update() is called to update both size and position, the system + // will first MOVE the PopupWindow to the new position, and then + // perform a size-updating operation, so there will be a flash in + // PopupWindow if user presses a key and moves finger to next one whose + // size is different. + // PopupWindow will handle the updating issue in one go in the future, + // but before that, if we find the size is changed, a mandatory dismiss + // operation is required. In our UI design, normal QWERTY keys' width + // can be different in 1-pixel, and we do not dismiss the balloon when + // user move between QWERTY keys. + mForceDismiss = false; + if (isShowing()) { + mForceDismiss = oldWidth - newWidth > 1 || newWidth - oldWidth > 1; + } + } + + + private class BalloonTimer extends Handler implements Runnable { + public static final int ACTION_SHOW = 1; + public static final int ACTION_HIDE = 2; + public static final int ACTION_UPDATE = 3; + + /** + * The pending action. + */ + private int mAction; + + private int mPositionInParent[] = new int[2]; + private int mWidth; + private int mHeight; + + private boolean mTimerPending = false; + + public void startTimer(long time, int action, int positionInParent[], + int width, int height) { + mAction = action; + if (ACTION_HIDE != action) { + mPositionInParent[0] = positionInParent[0]; + mPositionInParent[1] = positionInParent[1]; + } + mWidth = width; + mHeight = height; + postDelayed(this, time); + mTimerPending = true; + } + + public boolean isPending() { + return mTimerPending; + } + + public boolean removeTimer() { + if (mTimerPending) { + mTimerPending = false; + removeCallbacks(this); + return true; + } + + return false; + } + + public int getAction() { + return mAction; + } + + public void run() { + switch (mAction) { + case ACTION_SHOW: + mParent.getLocationInWindow(mParentLocationInWindow); + showAtLocation(mParent, Gravity.LEFT | Gravity.TOP, + mPositionInParent[0], mPositionInParent[1] + + mParentLocationInWindow[1]); + break; + case ACTION_HIDE: + dismiss(); + break; + case ACTION_UPDATE: + mParent.getLocationInWindow(mParentLocationInWindow); + update(mPositionInParent[0], mPositionInParent[1] + + mParentLocationInWindow[1], mWidth, mHeight); + } + mTimerPending = false; + } + } + + private class BalloonView extends View { + /** + * Suspension points used to display long items. + */ + private static final String SUSPENSION_POINTS = "..."; + + /** + * The icon to be shown. If it is not null, {@link #mLabel} will be + * ignored. + */ + private Drawable mIcon; + + /** + * The label to be shown. It is enabled only if {@link #mIcon} is null. + */ + private String mLabel; + + private int mLabeColor = 0xff000000; + private Paint mPaintLabel; + private FontMetricsInt mFmi; + + /** + * The width to show suspension points. + */ + private float mSuspensionPointsWidth; + + + public BalloonView(Context context) { + super(context); + mPaintLabel = new Paint(); + mPaintLabel.setColor(mLabeColor); + mPaintLabel.setAntiAlias(true); + mPaintLabel.setFakeBoldText(true); + mFmi = mPaintLabel.getFontMetricsInt(); + } + + public void setIcon(Drawable icon) { + mIcon = icon; + } + + public void setTextConfig(String label, float fontSize, + boolean textBold, int textColor) { + // Icon should be cleared so that the label will be enabled. + mIcon = null; + mLabel = label; + mPaintLabel.setTextSize(fontSize); + mPaintLabel.setFakeBoldText(textBold); + mPaintLabel.setColor(textColor); + mFmi = mPaintLabel.getFontMetricsInt(); + mSuspensionPointsWidth = mPaintLabel.measureText(SUSPENSION_POINTS); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + final int widthSize = MeasureSpec.getSize(widthMeasureSpec); + final int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthMode == MeasureSpec.EXACTLY) { + setMeasuredDimension(widthSize, heightSize); + return; + } + + int measuredWidth = mPaddingLeft + mPaddingRight; + int measuredHeight = mPaddingTop + mPaddingBottom; + if (null != mIcon) { + measuredWidth += mIcon.getIntrinsicWidth(); + measuredHeight += mIcon.getIntrinsicHeight(); + } else if (null != mLabel) { + measuredWidth += (int) (mPaintLabel.measureText(mLabel)); + measuredHeight += mFmi.bottom - mFmi.top; + } + if (widthSize > measuredWidth || widthMode == MeasureSpec.AT_MOST) { + measuredWidth = widthSize; + } + + if (heightSize > measuredHeight + || heightMode == MeasureSpec.AT_MOST) { + measuredHeight = heightSize; + } + + int maxWidth = Environment.getInstance().getScreenWidth() - + mPaddingLeft - mPaddingRight; + if (measuredWidth > maxWidth) { + measuredWidth = maxWidth; + } + setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + protected void onDraw(Canvas canvas) { + int width = getWidth(); + int height = getHeight(); + if (null != mIcon) { + int marginLeft = (width - mIcon.getIntrinsicWidth()) / 2; + int marginRight = width - mIcon.getIntrinsicWidth() + - marginLeft; + int marginTop = (height - mIcon.getIntrinsicHeight()) / 2; + int marginBottom = height - mIcon.getIntrinsicHeight() + - marginTop; + mIcon.setBounds(marginLeft, marginTop, width - marginRight, + height - marginBottom); + mIcon.draw(canvas); + } else if (null != mLabel) { + float labelMeasuredWidth = mPaintLabel.measureText(mLabel); + float x = mPaddingLeft; + x += (width - labelMeasuredWidth - mPaddingLeft - mPaddingRight) / 2.0f; + String labelToDraw = mLabel; + if (x < mPaddingLeft) { + x = mPaddingLeft; + labelToDraw = getLimitedLabelForDrawing(mLabel, + width - mPaddingLeft - mPaddingRight); + } + + int fontHeight = mFmi.bottom - mFmi.top; + float marginY = (height - fontHeight) / 2.0f; + float y = marginY - mFmi.top; + canvas.drawText(labelToDraw, x, y, mPaintLabel); + } + } + + private String getLimitedLabelForDrawing(String rawLabel, + float widthToDraw) { + int subLen = rawLabel.length(); + if (subLen <= 1) return rawLabel; + do { + subLen--; + float width = mPaintLabel.measureText(rawLabel, 0, subLen); + if (width + mSuspensionPointsWidth <= widthToDraw || 1 >= subLen) { + return rawLabel.substring(0, subLen) + + SUSPENSION_POINTS; + } + } while (true); + } + } +} diff --git a/src/com/android/inputmethod/pinyin/CandidateView.java b/src/com/android/inputmethod/pinyin/CandidateView.java new file mode 100644 index 0000000..8dc1bf1 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/CandidateView.java @@ -0,0 +1,760 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import com.android.inputmethod.pinyin.PinyinIME.DecodingInfo; + +import java.util.Vector; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Paint.FontMetricsInt; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +/** + * View to show candidate list. There two candidate view instances which are + * used to show animation when user navigates between pages. + */ +public class CandidateView extends View { + /** + * The minimum width to show a item. + */ + private static final float MIN_ITEM_WIDTH = 22; + + /** + * Suspension points used to display long items. + */ + private static final String SUSPENSION_POINTS = "..."; + + /** + * The width to draw candidates. + */ + private int mContentWidth; + + /** + * The height to draw candidate content. + */ + private int mContentHeight; + + /** + * Whether footnotes are displayed. Footnote is shown when hardware keyboard + * is available. + */ + private boolean mShowFootnote = true; + + /** + * Balloon hint for candidate press/release. + */ + private BalloonHint mBalloonHint; + + /** + * Desired position of the balloon to the input view. + */ + private int mHintPositionToInputView[] = new int[2]; + + /** + * Decoding result to show. + */ + private DecodingInfo mDecInfo; + + /** + * Listener used to notify IME that user clicks a candidate, or navigate + * between them. + */ + private CandidateViewListener mCvListener; + + /** + * Used to notify the container to update the status of forward/backward + * arrows. + */ + private ArrowUpdater mArrowUpdater; + + /** + * If true, update the arrow status when drawing candidates. + */ + private boolean mUpdateArrowStatusWhenDraw = false; + + /** + * Page number of the page displayed in this view. + */ + private int mPageNo; + + /** + * Active candidate position in this page. + */ + private int mActiveCandInPage; + + /** + * Used to decided whether the active candidate should be highlighted or + * not. If user changes focus to composing view (The view to show Pinyin + * string), the highlight in candidate view should be removed. + */ + private boolean mEnableActiveHighlight = true; + + /** + * The page which is just calculated. + */ + private int mPageNoCalculated = -1; + + /** + * The Drawable used to display as the background of the high-lighted item. + */ + private Drawable mActiveCellDrawable; + + /** + * The Drawable used to display as separators between candidates. + */ + private Drawable mSeparatorDrawable; + + /** + * Color to draw normal candidates generated by IME. + */ + private int mImeCandidateColor; + + /** + * Color to draw normal candidates Recommended by application. + */ + private int mRecommendedCandidateColor; + + /** + * Color to draw the normal(not highlighted) candidates, it can be one of + * {@link #mImeCandidateColor} or {@link #mRecommendedCandidateColor}. + */ + private int mNormalCandidateColor; + + /** + * Color to draw the active(highlighted) candidates, including candidates + * from IME and candidates from application. + */ + private int mActiveCandidateColor; + + /** + * Text size to draw candidates generated by IME. + */ + private int mImeCandidateTextSize; + + /** + * Text size to draw candidates recommended by application. + */ + private int mRecommendedCandidateTextSize; + + /** + * The current text size to draw candidates. It can be one of + * {@link #mImeCandidateTextSize} or {@link #mRecommendedCandidateTextSize}. + */ + private int mCandidateTextSize; + + /** + * Paint used to draw candidates. + */ + private Paint mCandidatesPaint; + + /** + * Used to draw footnote. + */ + private Paint mFootnotePaint; + + /** + * The width to show suspension points. + */ + private float mSuspensionPointsWidth; + + /** + * Rectangle used to draw the active candidate. + */ + private RectF mActiveCellRect; + + /** + * Left and right margins for a candidate. It is specified in xml, and is + * the minimum margin for a candidate. The actual gap between two candidates + * is 2 * {@link #mCandidateMargin} + {@link #mSeparatorDrawable}. + * getIntrinsicWidth(). Because length of candidate is not fixed, there can + * be some extra space after the last candidate in the current page. In + * order to achieve best look-and-feel, this extra space will be divided and + * allocated to each candidates. + */ + private float mCandidateMargin; + + /** + * Left and right extra margins for a candidate. + */ + private float mCandidateMarginExtra; + + /** + * Rectangles for the candidates in this page. + **/ + private Vector mCandRects; + + /** + * FontMetricsInt used to measure the size of candidates. + */ + private FontMetricsInt mFmiCandidates; + + /** + * FontMetricsInt used to measure the size of footnotes. + */ + private FontMetricsInt mFmiFootnote; + + private PressTimer mTimer = new PressTimer(); + + private GestureDetector mGestureDetector; + + private int mLocationTmp[] = new int[2]; + + public CandidateView(Context context, AttributeSet attrs) { + super(context, attrs); + + Resources r = context.getResources(); + + Configuration conf = r.getConfiguration(); + if (conf.keyboard == Configuration.KEYBOARD_NOKEYS + || conf.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { + mShowFootnote = false; + } + + mActiveCellDrawable = r.getDrawable(R.drawable.candidate_hl_bg); + mSeparatorDrawable = r.getDrawable(R.drawable.candidates_vertical_line); + mCandidateMargin = r.getDimension(R.dimen.candidate_margin_left_right); + + mImeCandidateColor = r.getColor(R.color.candidate_color); + mRecommendedCandidateColor = r.getColor(R.color.recommended_candidate_color); + mNormalCandidateColor = mImeCandidateColor; + mActiveCandidateColor = r.getColor(R.color.active_candidate_color); + + mCandidatesPaint = new Paint(); + mCandidatesPaint.setAntiAlias(true); + + mFootnotePaint = new Paint(); + mFootnotePaint.setAntiAlias(true); + mFootnotePaint.setColor(r.getColor(R.color.footnote_color)); + mActiveCellRect = new RectF(); + + mCandRects = new Vector(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int mOldWidth = mMeasuredWidth; + int mOldHeight = mMeasuredHeight; + + setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), + widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), + heightMeasureSpec)); + + if (mOldWidth != mMeasuredWidth || mOldHeight != mMeasuredHeight) { + onSizeChanged(); + } + } + + public void initialize(ArrowUpdater arrowUpdater, BalloonHint balloonHint, + GestureDetector gestureDetector, CandidateViewListener cvListener) { + mArrowUpdater = arrowUpdater; + mBalloonHint = balloonHint; + mGestureDetector = gestureDetector; + mCvListener = cvListener; + } + + public void setDecodingInfo(DecodingInfo decInfo) { + if (null == decInfo) return; + mDecInfo = decInfo; + mPageNoCalculated = -1; + + if (mDecInfo.candidatesFromApp()) { + mNormalCandidateColor = mRecommendedCandidateColor; + mCandidateTextSize = mRecommendedCandidateTextSize; + } else { + mNormalCandidateColor = mImeCandidateColor; + mCandidateTextSize = mImeCandidateTextSize; + } + if (mCandidatesPaint.getTextSize() != mCandidateTextSize) { + mCandidatesPaint.setTextSize(mCandidateTextSize); + mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); + mSuspensionPointsWidth = + mCandidatesPaint.measureText(SUSPENSION_POINTS); + } + + // Remove any pending timer for the previous list. + mTimer.removeTimer(); + } + + public int getActiveCandiatePosInPage() { + return mActiveCandInPage; + } + + public int getActiveCandiatePosGlobal() { + return mDecInfo.mPageStart.get(mPageNo) + mActiveCandInPage; + } + + /** + * Show a page in the decoding result set previously. + * + * @param pageNo Which page to show. + * @param activeCandInPage Which candidate should be set as active item. + * @param enableActiveHighlight When false, active item will not be + * highlighted. + */ + public void showPage(int pageNo, int activeCandInPage, + boolean enableActiveHighlight) { + if (null == mDecInfo) return; + mPageNo = pageNo; + mActiveCandInPage = activeCandInPage; + if (mEnableActiveHighlight != enableActiveHighlight) { + mEnableActiveHighlight = enableActiveHighlight; + } + + if (!calculatePage(mPageNo)) { + mUpdateArrowStatusWhenDraw = true; + } else { + mUpdateArrowStatusWhenDraw = false; + } + + invalidate(); + } + + public void enableActiveHighlight(boolean enableActiveHighlight) { + if (enableActiveHighlight == mEnableActiveHighlight) return; + + mEnableActiveHighlight = enableActiveHighlight; + invalidate(); + } + + public boolean activeCursorForward() { + if (!mDecInfo.pageReady(mPageNo)) return false; + int pageSize = mDecInfo.mPageStart.get(mPageNo + 1) + - mDecInfo.mPageStart.get(mPageNo); + if (mActiveCandInPage + 1 < pageSize) { + showPage(mPageNo, mActiveCandInPage + 1, true); + return true; + } + return false; + } + + public boolean activeCurseBackward() { + if (mActiveCandInPage > 0) { + showPage(mPageNo, mActiveCandInPage - 1, true); + return true; + } + return false; + } + + private void onSizeChanged() { + mContentWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; + mContentHeight = (int) ((mMeasuredHeight - mPaddingTop - mPaddingBottom) * 0.95f); + /** + * How to decide the font size if the height for display is given? + * Now it is implemented in a stupid way. + */ + int textSize = 1; + mCandidatesPaint.setTextSize(textSize); + mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); + while (mFmiCandidates.bottom - mFmiCandidates.top < mContentHeight) { + textSize++; + mCandidatesPaint.setTextSize(textSize); + mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); + } + + mImeCandidateTextSize = textSize; + mRecommendedCandidateTextSize = textSize * 3 / 4; + if (null == mDecInfo) { + mCandidateTextSize = mImeCandidateTextSize; + mCandidatesPaint.setTextSize(mCandidateTextSize); + mFmiCandidates = mCandidatesPaint.getFontMetricsInt(); + mSuspensionPointsWidth = + mCandidatesPaint.measureText(SUSPENSION_POINTS); + } else { + // Reset the decoding information to update members for painting. + setDecodingInfo(mDecInfo); + } + + textSize = 1; + mFootnotePaint.setTextSize(textSize); + mFmiFootnote = mFootnotePaint.getFontMetricsInt(); + while (mFmiFootnote.bottom - mFmiFootnote.top < mContentHeight / 2) { + textSize++; + mFootnotePaint.setTextSize(textSize); + mFmiFootnote = mFootnotePaint.getFontMetricsInt(); + } + textSize--; + mFootnotePaint.setTextSize(textSize); + mFmiFootnote = mFootnotePaint.getFontMetricsInt(); + + // When the size is changed, the first page will be displayed. + mPageNo = 0; + mActiveCandInPage = 0; + } + + private boolean calculatePage(int pageNo) { + if (pageNo == mPageNoCalculated) return true; + + mContentWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; + mContentHeight = (int) ((mMeasuredHeight - mPaddingTop - mPaddingBottom) * 0.95f); + + if (mContentWidth <= 0 || mContentHeight <= 0) return false; + + int candSize = mDecInfo.mCandidatesList.size(); + + // If the size of page exists, only calculate the extra margin. + boolean onlyExtraMargin = false; + int fromPage = mDecInfo.mPageStart.size() - 1; + if (mDecInfo.mPageStart.size() > pageNo + 1) { + onlyExtraMargin = true; + fromPage = pageNo; + } + + // If the previous pages have no information, calculate them first. + for (int p = fromPage; p <= pageNo; p++) { + int pStart = mDecInfo.mPageStart.get(p); + int pSize = 0; + int charNum = 0; + float lastItemWidth = 0; + + float xPos; + xPos = 0; + xPos += mSeparatorDrawable.getIntrinsicWidth(); + while (xPos < mContentWidth && pStart + pSize < candSize) { + int itemPos = pStart + pSize; + String itemStr = mDecInfo.mCandidatesList.get(itemPos); + float itemWidth = mCandidatesPaint.measureText(itemStr); + if (itemWidth < MIN_ITEM_WIDTH) itemWidth = MIN_ITEM_WIDTH; + + itemWidth += mCandidateMargin * 2; + itemWidth += mSeparatorDrawable.getIntrinsicWidth(); + if (xPos + itemWidth < mContentWidth || 0 == pSize) { + xPos += itemWidth; + lastItemWidth = itemWidth; + pSize++; + charNum += itemStr.length(); + } else { + break; + } + } + if (!onlyExtraMargin) { + mDecInfo.mPageStart.add(pStart + pSize); + mDecInfo.mCnToPage.add(mDecInfo.mCnToPage.get(p) + charNum); + } + + float marginExtra = (mContentWidth - xPos) / pSize / 2; + + if (mContentWidth - xPos > lastItemWidth) { + // Must be the last page, because if there are more items, + // the next item's width must be less than lastItemWidth. + // In this case, if the last margin is less than the current + // one, the last margin can be used, so that the + // look-and-feeling will be the same as the previous page. + if (mCandidateMarginExtra <= marginExtra) { + marginExtra = mCandidateMarginExtra; + } + } else if (pSize == 1) { + marginExtra = 0; + } + mCandidateMarginExtra = marginExtra; + } + mPageNoCalculated = pageNo; + return true; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + // The invisible candidate view(the one which is not in foreground) can + // also be called to drawn, but its decoding result and candidate list + // may be empty. + if (null == mDecInfo || mDecInfo.isCandidatesListEmpty()) return; + + // Calculate page. If the paging information is ready, the function will + // return at once. + calculatePage(mPageNo); + + int pStart = mDecInfo.mPageStart.get(mPageNo); + int pSize = mDecInfo.mPageStart.get(mPageNo + 1) - pStart; + float candMargin = mCandidateMargin + mCandidateMarginExtra; + if (mActiveCandInPage > pSize - 1) { + mActiveCandInPage = pSize - 1; + } + + mCandRects.removeAllElements(); + + float xPos = mPaddingLeft; + int yPos = (getMeasuredHeight() - + (mFmiCandidates.bottom - mFmiCandidates.top)) / 2 + - mFmiCandidates.top; + xPos += drawVerticalSeparator(canvas, xPos); + for (int i = 0; i < pSize; i++) { + float footnoteSize = 0; + String footnote = null; + if (mShowFootnote) { + footnote = Integer.toString(i + 1); + footnoteSize = mFootnotePaint.measureText(footnote); + assert (footnoteSize < candMargin); + } + String cand = mDecInfo.mCandidatesList.get(pStart + i); + float candidateWidth = mCandidatesPaint.measureText(cand); + float centerOffset = 0; + if (candidateWidth < MIN_ITEM_WIDTH) { + centerOffset = (MIN_ITEM_WIDTH - candidateWidth) / 2; + candidateWidth = MIN_ITEM_WIDTH; + } + + float itemTotalWidth = candidateWidth + 2 * candMargin; + + if (mActiveCandInPage == i && mEnableActiveHighlight) { + mActiveCellRect.set(xPos, mPaddingTop + 1, xPos + + itemTotalWidth, getHeight() - mPaddingBottom - 1); + mActiveCellDrawable.setBounds((int) mActiveCellRect.left, + (int) mActiveCellRect.top, (int) mActiveCellRect.right, + (int) mActiveCellRect.bottom); + mActiveCellDrawable.draw(canvas); + } + + if (mCandRects.size() < pSize) mCandRects.add(new RectF()); + mCandRects.elementAt(i).set(xPos - 1, yPos + mFmiCandidates.top, + xPos + itemTotalWidth + 1, yPos + mFmiCandidates.bottom); + + // Draw footnote + if (mShowFootnote) { + canvas.drawText(footnote, xPos + (candMargin - footnoteSize) + / 2, yPos, mFootnotePaint); + } + + // Left margin + xPos += candMargin; + if (candidateWidth > mContentWidth - xPos - centerOffset) { + cand = getLimitedCandidateForDrawing(cand, + mContentWidth - xPos - centerOffset); + } + if (mActiveCandInPage == i && mEnableActiveHighlight) { + mCandidatesPaint.setColor(mActiveCandidateColor); + } else { + mCandidatesPaint.setColor(mNormalCandidateColor); + } + canvas.drawText(cand, xPos + centerOffset, yPos, + mCandidatesPaint); + + // Candidate and right margin + xPos += candidateWidth + candMargin; + + // Draw the separator between candidates. + xPos += drawVerticalSeparator(canvas, xPos); + } + + // Update the arrow status of the container. + if (null != mArrowUpdater && mUpdateArrowStatusWhenDraw) { + mArrowUpdater.updateArrowStatus(); + mUpdateArrowStatusWhenDraw = false; + } + } + + private String getLimitedCandidateForDrawing(String rawCandidate, + float widthToDraw) { + int subLen = rawCandidate.length(); + if (subLen <= 1) return rawCandidate; + do { + subLen--; + float width = mCandidatesPaint.measureText(rawCandidate, 0, subLen); + if (width + mSuspensionPointsWidth <= widthToDraw || 1 >= subLen) { + return rawCandidate.substring(0, subLen) + + SUSPENSION_POINTS; + } + } while (true); + } + + private float drawVerticalSeparator(Canvas canvas, float xPos) { + mSeparatorDrawable.setBounds((int) xPos, mPaddingTop, (int) xPos + + mSeparatorDrawable.getIntrinsicWidth(), getMeasuredHeight() + - mPaddingBottom); + mSeparatorDrawable.draw(canvas); + return mSeparatorDrawable.getIntrinsicWidth(); + } + + private int mapToItemInPage(int x, int y) { + // mCandRects.size() == 0 happens when the page is set, but + // touch events occur before onDraw(). It usually happens with + // monkey test. + if (!mDecInfo.pageReady(mPageNo) || mPageNoCalculated != mPageNo + || mCandRects.size() == 0) { + return -1; + } + + int pageStart = mDecInfo.mPageStart.get(mPageNo); + int pageSize = mDecInfo.mPageStart.get(mPageNo + 1) - pageStart; + if (mCandRects.size() < pageSize) { + return -1; + } + + // If not found, try to find the nearest one. + float nearestDis = Float.MAX_VALUE; + int nearest = -1; + for (int i = 0; i < pageSize; i++) { + RectF r = mCandRects.elementAt(i); + if (r.left < x && r.right > x && r.top < y && r.bottom > y) { + return i; + } + float disx = (r.left + r.right) / 2 - x; + float disy = (r.top + r.bottom) / 2 - y; + float dis = disx * disx + disy * disy; + if (dis < nearestDis) { + nearestDis = dis; + nearest = i; + } + } + + return nearest; + } + + // Because the candidate view under the current focused one may also get + // touching events. Here we just bypass the event to the container and let + // it decide which view should handle the event. + @Override + public boolean onTouchEvent(MotionEvent event) { + return super.onTouchEvent(event); + } + + public boolean onTouchEventReal(MotionEvent event) { + // The page in the background can also be touched. + if (null == mDecInfo || !mDecInfo.pageReady(mPageNo) + || mPageNoCalculated != mPageNo) return true; + + int x, y; + x = (int) event.getX(); + y = (int) event.getY(); + + if (mGestureDetector.onTouchEvent(event)) { + mTimer.removeTimer(); + mBalloonHint.delayedDismiss(0); + return true; + } + + int clickedItemInPage = -1; + + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + clickedItemInPage = mapToItemInPage(x, y); + if (clickedItemInPage >= 0) { + invalidate(); + mCvListener.onClickChoice(clickedItemInPage + + mDecInfo.mPageStart.get(mPageNo)); + } + mBalloonHint.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS); + break; + + case MotionEvent.ACTION_DOWN: + clickedItemInPage = mapToItemInPage(x, y); + if (clickedItemInPage >= 0) { + showBalloon(clickedItemInPage, true); + mTimer.startTimer(BalloonHint.TIME_DELAY_SHOW, mPageNo, + clickedItemInPage); + } + break; + + case MotionEvent.ACTION_CANCEL: + break; + + case MotionEvent.ACTION_MOVE: + clickedItemInPage = mapToItemInPage(x, y); + if (clickedItemInPage >= 0 + && (clickedItemInPage != mTimer.getActiveCandOfPageToShow() || mPageNo != mTimer + .getPageToShow())) { + showBalloon(clickedItemInPage, true); + mTimer.startTimer(BalloonHint.TIME_DELAY_SHOW, mPageNo, + clickedItemInPage); + } + } + return true; + } + + private void showBalloon(int candPos, boolean delayedShow) { + mBalloonHint.removeTimer(); + + RectF r = mCandRects.elementAt(candPos); + int desired_width = (int) (r.right - r.left); + int desired_height = (int) (r.bottom - r.top); + mBalloonHint.setBalloonConfig(mDecInfo.mCandidatesList + .get(mDecInfo.mPageStart.get(mPageNo) + candPos), 44, true, + mImeCandidateColor, desired_width, desired_height); + + getLocationOnScreen(mLocationTmp); + mHintPositionToInputView[0] = mLocationTmp[0] + + (int) (r.left - (mBalloonHint.getWidth() - desired_width) / 2); + mHintPositionToInputView[1] = -mBalloonHint.getHeight(); + + long delay = BalloonHint.TIME_DELAY_SHOW; + if (!delayedShow) delay = 0; + mBalloonHint.dismiss(); + if (!mBalloonHint.isShowing()) { + mBalloonHint.delayedShow(delay, mHintPositionToInputView); + } else { + mBalloonHint.delayedUpdate(0, mHintPositionToInputView, -1, -1); + } + } + + private class PressTimer extends Handler implements Runnable { + private boolean mTimerPending = false; + private int mPageNoToShow; + private int mActiveCandOfPage; + + public PressTimer() { + super(); + } + + public void startTimer(long afterMillis, int pageNo, int activeInPage) { + mTimer.removeTimer(); + postDelayed(this, afterMillis); + mTimerPending = true; + mPageNoToShow = pageNo; + mActiveCandOfPage = activeInPage; + } + + public int getPageToShow() { + return mPageNoToShow; + } + + public int getActiveCandOfPageToShow() { + return mActiveCandOfPage; + } + + public boolean removeTimer() { + if (mTimerPending) { + mTimerPending = false; + removeCallbacks(this); + return true; + } + return false; + } + + public boolean isPending() { + return mTimerPending; + } + + public void run() { + if (mPageNoToShow >= 0 && mActiveCandOfPage >= 0) { + // Always enable to highlight the clicked one. + showPage(mPageNoToShow, mActiveCandOfPage, true); + invalidate(); + } + mTimerPending = false; + } + } +} diff --git a/src/com/android/inputmethod/pinyin/CandidateViewListener.java b/src/com/android/inputmethod/pinyin/CandidateViewListener.java new file mode 100644 index 0000000..795d119 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/CandidateViewListener.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +/** + * Interface to notify the input method when the user clicks a candidate or + * makes a direction-gesture on candidate view. + */ +public interface CandidateViewListener { + public void onClickChoice(int choiceId); + + public void onToLeftGesture(); + + public void onToRightGesture(); + + public void onToTopGesture(); + + public void onToBottomGesture(); +} diff --git a/src/com/android/inputmethod/pinyin/CandidatesContainer.java b/src/com/android/inputmethod/pinyin/CandidatesContainer.java new file mode 100644 index 0000000..5b2a999 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/CandidatesContainer.java @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import com.android.inputmethod.pinyin.PinyinIME.DecodingInfo; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.TranslateAnimation; +import android.view.animation.Animation.AnimationListener; +import android.widget.ImageButton; +import android.widget.RelativeLayout; +import android.widget.ViewFlipper; + +interface ArrowUpdater { + void updateArrowStatus(); +} + + +/** + * Container used to host the two candidate views. When user drags on candidate + * view, animation is used to dismiss the current candidate view and show a new + * one. These two candidate views and their parent are hosted by this container. + *

+ * Besides the candidate views, there are two arrow views to show the page + * forward/backward arrows. + *

+ */ +public class CandidatesContainer extends RelativeLayout implements + OnTouchListener, AnimationListener, ArrowUpdater { + /** + * Alpha value to show an enabled arrow. + */ + private static int ARROW_ALPHA_ENABLED = 0xff; + + /** + * Alpha value to show an disabled arrow. + */ + private static int ARROW_ALPHA_DISABLED = 0x40; + + /** + * Animation time to show a new candidate view and dismiss the old one. + */ + private static int ANIMATION_TIME = 200; + + /** + * Listener used to notify IME that user clicks a candidate, or navigate + * between them. + */ + private CandidateViewListener mCvListener; + + /** + * The left arrow button used to show previous page. + */ + private ImageButton mLeftArrowBtn; + + /** + * The right arrow button used to show next page. + */ + private ImageButton mRightArrowBtn; + + /** + * Decoding result to show. + */ + private DecodingInfo mDecInfo; + + /** + * The animation view used to show candidates. It contains two views. + * Normally, the candidates are shown one of them. When user navigates to + * another page, animation effect will be performed. + */ + private ViewFlipper mFlipper; + + /** + * The x offset of the flipper in this container. + */ + private int xOffsetForFlipper; + + /** + * Animation used by the incoming view when the user navigates to a left + * page. + */ + private Animation mInAnimPushLeft; + + /** + * Animation used by the incoming view when the user navigates to a right + * page. + */ + private Animation mInAnimPushRight; + + /** + * Animation used by the incoming view when the user navigates to a page + * above. If the page navigation is triggered by DOWN key, this animation is + * used. + */ + private Animation mInAnimPushUp; + + /** + * Animation used by the incoming view when the user navigates to a page + * below. If the page navigation is triggered by UP key, this animation is + * used. + */ + private Animation mInAnimPushDown; + + /** + * Animation used by the outgoing view when the user navigates to a left + * page. + */ + private Animation mOutAnimPushLeft; + + /** + * Animation used by the outgoing view when the user navigates to a right + * page. + */ + private Animation mOutAnimPushRight; + + /** + * Animation used by the outgoing view when the user navigates to a page + * above. If the page navigation is triggered by DOWN key, this animation is + * used. + */ + private Animation mOutAnimPushUp; + + /** + * Animation used by the incoming view when the user navigates to a page + * below. If the page navigation is triggered by UP key, this animation is + * used. + */ + private Animation mOutAnimPushDown; + + /** + * Animation object which is used for the incoming view currently. + */ + private Animation mInAnimInUse; + + /** + * Animation object which is used for the outgoing view currently. + */ + private Animation mOutAnimInUse; + + /** + * Current page number in display. + */ + private int mCurrentPage = -1; + + public CandidatesContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void initialize(CandidateViewListener cvListener, + BalloonHint balloonHint, GestureDetector gestureDetector) { + mCvListener = cvListener; + + mLeftArrowBtn = (ImageButton) findViewById(R.id.arrow_left_btn); + mRightArrowBtn = (ImageButton) findViewById(R.id.arrow_right_btn); + mLeftArrowBtn.setOnTouchListener(this); + mRightArrowBtn.setOnTouchListener(this); + + mFlipper = (ViewFlipper) findViewById(R.id.candidate_flipper); + mFlipper.setMeasureAllChildren(true); + + invalidate(); + requestLayout(); + + for (int i = 0; i < mFlipper.getChildCount(); i++) { + CandidateView cv = (CandidateView) mFlipper.getChildAt(i); + cv.initialize(this, balloonHint, gestureDetector, mCvListener); + } + } + + public void showCandidates(PinyinIME.DecodingInfo decInfo, + boolean enableActiveHighlight) { + if (null == decInfo) return; + mDecInfo = decInfo; + mCurrentPage = 0; + + if (decInfo.isCandidatesListEmpty()) { + showArrow(mLeftArrowBtn, false); + showArrow(mRightArrowBtn, false); + } else { + showArrow(mLeftArrowBtn, true); + showArrow(mRightArrowBtn, true); + } + + for (int i = 0; i < mFlipper.getChildCount(); i++) { + CandidateView cv = (CandidateView) mFlipper.getChildAt(i); + cv.setDecodingInfo(mDecInfo); + } + stopAnimation(); + + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + cv.showPage(mCurrentPage, 0, enableActiveHighlight); + + updateArrowStatus(); + invalidate(); + } + + public int getCurrentPage() { + return mCurrentPage; + } + + public void enableActiveHighlight(boolean enableActiveHighlight) { + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + cv.enableActiveHighlight(enableActiveHighlight); + invalidate(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Environment env = Environment.getInstance(); + int measuredWidth = env.getScreenWidth(); + int measuredHeight = getPaddingTop(); + measuredHeight += env.getHeightForCandidates(); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth, + MeasureSpec.EXACTLY); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(measuredHeight, + MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (null != mLeftArrowBtn) { + xOffsetForFlipper = mLeftArrowBtn.getMeasuredWidth(); + } + } + + public boolean activeCurseBackward() { + if (mFlipper.isFlipping() || null == mDecInfo) { + return false; + } + + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + + if (cv.activeCurseBackward()) { + cv.invalidate(); + return true; + } else { + return pageBackward(true, true); + } + } + + public boolean activeCurseForward() { + if (mFlipper.isFlipping() || null == mDecInfo) { + return false; + } + + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + + if (cv.activeCursorForward()) { + cv.invalidate(); + return true; + } else { + return pageForward(true, true); + } + } + + public boolean pageBackward(boolean animLeftRight, + boolean enableActiveHighlight) { + if (null == mDecInfo) return false; + + if (mFlipper.isFlipping() || 0 == mCurrentPage) return false; + + int child = mFlipper.getDisplayedChild(); + int childNext = (child + 1) % 2; + CandidateView cv = (CandidateView) mFlipper.getChildAt(child); + CandidateView cvNext = (CandidateView) mFlipper.getChildAt(childNext); + + mCurrentPage--; + int activeCandInPage = cv.getActiveCandiatePosInPage(); + if (animLeftRight) + activeCandInPage = mDecInfo.mPageStart.elementAt(mCurrentPage + 1) + - mDecInfo.mPageStart.elementAt(mCurrentPage) - 1; + + cvNext.showPage(mCurrentPage, activeCandInPage, enableActiveHighlight); + loadAnimation(animLeftRight, false); + startAnimation(); + + updateArrowStatus(); + return true; + } + + public boolean pageForward(boolean animLeftRight, + boolean enableActiveHighlight) { + if (null == mDecInfo) return false; + + if (mFlipper.isFlipping() || !mDecInfo.preparePage(mCurrentPage + 1)) { + return false; + } + + int child = mFlipper.getDisplayedChild(); + int childNext = (child + 1) % 2; + CandidateView cv = (CandidateView) mFlipper.getChildAt(child); + int activeCandInPage = cv.getActiveCandiatePosInPage(); + cv.enableActiveHighlight(enableActiveHighlight); + + CandidateView cvNext = (CandidateView) mFlipper.getChildAt(childNext); + mCurrentPage++; + if (animLeftRight) activeCandInPage = 0; + + cvNext.showPage(mCurrentPage, activeCandInPage, enableActiveHighlight); + loadAnimation(animLeftRight, true); + startAnimation(); + + updateArrowStatus(); + return true; + } + + public int getActiveCandiatePos() { + if (null == mDecInfo) return -1; + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + return cv.getActiveCandiatePosGlobal(); + } + + public void updateArrowStatus() { + if (mCurrentPage < 0) return; + boolean forwardEnabled = mDecInfo.pageForwardable(mCurrentPage); + boolean backwardEnabled = mDecInfo.pageBackwardable(mCurrentPage); + + if (backwardEnabled) { + enableArrow(mLeftArrowBtn, true); + } else { + enableArrow(mLeftArrowBtn, false); + } + if (forwardEnabled) { + enableArrow(mRightArrowBtn, true); + } else { + enableArrow(mRightArrowBtn, false); + } + } + + private void enableArrow(ImageButton arrowBtn, boolean enabled) { + arrowBtn.setEnabled(enabled); + if (enabled) + arrowBtn.setAlpha(ARROW_ALPHA_ENABLED); + else + arrowBtn.setAlpha(ARROW_ALPHA_DISABLED); + } + + private void showArrow(ImageButton arrowBtn, boolean show) { + if (show) + arrowBtn.setVisibility(View.VISIBLE); + else + arrowBtn.setVisibility(View.INVISIBLE); + } + + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (v == mLeftArrowBtn) { + mCvListener.onToRightGesture(); + } else if (v == mRightArrowBtn) { + mCvListener.onToLeftGesture(); + } + } else if (event.getAction() == MotionEvent.ACTION_UP) { + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + cv.enableActiveHighlight(true); + } + + return false; + } + + // The reason why we handle candiate view's touch events here is because + // that the view under the focused view may get touch events instead of the + // focused one. + @Override + public boolean onTouchEvent(MotionEvent event) { + event.offsetLocation(-xOffsetForFlipper, 0); + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + cv.onTouchEventReal(event); + return true; + } + + public void loadAnimation(boolean animLeftRight, boolean forward) { + if (animLeftRight) { + if (forward) { + if (null == mInAnimPushLeft) { + mInAnimPushLeft = createAnimation(1.0f, 0, 0, 0, 0, 1.0f, + ANIMATION_TIME); + mOutAnimPushLeft = createAnimation(0, -1.0f, 0, 0, 1.0f, 0, + ANIMATION_TIME); + } + mInAnimInUse = mInAnimPushLeft; + mOutAnimInUse = mOutAnimPushLeft; + } else { + if (null == mInAnimPushRight) { + mInAnimPushRight = createAnimation(-1.0f, 0, 0, 0, 0, 1.0f, + ANIMATION_TIME); + mOutAnimPushRight = createAnimation(0, 1.0f, 0, 0, 1.0f, 0, + ANIMATION_TIME); + } + mInAnimInUse = mInAnimPushRight; + mOutAnimInUse = mOutAnimPushRight; + } + } else { + if (forward) { + if (null == mInAnimPushUp) { + mInAnimPushUp = createAnimation(0, 0, 1.0f, 0, 0, 1.0f, + ANIMATION_TIME); + mOutAnimPushUp = createAnimation(0, 0, 0, -1.0f, 1.0f, 0, + ANIMATION_TIME); + } + mInAnimInUse = mInAnimPushUp; + mOutAnimInUse = mOutAnimPushUp; + } else { + if (null == mInAnimPushDown) { + mInAnimPushDown = createAnimation(0, 0, -1.0f, 0, 0, 1.0f, + ANIMATION_TIME); + mOutAnimPushDown = createAnimation(0, 0, 0, 1.0f, 1.0f, 0, + ANIMATION_TIME); + } + mInAnimInUse = mInAnimPushDown; + mOutAnimInUse = mOutAnimPushDown; + } + } + + mInAnimInUse.setAnimationListener(this); + + mFlipper.setInAnimation(mInAnimInUse); + mFlipper.setOutAnimation(mOutAnimInUse); + } + + private Animation createAnimation(float xFrom, float xTo, float yFrom, + float yTo, float alphaFrom, float alphaTo, long duration) { + AnimationSet animSet = new AnimationSet(getContext(), null); + Animation trans = new TranslateAnimation(Animation.RELATIVE_TO_SELF, + xFrom, Animation.RELATIVE_TO_SELF, xTo, + Animation.RELATIVE_TO_SELF, yFrom, Animation.RELATIVE_TO_SELF, + yTo); + Animation alpha = new AlphaAnimation(alphaFrom, alphaTo); + animSet.addAnimation(trans); + animSet.addAnimation(alpha); + animSet.setDuration(duration); + return animSet; + } + + private void startAnimation() { + mFlipper.showNext(); + } + + private void stopAnimation() { + mFlipper.stopFlipping(); + } + + public void onAnimationEnd(Animation animation) { + if (!mLeftArrowBtn.isPressed() && !mRightArrowBtn.isPressed()) { + CandidateView cv = (CandidateView) mFlipper.getCurrentView(); + cv.enableActiveHighlight(true); + } + } + + public void onAnimationRepeat(Animation animation) { + } + + public void onAnimationStart(Animation animation) { + } +} diff --git a/src/com/android/inputmethod/pinyin/ComposingView.java b/src/com/android/inputmethod/pinyin/ComposingView.java new file mode 100644 index 0000000..f70af45 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/ComposingView.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.FontMetricsInt; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup.LayoutParams; + +/** + * View used to show composing string (The Pinyin string for the unselected + * syllables and the Chinese string for the selected syllables.) + */ +public class ComposingView extends View { + /** + *

+ * There are three statuses for the composing view. + *

+ * + *

+ * {@link #SHOW_PINYIN} is used to show the current Pinyin string without + * highlighted effect. When user inputs Pinyin characters one by one, the + * Pinyin string will be shown in this mode. + *

+ *

+ * {@link #SHOW_STRING_LOWERCASE} is used to show the Pinyin string in + * lowercase with highlighted effect. When user presses UP key and there is + * no fixed Chinese characters, composing view will switch from + * {@link #SHOW_PINYIN} to this mode, and in this mode, user can press + * confirm key to input the lower-case string, so that user can input + * English letter in Chinese mode. + *

+ *

+ * {@link #EDIT_PINYIN} is used to edit the Pinyin string (shown with + * highlighted effect). When current status is {@link #SHOW_PINYIN} and user + * presses UP key, if there are fixed Characters, the input method will + * switch to {@link #EDIT_PINYIN} thus user can modify some characters in + * the middle of the Pinyin string. If the current status is + * {@link #SHOW_STRING_LOWERCASE} and user presses LEFT and RIGHT key, it + * will also switch to {@link #EDIT_PINYIN}. + *

+ *

+ * Whenever user presses down key, the status switches to + * {@link #SHOW_PINYIN}. + *

+ *

+ * When composing view's status is {@link #SHOW_PINYIN}, the IME's status is + * {@link PinyinIME.ImeState#STATE_INPUT}, otherwise, the IME's status + * should be {@link PinyinIME.ImeState#STATE_COMPOSING}. + *

+ */ + public enum ComposingStatus { + SHOW_PINYIN, SHOW_STRING_LOWERCASE, EDIT_PINYIN, + } + + private static final int LEFT_RIGHT_MARGIN = 5; + + /** + * Used to draw composing string. When drawing the active and idle part of + * the spelling(Pinyin) string, the color may be changed. + */ + private Paint mPaint; + + /** + * Drawable used to draw highlight effect. + */ + private Drawable mHlDrawable; + + /** + * Drawable used to draw cursor for editing mode. + */ + private Drawable mCursor; + + /** + * Used to estimate dimensions to show the string . + */ + private FontMetricsInt mFmi; + + private int mStrColor; + private int mStrColorHl; + private int mStrColorIdle; + + private int mFontSize; + + private ComposingStatus mComposingStatus; + + PinyinIME.DecodingInfo mDecInfo; + + public ComposingView(Context context, AttributeSet attrs) { + super(context, attrs); + + Resources r = context.getResources(); + mHlDrawable = r.getDrawable(R.drawable.composing_hl_bg); + mCursor = r.getDrawable(R.drawable.composing_area_cursor); + + mStrColor = r.getColor(R.color.composing_color); + mStrColorHl = r.getColor(R.color.composing_color_hl); + mStrColorIdle = r.getColor(R.color.composing_color_idle); + + mFontSize = r.getDimensionPixelSize(R.dimen.composing_height); + + mPaint = new Paint(); + mPaint.setColor(mStrColor); + mPaint.setAntiAlias(true); + mPaint.setTextSize(mFontSize); + + mFmi = mPaint.getFontMetricsInt(); + } + + public void reset() { + mComposingStatus = ComposingStatus.SHOW_PINYIN; + } + + /** + * Set the composing string to show. If the IME status is + * {@link PinyinIME.ImeState#STATE_INPUT}, the composing view's status will + * be set to {@link ComposingStatus#SHOW_PINYIN}, otherwise the composing + * view will set its status to {@link ComposingStatus#SHOW_STRING_LOWERCASE} + * or {@link ComposingStatus#EDIT_PINYIN} automatically. + */ + public void setDecodingInfo(PinyinIME.DecodingInfo decInfo, + PinyinIME.ImeState imeStatus) { + mDecInfo = decInfo; + + if (PinyinIME.ImeState.STATE_INPUT == imeStatus) { + mComposingStatus = ComposingStatus.SHOW_PINYIN; + mDecInfo.moveCursorToEdge(false); + } else { + if (decInfo.getFixedLen() != 0 + || ComposingStatus.EDIT_PINYIN == mComposingStatus) { + mComposingStatus = ComposingStatus.EDIT_PINYIN; + } else { + mComposingStatus = ComposingStatus.SHOW_STRING_LOWERCASE; + } + mDecInfo.moveCursor(0); + } + + measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + requestLayout(); + invalidate(); + } + + public boolean moveCursor(int keyCode) { + if (keyCode != KeyEvent.KEYCODE_DPAD_LEFT + && keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) return false; + + if (ComposingStatus.EDIT_PINYIN == mComposingStatus) { + int offset = 0; + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) + offset = -1; + else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) offset = 1; + mDecInfo.moveCursor(offset); + } else if (ComposingStatus.SHOW_STRING_LOWERCASE == mComposingStatus) { + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + mComposingStatus = ComposingStatus.EDIT_PINYIN; + + measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + requestLayout(); + } + + } + invalidate(); + return true; + } + + public ComposingStatus getComposingStatus() { + return mComposingStatus; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + float width; + int height; + height = mFmi.bottom - mFmi.top + mPaddingTop + mPaddingBottom; + + if (null == mDecInfo) { + width = 0; + } else { + width = mPaddingLeft + mPaddingRight + LEFT_RIGHT_MARGIN * 2; + + String str; + if (ComposingStatus.SHOW_STRING_LOWERCASE == mComposingStatus) { + str = mDecInfo.getOrigianlSplStr().toString(); + } else { + str = mDecInfo.getComposingStrForDisplay(); + } + width += mPaint.measureText(str, 0, str.length()); + } + setMeasuredDimension((int) (width + 0.5f), height); + } + + @Override + protected void onDraw(Canvas canvas) { + if (ComposingStatus.EDIT_PINYIN == mComposingStatus + || ComposingStatus.SHOW_PINYIN == mComposingStatus) { + drawForPinyin(canvas); + return; + } + + float x, y; + x = mPaddingLeft + LEFT_RIGHT_MARGIN; + y = -mFmi.top + mPaddingTop; + + mPaint.setColor(mStrColorHl); + mHlDrawable.setBounds(mPaddingLeft, mPaddingTop, getWidth() + - mPaddingRight, getHeight() - mPaddingBottom); + mHlDrawable.draw(canvas); + + String splStr = mDecInfo.getOrigianlSplStr().toString(); + canvas.drawText(splStr, 0, splStr.length(), x, y, mPaint); + } + + private void drawCursor(Canvas canvas, float x) { + mCursor.setBounds((int) x, mPaddingTop, (int) x + + mCursor.getIntrinsicWidth(), getHeight() - mPaddingBottom); + mCursor.draw(canvas); + } + + private void drawForPinyin(Canvas canvas) { + float x, y; + x = mPaddingLeft + LEFT_RIGHT_MARGIN; + y = -mFmi.top + mPaddingTop; + + mPaint.setColor(mStrColor); + + int cursorPos = mDecInfo.getCursorPosInCmpsDisplay(); + int cmpsPos = cursorPos; + String cmpsStr = mDecInfo.getComposingStrForDisplay(); + int activeCmpsLen = mDecInfo.getActiveCmpsDisplayLen(); + if (cursorPos > activeCmpsLen) cmpsPos = activeCmpsLen; + canvas.drawText(cmpsStr, 0, cmpsPos, x, y, mPaint); + x += mPaint.measureText(cmpsStr, 0, cmpsPos); + if (cursorPos <= activeCmpsLen) { + if (ComposingStatus.EDIT_PINYIN == mComposingStatus) { + drawCursor(canvas, x); + } + canvas.drawText(cmpsStr, cmpsPos, activeCmpsLen, x, y, mPaint); + } + + x += mPaint.measureText(cmpsStr, cmpsPos, activeCmpsLen); + + if (cmpsStr.length() > activeCmpsLen) { + mPaint.setColor(mStrColorIdle); + int oriPos = activeCmpsLen; + if (cursorPos > activeCmpsLen) { + if (cursorPos > cmpsStr.length()) cursorPos = cmpsStr.length(); + canvas.drawText(cmpsStr, oriPos, cursorPos, x, y, mPaint); + x += mPaint.measureText(cmpsStr, oriPos, cursorPos); + + if (ComposingStatus.EDIT_PINYIN == mComposingStatus) { + drawCursor(canvas, x); + } + + oriPos = cursorPos; + } + canvas.drawText(cmpsStr, oriPos, cmpsStr.length(), x, y, mPaint); + } + } +} diff --git a/src/com/android/inputmethod/pinyin/EnglishInputProcessor.java b/src/com/android/inputmethod/pinyin/EnglishInputProcessor.java new file mode 100644 index 0000000..6d61119 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/EnglishInputProcessor.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.view.KeyEvent; +import android.view.inputmethod.InputConnection; + +/** + * Class to handle English input. + */ +public class EnglishInputProcessor { + + private int mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN; + + public boolean processKey(InputConnection inputContext, KeyEvent event, + boolean upperCase, boolean realAction) { + if (null == inputContext || null == event) return false; + + int keyCode = event.getKeyCode(); + + CharSequence prefix = null; + prefix = inputContext.getTextBeforeCursor(2, 0); + + int keyChar; + keyChar = 0; + if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) { + keyChar = keyCode - KeyEvent.KEYCODE_A + 'a'; + if (upperCase) { + keyChar = keyChar + 'A' - 'a'; + } + } else if (keyCode >= KeyEvent.KEYCODE_0 + && keyCode <= KeyEvent.KEYCODE_9) + keyChar = keyCode - KeyEvent.KEYCODE_0 + '0'; + else if (keyCode == KeyEvent.KEYCODE_COMMA) + keyChar = ','; + else if (keyCode == KeyEvent.KEYCODE_PERIOD) + keyChar = '.'; + else if (keyCode == KeyEvent.KEYCODE_APOSTROPHE) + keyChar = '\''; + else if (keyCode == KeyEvent.KEYCODE_AT) + keyChar = '@'; + else if (keyCode == KeyEvent.KEYCODE_SLASH) keyChar = '/'; + + if (0 == keyChar) { + mLastKeyCode = keyCode; + + String insert = null; + if (KeyEvent.KEYCODE_DEL == keyCode) { + if (realAction) { + inputContext.deleteSurroundingText(1, 0); + } + } else if (KeyEvent.KEYCODE_ENTER == keyCode) { + insert = "\n"; + } else if (KeyEvent.KEYCODE_SPACE == keyCode) { + insert = " "; + } else { + return false; + } + + if (null != insert && realAction) + inputContext.commitText(insert, insert.length()); + + return true; + } + + if (!realAction) + return true; + + if (KeyEvent.KEYCODE_SHIFT_LEFT == mLastKeyCode + || KeyEvent.KEYCODE_SHIFT_LEFT == mLastKeyCode) { + if (keyChar >= 'a' && keyChar <= 'z') + keyChar = keyChar - 'a' + 'A'; + } else if (KeyEvent.KEYCODE_ALT_LEFT == mLastKeyCode) { + } + + String result = String.valueOf((char) keyChar); + inputContext.commitText(result, result.length()); + mLastKeyCode = keyCode; + return true; + } +} diff --git a/src/com/android/inputmethod/pinyin/Environment.java b/src/com/android/inputmethod/pinyin/Environment.java new file mode 100644 index 0000000..8869294 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/Environment.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.content.Context; +import android.content.res.Configuration; +import android.view.Display; +import android.view.WindowManager; + +/** + * Global environment configurations for showing soft keyboard and candidate + * view. All original dimension values are defined in float, and the real size + * is calculated from the float values of and screen size. In this way, this + * input method can work even when screen size is changed. + */ +public class Environment { + /** + * The key height for portrait mode. It is relative to the screen height. + */ + private static final float KEY_HEIGHT_RATIO_PORTRAIT = 0.105f; + + /** + * The key height for landscape mode. It is relative to the screen height. + */ + private static final float KEY_HEIGHT_RATIO_LANDSCAPE = 0.147f; + + /** + * The height of the candidates area for portrait mode. It is relative to + * screen height. + */ + private static final float CANDIDATES_AREA_HEIGHT_RATIO_PORTRAIT = 0.084f; + + /** + * The height of the candidates area for portrait mode. It is relative to + * screen height. + */ + private static final float CANDIDATES_AREA_HEIGHT_RATIO_LANDSCAPE = 0.125f; + + /** + * How much should the balloon width be larger than width of the real key. + * It is relative to the smaller one of screen width and height. + */ + private static final float KEY_BALLOON_WIDTH_PLUS_RATIO = 0.08f; + + /** + * How much should the balloon height be larger than that of the real key. + * It is relative to the smaller one of screen width and height. + */ + private static final float KEY_BALLOON_HEIGHT_PLUS_RATIO = 0.07f; + + /** + * The text size for normal keys. It is relative to the smaller one of + * screen width and height. + */ + private static final float NORMAL_KEY_TEXT_SIZE_RATIO = 0.075f; + + /** + * The text size for function keys. It is relative to the smaller one of + * screen width and height. + */ + private static final float FUNCTION_KEY_TEXT_SIZE_RATIO = 0.055f; + + /** + * The text size balloons of normal keys. It is relative to the smaller one + * of screen width and height. + */ + private static final float NORMAL_BALLOON_TEXT_SIZE_RATIO = 0.14f; + + /** + * The text size balloons of function keys. It is relative to the smaller + * one of screen width and height. + */ + private static final float FUNCTION_BALLOON_TEXT_SIZE_RATIO = 0.085f; + + /** + * The configurations are managed in a singleton. + */ + private static Environment mInstance; + + private int mScreenWidth; + private int mScreenHeight; + private int mKeyHeight; + private int mCandidatesAreaHeight; + private int mKeyBalloonWidthPlus; + private int mKeyBalloonHeightPlus; + private int mNormalKeyTextSize; + private int mFunctionKeyTextSize; + private int mNormalBalloonTextSize; + private int mFunctionBalloonTextSize; + private Configuration mConfig = new Configuration(); + private boolean mDebug = false; + + private Environment() { + } + + public static Environment getInstance() { + if (null == mInstance) { + mInstance = new Environment(); + } + return mInstance; + } + + public void onConfigurationChanged(Configuration newConfig, Context context) { + if (mConfig.orientation != newConfig.orientation) { + WindowManager wm = (WindowManager) context + .getSystemService(Context.WINDOW_SERVICE); + Display d = wm.getDefaultDisplay(); + mScreenWidth = d.getWidth(); + mScreenHeight = d.getHeight(); + + int scale; + if (mScreenHeight > mScreenWidth) { + mKeyHeight = (int) (mScreenHeight * KEY_HEIGHT_RATIO_PORTRAIT); + mCandidatesAreaHeight = (int) (mScreenHeight * CANDIDATES_AREA_HEIGHT_RATIO_PORTRAIT); + scale = mScreenWidth; + } else { + mKeyHeight = (int) (mScreenHeight * KEY_HEIGHT_RATIO_LANDSCAPE); + mCandidatesAreaHeight = (int) (mScreenHeight * CANDIDATES_AREA_HEIGHT_RATIO_LANDSCAPE); + scale = mScreenHeight; + } + mNormalKeyTextSize = (int) (scale * NORMAL_KEY_TEXT_SIZE_RATIO); + mFunctionKeyTextSize = (int) (scale * FUNCTION_KEY_TEXT_SIZE_RATIO); + mNormalBalloonTextSize = (int) (scale * NORMAL_BALLOON_TEXT_SIZE_RATIO); + mFunctionBalloonTextSize = (int) (scale * FUNCTION_BALLOON_TEXT_SIZE_RATIO); + mKeyBalloonWidthPlus = (int) (scale * KEY_BALLOON_WIDTH_PLUS_RATIO); + mKeyBalloonHeightPlus = (int) (scale * KEY_BALLOON_HEIGHT_PLUS_RATIO); + } + + mConfig.updateFrom(newConfig); + } + + public Configuration getConfiguration() { + return mConfig; + } + + public int getScreenWidth() { + return mScreenWidth; + } + + public int getScreenHeight() { + return mScreenHeight; + } + + public int getHeightForCandidates() { + return mCandidatesAreaHeight; + } + + public float getKeyXMarginFactor() { + return 1.0f; + } + + public float getKeyYMarginFactor() { + if (Configuration.ORIENTATION_LANDSCAPE == mConfig.orientation) { + return 0.7f; + } + return 1.0f; + } + + public int getKeyHeight() { + return mKeyHeight; + } + + public int getKeyBalloonWidthPlus() { + return mKeyBalloonWidthPlus; + } + + public int getKeyBalloonHeightPlus() { + return mKeyBalloonHeightPlus; + } + + public int getSkbHeight() { + if (Configuration.ORIENTATION_PORTRAIT == mConfig.orientation) { + return mKeyHeight * 4; + } else if (Configuration.ORIENTATION_LANDSCAPE == mConfig.orientation) { + return mKeyHeight * 4; + } + return 0; + } + + public int getKeyTextSize(boolean isFunctionKey) { + if (isFunctionKey) { + return mFunctionKeyTextSize; + } else { + return mNormalKeyTextSize; + } + } + + public int getBalloonTextSize(boolean isFunctionKey) { + if (isFunctionKey) { + return mFunctionBalloonTextSize; + } else { + return mNormalBalloonTextSize; + } + } + + public boolean hasHardKeyboard() { + if (mConfig.keyboard == Configuration.KEYBOARD_NOKEYS + || mConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { + return false; + } + return true; + } + + public boolean needDebug() { + return mDebug; + } +} diff --git a/src/com/android/inputmethod/pinyin/InputModeSwitcher.java b/src/com/android/inputmethod/pinyin/InputModeSwitcher.java new file mode 100644 index 0000000..7167182 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/InputModeSwitcher.java @@ -0,0 +1,825 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow; + +import android.content.res.Resources; +import android.view.inputmethod.EditorInfo; + +/** + * Switcher used to switching input mode between Chinese, English, symbol,etc. + */ +public class InputModeSwitcher { + /** + * User defined key code, used by soft keyboard. + */ + private static final int USERDEF_KEYCODE_SHIFT_1 = -1; + + /** + * User defined key code, used by soft keyboard. + */ + private static final int USERDEF_KEYCODE_LANG_2 = -2; + + /** + * User defined key code, used by soft keyboard. + */ + private static final int USERDEF_KEYCODE_SYM_3 = -3; + + /** + * User defined key code, used by soft keyboard. + */ + public static final int USERDEF_KEYCODE_PHONE_SYM_4 = -4; + + /** + * User defined key code, used by soft keyboard. + */ + private static final int USERDEF_KEYCODE_MORE_SYM_5 = -5; + + /** + * User defined key code, used by soft keyboard. + */ + private static final int USERDEF_KEYCODE_SMILEY_6 = -6; + + + /** + * Bits used to indicate soft keyboard layout. If none bit is set, the + * current input mode does not require a soft keyboard. + **/ + private static final int MASK_SKB_LAYOUT = 0xf0000000; + + /** + * A kind of soft keyboard layout. An input mode should be anded with + * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. + */ + private static final int MASK_SKB_LAYOUT_QWERTY = 0x10000000; + + /** + * A kind of soft keyboard layout. An input mode should be anded with + * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. + */ + private static final int MASK_SKB_LAYOUT_SYMBOL1 = 0x20000000; + + /** + * A kind of soft keyboard layout. An input mode should be anded with + * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. + */ + private static final int MASK_SKB_LAYOUT_SYMBOL2 = 0x30000000; + + /** + * A kind of soft keyboard layout. An input mode should be anded with + * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. + */ + private static final int MASK_SKB_LAYOUT_SMILEY = 0x40000000; + + /** + * A kind of soft keyboard layout. An input mode should be anded with + * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout. + */ + private static final int MASK_SKB_LAYOUT_PHONE = 0x50000000; + + /** + * Used to indicate which language the current input mode is in. If the + * current input mode works with a none-QWERTY soft keyboard, these bits are + * also used to get language information. For example, a Chinese symbol soft + * keyboard and an English one are different in an icon which is used to + * tell user the language information. BTW, the smiley soft keyboard mode + * should be set with {@link #MASK_LANGUAGE_CN} because it can only be + * launched from Chinese QWERTY soft keyboard, and it has Chinese icon on + * soft keyboard. + */ + private static final int MASK_LANGUAGE = 0x0f000000; + + /** + * Used to indicate the current language. An input mode should be anded with + * {@link #MASK_LANGUAGE} to get this information. + */ + private static final int MASK_LANGUAGE_CN = 0x01000000; + + /** + * Used to indicate the current language. An input mode should be anded with + * {@link #MASK_LANGUAGE} to get this information. + */ + private static final int MASK_LANGUAGE_EN = 0x02000000; + + /** + * Used to indicate which case the current input mode is in. For example, + * English QWERTY has lowercase and uppercase. For the Chinese QWERTY, these + * bits are ignored. For phone keyboard layout, these bits can be + * {@link #MASK_CASE_UPPER} to request symbol page for phone soft keyboard. + */ + private static final int MASK_CASE = 0x00f00000; + + /** + * Used to indicate the current case information. An input mode should be + * anded with {@link #MASK_CASE} to get this information. + */ + private static final int MASK_CASE_LOWER = 0x00100000; + + /** + * Used to indicate the current case information. An input mode should be + * anded with {@link #MASK_CASE} to get this information. + */ + private static final int MASK_CASE_UPPER = 0x00200000; + + /** + * Mode for inputing Chinese with soft keyboard. + */ + public static final int MODE_SKB_CHINESE = (MASK_SKB_LAYOUT_QWERTY | MASK_LANGUAGE_CN); + + /** + * Mode for inputing basic symbols for Chinese mode with soft keyboard. + */ + public static final int MODE_SKB_SYMBOL1_CN = (MASK_SKB_LAYOUT_SYMBOL1 | MASK_LANGUAGE_CN); + + /** + * Mode for inputing more symbols for Chinese mode with soft keyboard. + */ + public static final int MODE_SKB_SYMBOL2_CN = (MASK_SKB_LAYOUT_SYMBOL2 | MASK_LANGUAGE_CN); + + /** + * Mode for inputing English lower characters with soft keyboard. + */ + public static final int MODE_SKB_ENGLISH_LOWER = (MASK_SKB_LAYOUT_QWERTY + | MASK_LANGUAGE_EN | MASK_CASE_LOWER); + + /** + * Mode for inputing English upper characters with soft keyboard. + */ + public static final int MODE_SKB_ENGLISH_UPPER = (MASK_SKB_LAYOUT_QWERTY + | MASK_LANGUAGE_EN | MASK_CASE_UPPER); + + /** + * Mode for inputing basic symbols for English mode with soft keyboard. + */ + public static final int MODE_SKB_SYMBOL1_EN = (MASK_SKB_LAYOUT_SYMBOL1 | MASK_LANGUAGE_EN); + + /** + * Mode for inputing more symbols for English mode with soft keyboard. + */ + public static final int MODE_SKB_SYMBOL2_EN = (MASK_SKB_LAYOUT_SYMBOL2 | MASK_LANGUAGE_EN); + + /** + * Mode for inputing smileys with soft keyboard. + */ + public static final int MODE_SKB_SMILEY = (MASK_SKB_LAYOUT_SMILEY | MASK_LANGUAGE_CN); + + /** + * Mode for inputing phone numbers. + */ + public static final int MODE_SKB_PHONE_NUM = (MASK_SKB_LAYOUT_PHONE); + + /** + * Mode for inputing phone numbers. + */ + public static final int MODE_SKB_PHONE_SYM = (MASK_SKB_LAYOUT_PHONE | MASK_CASE_UPPER); + + /** + * Mode for inputing Chinese with a hardware keyboard. + */ + public static final int MODE_HKB_CHINESE = (MASK_LANGUAGE_CN); + + /** + * Mode for inputing English with a hardware keyboard + */ + public static final int MODE_HKB_ENGLISH = (MASK_LANGUAGE_EN); + + /** + * Unset mode. + */ + public static final int MODE_UNSET = 0; + + /** + * Maximum toggle states for a soft keyboard. + */ + public static final int MAX_TOGGLE_STATES = 4; + + /** + * The input mode for the current edit box. + */ + private int mInputMode = MODE_UNSET; + + /** + * Used to remember previous input mode. When user enters an edit field, the + * previous input mode will be tried. If the previous mode can not be used + * for the current situation (For example, previous mode is a soft keyboard + * mode to input symbols, and we have a hardware keyboard for the current + * situation), {@link #mRecentLauageInputMode} will be tried. + **/ + private int mPreviousInputMode = MODE_SKB_CHINESE; + + /** + * Used to remember recent mode to input language. + */ + private int mRecentLauageInputMode = MODE_SKB_CHINESE; + + /** + * Editor information of the current edit box. + */ + private EditorInfo mEditorInfo; + + /** + * Used to indicate required toggling operations. + */ + private ToggleStates mToggleStates = new ToggleStates(); + + /** + * The current field is a short message field? + */ + private boolean mShortMessageField; + + /** + * Is return key in normal state? + */ + private boolean mEnterKeyNormal = true; + + /** + * Current icon. 0 for none icon. + */ + int mInputIcon = R.drawable.ime_pinyin; + + /** + * IME service. + */ + private PinyinIME mImeService; + + /** + * Key toggling state for Chinese mode. + */ + private int mToggleStateCn; + + /** + * Key toggling state for Chinese mode with candidates. + */ + private int mToggleStateCnCand; + + /** + * Key toggling state for English lowwercase mode. + */ + private int mToggleStateEnLower; + + /** + * Key toggling state for English upppercase mode. + */ + private int mToggleStateEnUpper; + + /** + * Key toggling state for English symbol mode for the first page. + */ + private int mToggleStateEnSym1; + + /** + * Key toggling state for English symbol mode for the second page. + */ + private int mToggleStateEnSym2; + + /** + * Key toggling state for smiley mode. + */ + private int mToggleStateSmiley; + + /** + * Key toggling state for phone symbol mode. + */ + private int mToggleStatePhoneSym; + + /** + * Key toggling state for GO action of ENTER key. + */ + private int mToggleStateGo; + + /** + * Key toggling state for SEARCH action of ENTER key. + */ + private int mToggleStateSearch; + + /** + * Key toggling state for SEND action of ENTER key. + */ + private int mToggleStateSend; + + /** + * Key toggling state for NEXT action of ENTER key. + */ + private int mToggleStateNext; + + /** + * Key toggling state for SEND action of ENTER key. + */ + private int mToggleStateDone; + + /** + * QWERTY row toggling state for Chinese input. + */ + private int mToggleRowCn; + + /** + * QWERTY row toggling state for English input. + */ + private int mToggleRowEn; + + /** + * QWERTY row toggling state for URI input. + */ + private int mToggleRowUri; + + /** + * QWERTY row toggling state for email address input. + */ + private int mToggleRowEmailAddress; + + class ToggleStates { + /** + * If it is true, this soft keyboard is a QWERTY one. + */ + boolean mQwerty; + + /** + * If {@link #mQwerty} is true, this variable is used to decide the + * letter case of the QWERTY keyboard. + */ + boolean mQwertyUpperCase; + + /** + * The id of enabled row in the soft keyboard. Refer to + * {@link com.android.inputmethod.pinyin.SoftKeyboard.KeyRow} for + * details. + */ + public int mRowIdToEnable; + + /** + * Used to store all other toggle states for the current input mode. + */ + public int mKeyStates[] = new int[MAX_TOGGLE_STATES]; + + /** + * Number of states to toggle. + */ + public int mKeyStatesNum; + } + + public InputModeSwitcher(PinyinIME imeService) { + mImeService = imeService; + Resources r = mImeService.getResources(); + mToggleStateCn = Integer.parseInt(r.getString(R.string.toggle_cn)); + mToggleStateCnCand = Integer.parseInt(r + .getString(R.string.toggle_cn_cand)); + mToggleStateEnLower = Integer.parseInt(r + .getString(R.string.toggle_en_lower)); + mToggleStateEnUpper = Integer.parseInt(r + .getString(R.string.toggle_en_upper)); + mToggleStateEnSym1 = Integer.parseInt(r + .getString(R.string.toggle_en_sym1)); + mToggleStateEnSym2 = Integer.parseInt(r + .getString(R.string.toggle_en_sym2)); + mToggleStateSmiley = Integer.parseInt(r + .getString(R.string.toggle_smiley)); + mToggleStatePhoneSym = Integer.parseInt(r + .getString(R.string.toggle_phone_sym)); + + mToggleStateGo = Integer + .parseInt(r.getString(R.string.toggle_enter_go)); + mToggleStateSearch = Integer.parseInt(r + .getString(R.string.toggle_enter_search)); + mToggleStateSend = Integer.parseInt(r + .getString(R.string.toggle_enter_send)); + mToggleStateNext = Integer.parseInt(r + .getString(R.string.toggle_enter_next)); + mToggleStateDone = Integer.parseInt(r + .getString(R.string.toggle_enter_done)); + + mToggleRowCn = Integer.parseInt(r.getString(R.string.toggle_row_cn)); + mToggleRowEn = Integer.parseInt(r.getString(R.string.toggle_row_en)); + mToggleRowUri = Integer.parseInt(r.getString(R.string.toggle_row_uri)); + mToggleRowEmailAddress = Integer.parseInt(r + .getString(R.string.toggle_row_emailaddress)); + } + + public int getInputMode() { + return mInputMode; + } + + public ToggleStates getToggleStates() { + return mToggleStates; + } + + public int getSkbLayout() { + int layout = (mInputMode & MASK_SKB_LAYOUT); + + switch (layout) { + case MASK_SKB_LAYOUT_QWERTY: + return R.xml.skb_qwerty; + case MASK_SKB_LAYOUT_SYMBOL1: + return R.xml.skb_sym1; + case MASK_SKB_LAYOUT_SYMBOL2: + return R.xml.skb_sym2; + case MASK_SKB_LAYOUT_SMILEY: + return R.xml.skb_smiley; + case MASK_SKB_LAYOUT_PHONE: + return R.xml.skb_phone; + } + return 0; + } + + // Return the icon to update. + public int switchLanguageWithHkb() { + int newInputMode = MODE_HKB_CHINESE; + mInputIcon = R.drawable.ime_pinyin; + + if (MODE_HKB_CHINESE == mInputMode) { + newInputMode = MODE_HKB_ENGLISH; + mInputIcon = R.drawable.ime_en; + } + + saveInputMode(newInputMode); + return mInputIcon; + } + + // Return the icon to update. + public int switchModeForUserKey(int userKey) { + int newInputMode = MODE_UNSET; + + if (USERDEF_KEYCODE_LANG_2 == userKey) { + if (MODE_SKB_CHINESE == mInputMode) { + newInputMode = MODE_SKB_ENGLISH_LOWER; + } else if (MODE_SKB_ENGLISH_LOWER == mInputMode + || MODE_SKB_ENGLISH_UPPER == mInputMode) { + newInputMode = MODE_SKB_CHINESE; + } else if (MODE_SKB_SYMBOL1_CN == mInputMode) { + newInputMode = MODE_SKB_SYMBOL1_EN; + } else if (MODE_SKB_SYMBOL1_EN == mInputMode) { + newInputMode = MODE_SKB_SYMBOL1_CN; + } else if (MODE_SKB_SYMBOL2_CN == mInputMode) { + newInputMode = MODE_SKB_SYMBOL2_EN; + } else if (MODE_SKB_SYMBOL2_EN == mInputMode) { + newInputMode = MODE_SKB_SYMBOL2_CN; + } else if (MODE_SKB_SMILEY == mInputMode) { + newInputMode = MODE_SKB_CHINESE; + } + } else if (USERDEF_KEYCODE_SYM_3 == userKey) { + if (MODE_SKB_CHINESE == mInputMode) { + newInputMode = MODE_SKB_SYMBOL1_CN; + } else if (MODE_SKB_ENGLISH_UPPER == mInputMode + || MODE_SKB_ENGLISH_LOWER == mInputMode) { + newInputMode = MODE_SKB_SYMBOL1_EN; + } else if (MODE_SKB_SYMBOL1_EN == mInputMode + || MODE_SKB_SYMBOL2_EN == mInputMode) { + newInputMode = MODE_SKB_ENGLISH_LOWER; + } else if (MODE_SKB_SYMBOL1_CN == mInputMode + || MODE_SKB_SYMBOL2_CN == mInputMode) { + newInputMode = MODE_SKB_CHINESE; + } else if (MODE_SKB_SMILEY == mInputMode) { + newInputMode = MODE_SKB_SYMBOL1_CN; + } + } else if (USERDEF_KEYCODE_SHIFT_1 == userKey) { + if (MODE_SKB_ENGLISH_LOWER == mInputMode) { + newInputMode = MODE_SKB_ENGLISH_UPPER; + } else if (MODE_SKB_ENGLISH_UPPER == mInputMode) { + newInputMode = MODE_SKB_ENGLISH_LOWER; + } + } else if (USERDEF_KEYCODE_MORE_SYM_5 == userKey) { + int sym = (MASK_SKB_LAYOUT & mInputMode); + if (MASK_SKB_LAYOUT_SYMBOL1 == sym) { + sym = MASK_SKB_LAYOUT_SYMBOL2; + } else { + sym = MASK_SKB_LAYOUT_SYMBOL1; + } + newInputMode = ((mInputMode & (~MASK_SKB_LAYOUT)) | sym); + } else if (USERDEF_KEYCODE_SMILEY_6 == userKey) { + if (MODE_SKB_CHINESE == mInputMode) { + newInputMode = MODE_SKB_SMILEY; + } else { + newInputMode = MODE_SKB_CHINESE; + } + } else if (USERDEF_KEYCODE_PHONE_SYM_4 == userKey) { + if (MODE_SKB_PHONE_NUM == mInputMode) { + newInputMode = MODE_SKB_PHONE_SYM; + } else { + newInputMode = MODE_SKB_PHONE_NUM; + } + } + + if (newInputMode == mInputMode || MODE_UNSET == newInputMode) { + return mInputIcon; + } + + saveInputMode(newInputMode); + prepareToggleStates(true); + return mInputIcon; + } + + // Return the icon to update. + public int requestInputWithHkb(EditorInfo editorInfo) { + mShortMessageField = false; + boolean english = false; + int newInputMode = MODE_HKB_CHINESE; + + switch (editorInfo.inputType & EditorInfo.TYPE_MASK_CLASS) { + case EditorInfo.TYPE_CLASS_NUMBER: + case EditorInfo.TYPE_CLASS_PHONE: + case EditorInfo.TYPE_CLASS_DATETIME: + english = true; + break; + case EditorInfo.TYPE_CLASS_TEXT: + int v = editorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION; + if (v == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + || v == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD + || v == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD + || v == EditorInfo.TYPE_TEXT_VARIATION_URI) { + english = true; + } else if (v == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { + mShortMessageField = true; + } + break; + default: + } + + if (english) { + // If the application request English mode, we switch to it. + newInputMode = MODE_HKB_ENGLISH; + } else { + // If the application do not request English mode, we will + // try to keep the previous mode to input language text. + // Because there is not soft keyboard, we need discard all + // soft keyboard related information from the previous language + // mode. + if ((mRecentLauageInputMode & MASK_LANGUAGE) == MASK_LANGUAGE_CN) { + newInputMode = MODE_HKB_CHINESE; + } else { + newInputMode = MODE_HKB_ENGLISH; + } + } + mEditorInfo = editorInfo; + saveInputMode(newInputMode); + prepareToggleStates(false); + return mInputIcon; + } + + // Return the icon to update. + public int requestInputWithSkb(EditorInfo editorInfo) { + mShortMessageField = false; + + int newInputMode = MODE_SKB_CHINESE; + + switch (editorInfo.inputType & EditorInfo.TYPE_MASK_CLASS) { + case EditorInfo.TYPE_CLASS_NUMBER: + case EditorInfo.TYPE_CLASS_DATETIME: + newInputMode = MODE_SKB_SYMBOL1_EN; + break; + case EditorInfo.TYPE_CLASS_PHONE: + newInputMode = MODE_SKB_PHONE_NUM; + break; + case EditorInfo.TYPE_CLASS_TEXT: + int v = editorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION; + if (v == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + || v == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD + || v == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD + || v == EditorInfo.TYPE_TEXT_VARIATION_URI) { + // If the application request English mode, we switch to it. + newInputMode = MODE_SKB_ENGLISH_LOWER; + } else { + if (v == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { + mShortMessageField = true; + } + // If the application do not request English mode, we will + // try to keep the previous mode. + int skbLayout = (mInputMode & MASK_SKB_LAYOUT); + newInputMode = mInputMode; + if (0 == skbLayout) { + if ((mInputMode & MASK_LANGUAGE) == MASK_LANGUAGE_CN) { + newInputMode = MODE_SKB_CHINESE; + } else { + newInputMode = MODE_SKB_ENGLISH_LOWER; + } + } + } + break; + default: + // Try to keep the previous mode. + int skbLayout = (mInputMode & MASK_SKB_LAYOUT); + newInputMode = mInputMode; + if (0 == skbLayout) { + if ((mInputMode & MASK_LANGUAGE) == MASK_LANGUAGE_CN) { + newInputMode = MODE_SKB_CHINESE; + } else { + newInputMode = MODE_SKB_ENGLISH_LOWER; + } + } + break; + } + + mEditorInfo = editorInfo; + saveInputMode(newInputMode); + prepareToggleStates(true); + return mInputIcon; + } + + // Return the icon to update. + public int requestBackToPreviousSkb() { + int layout = (mInputMode & MASK_SKB_LAYOUT); + int lastLayout = (mPreviousInputMode & MASK_SKB_LAYOUT); + if (0 != layout && 0 != lastLayout) { + mInputMode = mPreviousInputMode; + saveInputMode(mInputMode); + prepareToggleStates(true); + return mInputIcon; + } + return 0; + } + + public int getTooggleStateForCnCand() { + return mToggleStateCnCand; + } + + public boolean isEnglishWithHkb() { + return MODE_HKB_ENGLISH == mInputMode; + } + + public boolean isEnglishWithSkb() { + return MODE_SKB_ENGLISH_LOWER == mInputMode + || MODE_SKB_ENGLISH_UPPER == mInputMode; + } + + public boolean isEnglishUpperCaseWithSkb() { + return MODE_SKB_ENGLISH_UPPER == mInputMode; + } + + public boolean isChineseText() { + int skbLayout = (mInputMode & MASK_SKB_LAYOUT); + if (MASK_SKB_LAYOUT_QWERTY == skbLayout || 0 == skbLayout) { + int language = (mInputMode & MASK_LANGUAGE); + if (MASK_LANGUAGE_CN == language) return true; + } + return false; + } + + public boolean isChineseTextWithHkb() { + int skbLayout = (mInputMode & MASK_SKB_LAYOUT); + if (0 == skbLayout) { + int language = (mInputMode & MASK_LANGUAGE); + if (MASK_LANGUAGE_CN == language) return true; + } + return false; + } + + public boolean isChineseTextWithSkb() { + int skbLayout = (mInputMode & MASK_SKB_LAYOUT); + if (MASK_SKB_LAYOUT_QWERTY == skbLayout) { + int language = (mInputMode & MASK_LANGUAGE); + if (MASK_LANGUAGE_CN == language) return true; + } + return false; + } + + public boolean isSymbolWithSkb() { + int skbLayout = (mInputMode & MASK_SKB_LAYOUT); + if (MASK_SKB_LAYOUT_SYMBOL1 == skbLayout + || MASK_SKB_LAYOUT_SYMBOL2 == skbLayout) { + return true; + } + return false; + } + + public boolean isEnterNoramlState() { + return mEnterKeyNormal; + } + + public boolean tryHandleLongPressSwitch(int keyCode) { + if (USERDEF_KEYCODE_LANG_2 == keyCode + || USERDEF_KEYCODE_PHONE_SYM_4 == keyCode) { + mImeService.showOptionsMenu(); + return true; + } + return false; + } + + private void saveInputMode(int newInputMode) { + mPreviousInputMode = mInputMode; + mInputMode = newInputMode; + + int skbLayout = (mInputMode & MASK_SKB_LAYOUT); + if (MASK_SKB_LAYOUT_QWERTY == skbLayout || 0 == skbLayout) { + mRecentLauageInputMode = mInputMode; + } + + mInputIcon = R.drawable.ime_pinyin; + if (isEnglishWithHkb()) { + mInputIcon = R.drawable.ime_en; + } else if (isChineseTextWithHkb()) { + mInputIcon = R.drawable.ime_pinyin; + } + + if (!Environment.getInstance().hasHardKeyboard()) { + mInputIcon = 0; + } + } + + private void prepareToggleStates(boolean needSkb) { + mEnterKeyNormal = true; + if (!needSkb) return; + + mToggleStates.mQwerty = false; + mToggleStates.mKeyStatesNum = 0; + + int states[] = mToggleStates.mKeyStates; + int statesNum = 0; + // Toggle state for language. + int language = (mInputMode & MASK_LANGUAGE); + int layout = (mInputMode & MASK_SKB_LAYOUT); + int charcase = (mInputMode & MASK_CASE); + int variation = mEditorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION; + + if (MASK_SKB_LAYOUT_PHONE != layout) { + if (MASK_LANGUAGE_CN == language) { + // Chinese and Chinese symbol are always the default states, + // do not add a toggling operation. + if (MASK_SKB_LAYOUT_QWERTY == layout) { + mToggleStates.mQwerty = true; + mToggleStates.mQwertyUpperCase = true; + if (mShortMessageField) { + states[statesNum] = mToggleStateSmiley; + statesNum++; + } + } + } else if (MASK_LANGUAGE_EN == language) { + if (MASK_SKB_LAYOUT_QWERTY == layout) { + mToggleStates.mQwerty = true; + mToggleStates.mQwertyUpperCase = false; + states[statesNum] = mToggleStateEnLower; + if (MASK_CASE_UPPER == charcase) { + mToggleStates.mQwertyUpperCase = true; + states[statesNum] = mToggleStateEnUpper; + } + statesNum++; + } else if (MASK_SKB_LAYOUT_SYMBOL1 == layout) { + states[statesNum] = mToggleStateEnSym1; + statesNum++; + } else if (MASK_SKB_LAYOUT_SYMBOL2 == layout) { + states[statesNum] = mToggleStateEnSym2; + statesNum++; + } + } + + // Toggle rows for QWERTY. + mToggleStates.mRowIdToEnable = KeyRow.DEFAULT_ROW_ID; + if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) { + mToggleStates.mRowIdToEnable = mToggleRowEmailAddress; + } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) { + mToggleStates.mRowIdToEnable = mToggleRowUri; + } else if (MASK_LANGUAGE_CN == language) { + mToggleStates.mRowIdToEnable = mToggleRowCn; + } else if (MASK_LANGUAGE_EN == language) { + mToggleStates.mRowIdToEnable = mToggleRowEn; + } + } else { + if (MASK_CASE_UPPER == charcase) { + states[statesNum] = mToggleStatePhoneSym; + statesNum++; + } + } + + // Toggle state for enter key. + int action = mEditorInfo.imeOptions + & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); + + if (action == EditorInfo.IME_ACTION_GO) { + states[statesNum] = mToggleStateGo; + statesNum++; + mEnterKeyNormal = false; + } else if (action == EditorInfo.IME_ACTION_SEARCH) { + states[statesNum] = mToggleStateSearch; + statesNum++; + mEnterKeyNormal = false; + } else if (action == EditorInfo.IME_ACTION_SEND) { + states[statesNum] = mToggleStateSend; + statesNum++; + mEnterKeyNormal = false; + } else if (action == EditorInfo.IME_ACTION_NEXT) { + int f = mEditorInfo.inputType & EditorInfo.TYPE_MASK_FLAGS; + if (f != EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) { + states[statesNum] = mToggleStateNext; + statesNum++; + mEnterKeyNormal = false; + } + } else if (action == EditorInfo.IME_ACTION_DONE) { + states[statesNum] = mToggleStateDone; + statesNum++; + mEnterKeyNormal = false; + } + mToggleStates.mKeyStatesNum = statesNum; + } +} diff --git a/src/com/android/inputmethod/pinyin/KeyMapDream.java b/src/com/android/inputmethod/pinyin/KeyMapDream.java new file mode 100644 index 0000000..5a95c6f --- /dev/null +++ b/src/com/android/inputmethod/pinyin/KeyMapDream.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.view.KeyEvent; + +/** + * Class used to map the symbols on Dream's hardware keyboard to corresponding + * Chinese full-width symbols. + */ +public class KeyMapDream { + // Number of shift bits to store full-width symbols + private static final int SHIFT_FWCH = 8; + private static final int[] mKeyMap = { + KeyEvent.KEYCODE_UNKNOWN, + KeyEvent.KEYCODE_SOFT_LEFT, + KeyEvent.KEYCODE_SOFT_RIGHT, + KeyEvent.KEYCODE_HOME, + KeyEvent.KEYCODE_BACK, + KeyEvent.KEYCODE_CALL, + KeyEvent.KEYCODE_ENDCALL, + KeyEvent.KEYCODE_0 | ('\uff09' << SHIFT_FWCH), // ) + KeyEvent.KEYCODE_1 | ('\uff01' << SHIFT_FWCH), // ! + KeyEvent.KEYCODE_2 | ('\uff20' << SHIFT_FWCH), // @ + KeyEvent.KEYCODE_3 | ('\uff03' << SHIFT_FWCH), // # + KeyEvent.KEYCODE_4 | ('\uffe5' << SHIFT_FWCH), // $ - fullwidth Yuan + KeyEvent.KEYCODE_5 | ('\uff05' << SHIFT_FWCH), // % + KeyEvent.KEYCODE_6 | ('\u2026' << SHIFT_FWCH), // ^ - Apostrophe + KeyEvent.KEYCODE_7 | ('\uff06' << SHIFT_FWCH), // & + KeyEvent.KEYCODE_8 | ('\uff0a' << SHIFT_FWCH), // * + KeyEvent.KEYCODE_9 | ('\uff08' << SHIFT_FWCH), // ( + KeyEvent.KEYCODE_STAR, + KeyEvent.KEYCODE_POUND, + KeyEvent.KEYCODE_DPAD_UP, + KeyEvent.KEYCODE_DPAD_DOWN, + KeyEvent.KEYCODE_DPAD_LEFT, + KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.KEYCODE_DPAD_CENTER, + KeyEvent.KEYCODE_VOLUME_UP, + KeyEvent.KEYCODE_VOLUME_DOWN, + KeyEvent.KEYCODE_POWER, + KeyEvent.KEYCODE_CAMERA, + KeyEvent.KEYCODE_CLEAR, + KeyEvent.KEYCODE_A, + KeyEvent.KEYCODE_B | ('\uff3d' << SHIFT_FWCH), // ] + KeyEvent.KEYCODE_C | ('\u00a9' << SHIFT_FWCH), // copyright + KeyEvent.KEYCODE_D | ('\u3001' << SHIFT_FWCH), // \\ + KeyEvent.KEYCODE_E | ('_' << SHIFT_FWCH), // _ + KeyEvent.KEYCODE_F | ('\uff5b' << SHIFT_FWCH), // { + KeyEvent.KEYCODE_G | ('\uff5d' << SHIFT_FWCH), // } + KeyEvent.KEYCODE_H | ('\uff1a' << SHIFT_FWCH), // : + KeyEvent.KEYCODE_I | ('\uff0d' << SHIFT_FWCH), // - + KeyEvent.KEYCODE_J | ('\uff1b' << SHIFT_FWCH), // ; + KeyEvent.KEYCODE_K | ('\u201c' << SHIFT_FWCH), // " + KeyEvent.KEYCODE_L | ('\u2019' << SHIFT_FWCH), // ' + KeyEvent.KEYCODE_M | ('\u300b' << SHIFT_FWCH), // > - French quotes + KeyEvent.KEYCODE_N | ('\u300a' << SHIFT_FWCH), // < - French quotes + KeyEvent.KEYCODE_O | ('\uff0b' << SHIFT_FWCH), // + + KeyEvent.KEYCODE_P | ('\uff1d' << SHIFT_FWCH), // = + KeyEvent.KEYCODE_Q | ('\t' << SHIFT_FWCH), // \t + KeyEvent.KEYCODE_R | ('\u00ae' << SHIFT_FWCH), // trademark + KeyEvent.KEYCODE_S | ('\uff5c' << SHIFT_FWCH), // | + KeyEvent.KEYCODE_T | ('\u20ac' << SHIFT_FWCH), // + KeyEvent.KEYCODE_U | ('\u00d7' << SHIFT_FWCH), // multiplier + KeyEvent.KEYCODE_V | ('\uff3b' << SHIFT_FWCH), // [ + KeyEvent.KEYCODE_W | ('\uff40' << SHIFT_FWCH), // ` + KeyEvent.KEYCODE_X, KeyEvent.KEYCODE_Y | ('\u00f7' << SHIFT_FWCH), + KeyEvent.KEYCODE_Z, + KeyEvent.KEYCODE_COMMA | ('\uff1f' << SHIFT_FWCH), + KeyEvent.KEYCODE_PERIOD | ('\uff0f' << SHIFT_FWCH), + KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT, + KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT, + KeyEvent.KEYCODE_TAB, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_SYM, + KeyEvent.KEYCODE_EXPLORER, KeyEvent.KEYCODE_ENVELOPE, + KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_DEL, + KeyEvent.KEYCODE_GRAVE, KeyEvent.KEYCODE_MINUS, + KeyEvent.KEYCODE_EQUALS, KeyEvent.KEYCODE_LEFT_BRACKET, + KeyEvent.KEYCODE_RIGHT_BRACKET, KeyEvent.KEYCODE_BACKSLASH, + KeyEvent.KEYCODE_SEMICOLON, KeyEvent.KEYCODE_APOSTROPHE, + KeyEvent.KEYCODE_SLASH, + KeyEvent.KEYCODE_AT | ('\uff5e' << SHIFT_FWCH), + KeyEvent.KEYCODE_NUM, KeyEvent.KEYCODE_HEADSETHOOK, + KeyEvent.KEYCODE_FOCUS, KeyEvent.KEYCODE_PLUS, + KeyEvent.KEYCODE_MENU, KeyEvent.KEYCODE_NOTIFICATION, + KeyEvent.KEYCODE_SEARCH,}; + + static public char getChineseLabel(int keyCode) { + if (keyCode <= 0 || keyCode >= KeyEvent.getMaxKeyCode()) return 0; + assert ((mKeyMap[keyCode] & 0x000000ff) == keyCode); + return (char) (mKeyMap[keyCode] >> SHIFT_FWCH); + } +} diff --git a/src/com/android/inputmethod/pinyin/PinyinDecoderService.java b/src/com/android/inputmethod/pinyin/PinyinDecoderService.java new file mode 100644 index 0000000..a4a3ac4 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/PinyinDecoderService.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import com.android.inputmethod.pinyin.IPinyinDecoderService; + +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.Vector; + +import android.app.Service; +import android.content.Intent; +import android.content.res.AssetFileDescriptor; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +/** + * This class is used to separate the input method kernel in an individual + * service so that both IME and IME-syncer can use it. + */ +public class PinyinDecoderService extends Service { + native static boolean nativeImOpenDecoder(byte fn_sys_dict[], + byte fn_usr_dict[]); + + native static boolean nativeImOpenDecoderFd(FileDescriptor fd, + long startOffset, long length, byte fn_usr_dict[]); + + native static void nativeImSetMaxLens(int maxSpsLen, int maxHzsLen); + + native static boolean nativeImCloseDecoder(); + + native static int nativeImSearch(byte pyBuf[], int pyLen); + + native static int nativeImDelSearch(int pos, boolean is_pos_in_splid, + boolean clear_fixed_this_step); + + native static void nativeImResetSearch(); + + native static int nativeImAddLetter(byte ch); + + native static String nativeImGetPyStr(boolean decoded); + + native static int nativeImGetPyStrLen(boolean decoded); + + native static int[] nativeImGetSplStart(); + + native static String nativeImGetChoice(int choiceId); + + native static int nativeImChoose(int choiceId); + + native static int nativeImCancelLastChoice(); + + native static int nativeImGetFixedLen(); + + native static boolean nativeImCancelInput(); + + native static boolean nativeImFlushCache(); + + native static int nativeImGetPredictsNum(String fixedStr); + + native static String nativeImGetPredictItem(int predictNo); + + // Sync related + native static String nativeSyncUserDict(byte[] user_dict, String tomerge); + + native static boolean nativeSyncBegin(byte[] user_dict); + + native static boolean nativeSyncFinish(); + + native static String nativeSyncGetLemmas(); + + native static int nativeSyncPutLemmas(String tomerge); + + native static int nativeSyncGetLastCount(); + + native static int nativeSyncGetTotalCount(); + + native static boolean nativeSyncClearLastGot(); + + native static int nativeSyncGetCapacity(); + + private final static int MAX_PATH_FILE_LENGTH = 100; + private static boolean inited = false; + + private String mUsr_dict_file; + + static { + try { + System.loadLibrary("jni_pinyinime"); + } catch (UnsatisfiedLinkError ule) { + Log.e("PinyinDecoderService", + "WARNING: Could not load jni_pinyinime natives"); + } + } + + // Get file name of the specified dictionary + private boolean getUsrDictFileName(byte usr_dict[]) { + if (null == usr_dict) { + return false; + } + + for (int i = 0; i < mUsr_dict_file.length(); i++) + usr_dict[i] = (byte) mUsr_dict_file.charAt(i); + usr_dict[mUsr_dict_file.length()] = 0; + + return true; + } + + private void initPinyinEngine() { + byte usr_dict[]; + usr_dict = new byte[MAX_PATH_FILE_LENGTH]; + + // Here is how we open a built-in dictionary for access through + // a file descriptor... + AssetFileDescriptor afd = getResources().openRawResourceFd( + R.raw.dict_pinyin); + if (Environment.getInstance().needDebug()) { + Log + .i("foo", "Dict: start=" + afd.getStartOffset() + + ", length=" + afd.getLength() + ", fd=" + + afd.getParcelFileDescriptor()); + } + if (getUsrDictFileName(usr_dict)) { + inited = nativeImOpenDecoderFd(afd.getFileDescriptor(), afd + .getStartOffset(), afd.getLength(), usr_dict); + } + try { + afd.close(); + } catch (IOException e) { + } + } + + @Override + public void onCreate() { + super.onCreate(); + mUsr_dict_file = getFileStreamPath("usr_dict.dat").getPath(); + // This is a hack to make sure our "files" directory has been + // created. + try { + openFileOutput("dummy", 0).close(); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + + initPinyinEngine(); + } + + @Override + public void onDestroy() { + nativeImCloseDecoder(); + inited = false; + super.onDestroy(); + } + + private final IPinyinDecoderService.Stub mBinder = new IPinyinDecoderService.Stub() { + public int getInt() { + return 12345; + } + + public void setMaxLens(int maxSpsLen, int maxHzsLen) { + nativeImSetMaxLens(maxSpsLen, maxHzsLen); + } + + public int imSearch(byte[] pyBuf, int pyLen) { + return nativeImSearch(pyBuf, pyLen); + } + + public int imDelSearch(int pos, boolean is_pos_in_splid, + boolean clear_fixed_this_step) { + return nativeImDelSearch(pos, is_pos_in_splid, + clear_fixed_this_step); + } + + public void imResetSearch() { + nativeImResetSearch(); + } + + public int imAddLetter(byte ch) { + return nativeImAddLetter(ch); + } + + public String imGetPyStr(boolean decoded) { + return nativeImGetPyStr(decoded); + } + + public int imGetPyStrLen(boolean decoded) { + return nativeImGetPyStrLen(decoded); + } + + public int[] imGetSplStart() { + return nativeImGetSplStart(); + } + + public String imGetChoice(int choiceId) { + return nativeImGetChoice(choiceId); + } + + public String imGetChoices(int choicesNum) { + String retStr = null; + for (int i = 0; i < choicesNum; i++) { + if (null == retStr) + retStr = nativeImGetChoice(i); + else + retStr += " " + nativeImGetChoice(i); + } + return retStr; + } + + public List imGetChoiceList(int choicesStart, int choicesNum, + int sentFixedLen) { + Vector choiceList = new Vector(); + for (int i = choicesStart; i < choicesStart + choicesNum; i++) { + String retStr = nativeImGetChoice(i); + if (0 == i) retStr = retStr.substring(sentFixedLen); + choiceList.add(retStr); + } + return choiceList; + } + + public int imChoose(int choiceId) { + return nativeImChoose(choiceId); + } + + public int imCancelLastChoice() { + return nativeImCancelLastChoice(); + } + + public int imGetFixedLen() { + return nativeImGetFixedLen(); + } + + public boolean imCancelInput() { + return nativeImCancelInput(); + } + + public void imFlushCache() { + nativeImFlushCache(); + } + + public int imGetPredictsNum(String fixedStr) { + return nativeImGetPredictsNum(fixedStr); + } + + public String imGetPredictItem(int predictNo) { + return nativeImGetPredictItem(predictNo); + } + + public List imGetPredictList(int predictsStart, int predictsNum) { + Vector predictList = new Vector(); + for (int i = predictsStart; i < predictsStart + predictsNum; i++) { + predictList.add(nativeImGetPredictItem(i)); + } + return predictList; + } + + public String syncUserDict(String tomerge) { + byte usr_dict[]; + usr_dict = new byte[MAX_PATH_FILE_LENGTH]; + + if (getUsrDictFileName(usr_dict)) { + return nativeSyncUserDict(usr_dict, tomerge); + } + return null; + } + + public boolean syncBegin() { + byte usr_dict[]; + usr_dict = new byte[MAX_PATH_FILE_LENGTH]; + + if (getUsrDictFileName(usr_dict)) { + return nativeSyncBegin(usr_dict); + } + return false; + } + + public void syncFinish() { + nativeSyncFinish(); + } + + public int syncPutLemmas(String tomerge) { + return nativeSyncPutLemmas(tomerge); + } + + public String syncGetLemmas() { + return nativeSyncGetLemmas(); + } + + public int syncGetLastCount() { + return nativeSyncGetLastCount(); + } + + public int syncGetTotalCount() { + return nativeSyncGetTotalCount(); + } + + public void syncClearLastGot() { + nativeSyncClearLastGot(); + } + + public int imSyncGetCapacity() { + return nativeSyncGetCapacity(); + } + }; + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/src/com/android/inputmethod/pinyin/PinyinIME.java b/src/com/android/inputmethod/pinyin/PinyinIME.java new file mode 100644 index 0000000..9ac2c2d --- /dev/null +++ b/src/com/android/inputmethod/pinyin/PinyinIME.java @@ -0,0 +1,2134 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.res.Configuration; +import android.inputmethodservice.InputMethodService; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.Gravity; +import android.view.GestureDetector; +import android.view.LayoutInflater; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.View.MeasureSpec; +import android.view.ViewGroup.LayoutParams; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; + +/** + * Main class of the Pinyin input method. + */ +public class PinyinIME extends InputMethodService { + /** + * TAG for debug. + */ + static final String TAG = "PinyinIME"; + + /** + * If is is true, IME will simulate key events for delete key, and send the + * events back to the application. + */ + private static final boolean SIMULATE_KEY_DELETE = true; + + /** + * Necessary environment configurations like screen size for this IME. + */ + private Environment mEnvironment; + + /** + * Used to switch input mode. + */ + private InputModeSwitcher mInputModeSwitcher; + + /** + * Soft keyboard container view to host real soft keyboard view. + */ + private SkbContainer mSkbContainer; + + /** + * The floating container which contains the composing view. If necessary, + * some other view like candiates container can also be put here. + */ + private LinearLayout mFloatingContainer; + + /** + * View to show the composing string. + */ + private ComposingView mComposingView; + + /** + * Window to show the composing string. + */ + private PopupWindow mFloatingWindow; + + /** + * Used to show the floating window. + */ + private PopupTimer mFloatingWindowTimer = new PopupTimer(); + + /** + * View to show candidates list. + */ + private CandidatesContainer mCandidatesContainer; + + /** + * Balloon used when user presses a candidate. + */ + private BalloonHint mCandidatesBalloon; + + /** + * Used to notify the input method when the user touch a candidate. + */ + private ChoiceNotifier mChoiceNotifier; + + /** + * Used to notify gestures from soft keyboard. + */ + private OnGestureListener mGestureListenerSkb; + + /** + * Used to notify gestures from candidates view. + */ + private OnGestureListener mGestureListenerCandidates; + + /** + * The on-screen movement gesture detector for soft keyboard. + */ + private GestureDetector mGestureDetectorSkb; + + /** + * The on-screen movement gesture detector for candidates view. + */ + private GestureDetector mGestureDetectorCandidates; + + /** + * Option dialog to choose settings and other IMEs. + */ + private AlertDialog mOptionsDialog; + + /** + * Connection used to bind the decoding service. + */ + private PinyinDecoderServiceConnection mPinyinDecoderServiceConnection; + + /** + * The current IME status. + * + * @see com.android.inputmethod.pinyin.PinyinIME.ImeState + */ + private ImeState mImeState = ImeState.STATE_IDLE; + + /** + * The decoding information, include spelling(Pinyin) string, decoding + * result, etc. + */ + private DecodingInfo mDecInfo = new DecodingInfo(); + + /** + * For English input. + */ + private EnglishInputProcessor mImEn; + + // receive ringer mode changes + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + SoundManager.getInstance(context).updateRingerMode(); + } + }; + + @Override + public void onCreate() { + mEnvironment = Environment.getInstance(); + if (mEnvironment.needDebug()) { + Log.d(TAG, "onCreate."); + } + super.onCreate(); + + startPinyinDecoderService(); + mImEn = new EnglishInputProcessor(); + Settings.getInstance(PreferenceManager + .getDefaultSharedPreferences(getApplicationContext())); + + mInputModeSwitcher = new InputModeSwitcher(this); + mChoiceNotifier = new ChoiceNotifier(this); + mGestureListenerSkb = new OnGestureListener(false); + mGestureListenerCandidates = new OnGestureListener(true); + mGestureDetectorSkb = new GestureDetector(this, mGestureListenerSkb); + mGestureDetectorCandidates = new GestureDetector(this, + mGestureListenerCandidates); + + mEnvironment.onConfigurationChanged(getResources().getConfiguration(), + this); + } + + @Override + public void onDestroy() { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onDestroy."); + } + unbindService(mPinyinDecoderServiceConnection); + Settings.releaseInstance(); + super.onDestroy(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + Environment env = Environment.getInstance(); + if (mEnvironment.needDebug()) { + Log.d(TAG, "onConfigurationChanged"); + Log.d(TAG, "--last config: " + env.getConfiguration().toString()); + Log.d(TAG, "---new config: " + newConfig.toString()); + } + // We need to change the local environment first so that UI components + // can get the environment instance to handle size issues. When + // super.onConfigurationChanged() is called, onCreateCandidatesView() + // and onCreateInputView() will be executed if necessary. + env.onConfigurationChanged(newConfig, this); + + // Clear related UI of the previous configuration. + if (null != mSkbContainer) { + mSkbContainer.dismissPopups(); + } + if (null != mCandidatesBalloon) { + mCandidatesBalloon.dismiss(); + } + super.onConfigurationChanged(newConfig); + resetToIdleState(false); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (processKey(event, 0 != event.getRepeatCount())) return true; + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (processKey(event, true)) return true; + return super.onKeyUp(keyCode, event); + } + + private boolean processKey(KeyEvent event, boolean realAction) { + if (ImeState.STATE_BYPASS == mImeState) return false; + + int keyCode = event.getKeyCode(); + // SHIFT-SPACE is used to switch between Chinese and English + // when HKB is on. + if (KeyEvent.KEYCODE_SPACE == keyCode && event.isShiftPressed()) { + if (!realAction) return true; + + updateIcon(mInputModeSwitcher.switchLanguageWithHkb()); + resetToIdleState(false); + + int allMetaState = KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON + | KeyEvent.META_ALT_RIGHT_ON | KeyEvent.META_SHIFT_ON + | KeyEvent.META_SHIFT_LEFT_ON + | KeyEvent.META_SHIFT_RIGHT_ON | KeyEvent.META_SYM_ON; + getCurrentInputConnection().clearMetaKeyStates(allMetaState); + return true; + } + + // If HKB is on to input English, by-pass the key event so that + // default key listener will handle it. + if (mInputModeSwitcher.isEnglishWithHkb()) { + return false; + } + + if (processFunctionKeys(keyCode, realAction)) { + return true; + } + + int keyChar = 0; + if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) { + keyChar = keyCode - KeyEvent.KEYCODE_A + 'a'; + } else if (keyCode >= KeyEvent.KEYCODE_0 + && keyCode <= KeyEvent.KEYCODE_9) { + keyChar = keyCode - KeyEvent.KEYCODE_0 + '0'; + } else if (keyCode == KeyEvent.KEYCODE_COMMA) { + keyChar = ','; + } else if (keyCode == KeyEvent.KEYCODE_PERIOD) { + keyChar = '.'; + } else if (keyCode == KeyEvent.KEYCODE_SPACE) { + keyChar = ' '; + } else if (keyCode == KeyEvent.KEYCODE_APOSTROPHE) { + keyChar = '\''; + } + + if (mInputModeSwitcher.isEnglishWithSkb()) { + return mImEn.processKey(getCurrentInputConnection(), event, + mInputModeSwitcher.isEnglishUpperCaseWithSkb(), realAction); + } else if (mInputModeSwitcher.isChineseText()) { + if (mImeState == ImeState.STATE_IDLE || + mImeState == ImeState.STATE_APP_COMPLETION) { + mImeState = ImeState.STATE_IDLE; + return processStateIdle(keyChar, keyCode, event, realAction); + } else if (mImeState == ImeState.STATE_INPUT) { + return processStateInput(keyChar, keyCode, event, realAction); + } else if (mImeState == ImeState.STATE_PREDICT) { + return processStatePredict(keyChar, keyCode, event, realAction); + } else if (mImeState == ImeState.STATE_COMPOSING) { + return processStateEditComposing(keyChar, keyCode, event, + realAction); + } + } else { + if (0 != keyChar && realAction) { + commitResultText(String.valueOf((char) keyChar)); + } + } + + return false; + } + + // keyCode can be from both hard key or soft key. + private boolean processFunctionKeys(int keyCode, boolean realAction) { + // Back key is used to dismiss all popup UI in a soft keyboard. + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (isInputViewShown()) { + if (mSkbContainer.handleBack(realAction)) return true; + } + } + + // Chinese related input is handle separately. + if (mInputModeSwitcher.isChineseText()) { + return false; + } + + if (null != mCandidatesContainer && mCandidatesContainer.isShown() + && !mDecInfo.isCandidatesListEmpty()) { + if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { + if (!realAction) return true; + + chooseCandidate(-1); + return true; + } + + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { + if (!realAction) return true; + mCandidatesContainer.activeCurseBackward(); + return true; + } + + if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + if (!realAction) return true; + mCandidatesContainer.activeCurseForward(); + return true; + } + + if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { + if (!realAction) return true; + mCandidatesContainer.pageBackward(false, true); + return true; + } + + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + if (!realAction) return true; + mCandidatesContainer.pageForward(false, true); + return true; + } + + if (keyCode == KeyEvent.KEYCODE_DEL && + ImeState.STATE_PREDICT == mImeState) { + if (!realAction) return true; + resetToIdleState(false); + return true; + } + } else { + if (keyCode == KeyEvent.KEYCODE_DEL) { + if (!realAction) return true; + if (SIMULATE_KEY_DELETE) { + simulateKeyEventDownUp(keyCode); + } else { + getCurrentInputConnection().deleteSurroundingText(1, 0); + } + return true; + } + if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (!realAction) return true; + sendKeyChar('\n'); + return true; + } + if (keyCode == KeyEvent.KEYCODE_SPACE) { + if (!realAction) return true; + sendKeyChar(' '); + return true; + } + } + + return false; + } + + private boolean processStateIdle(int keyChar, int keyCode, KeyEvent event, + boolean realAction) { + // In this status, when user presses keys in [a..z], the status will + // change to input state. + if (keyChar >= 'a' && keyChar <= 'z' && !event.isAltPressed()) { + if (!realAction) return true; + mDecInfo.addSplChar((char) keyChar, true); + chooseAndUpdate(-1); + return true; + } else if (keyCode == KeyEvent.KEYCODE_DEL) { + if (!realAction) return true; + if (SIMULATE_KEY_DELETE) { + simulateKeyEventDownUp(keyCode); + } else { + getCurrentInputConnection().deleteSurroundingText(1, 0); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (!realAction) return true; + sendKeyChar('\n'); + return true; + } else if (keyCode == KeyEvent.KEYCODE_ALT_LEFT + || keyCode == KeyEvent.KEYCODE_ALT_RIGHT + || keyCode == KeyEvent.KEYCODE_SHIFT_LEFT + || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { + return true; + } else if (event.isAltPressed()) { + char fullwidth_char = KeyMapDream.getChineseLabel(keyCode); + if (0 != fullwidth_char) { + if (realAction) { + String result = String.valueOf(fullwidth_char); + commitResultText(result); + } + return true; + } else { + if (keyCode >= KeyEvent.KEYCODE_A + && keyCode <= KeyEvent.KEYCODE_Z) { + return true; + } + } + } else if (keyChar != 0 && keyChar != '\t') { + if (realAction) { + if (keyChar == ',' || keyChar == '.') { + inputCommaPeriod("", keyChar, false, ImeState.STATE_IDLE); + } else { + if (0 != keyChar) { + String result = String.valueOf((char) keyChar); + commitResultText(result); + } + } + } + return true; + } + return false; + } + + private boolean processStateInput(int keyChar, int keyCode, KeyEvent event, + boolean realAction) { + // If ALT key is pressed, input alternative key. But if the + // alternative key is quote key, it will be used for input a splitter + // in Pinyin string. + if (event.isAltPressed()) { + if ('\'' != event.getUnicodeChar(event.getMetaState())) { + if (realAction) { + char fullwidth_char = KeyMapDream.getChineseLabel(keyCode); + if (0 != fullwidth_char) { + commitResultText(mDecInfo + .getCurrentFullSent(mCandidatesContainer + .getActiveCandiatePos()) + + String.valueOf(fullwidth_char)); + resetToIdleState(false); + } + } + return true; + } else { + keyChar = '\''; + } + } + + if (keyChar >= 'a' && keyChar <= 'z' || keyChar == '\'' + && !mDecInfo.charBeforeCursorIsSeparator() + || keyCode == KeyEvent.KEYCODE_DEL) { + if (!realAction) return true; + return processSurfaceChange(keyChar, keyCode); + } else if (keyChar == ',' || keyChar == '.') { + if (!realAction) return true; + inputCommaPeriod(mDecInfo.getCurrentFullSent(mCandidatesContainer + .getActiveCandiatePos()), keyChar, true, + ImeState.STATE_IDLE); + return true; + } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN + || keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + if (!realAction) return true; + + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { + mCandidatesContainer.activeCurseBackward(); + } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + mCandidatesContainer.activeCurseForward(); + } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { + // If it has been the first page, a up key will shift + // the state to edit composing string. + if (!mCandidatesContainer.pageBackward(false, true)) { + mCandidatesContainer.enableActiveHighlight(false); + changeToStateComposing(true); + updateComposingText(true); + } + } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + mCandidatesContainer.pageForward(false, true); + } + return true; + } else if (keyCode >= KeyEvent.KEYCODE_1 + && keyCode <= KeyEvent.KEYCODE_9) { + if (!realAction) return true; + + int activePos = keyCode - KeyEvent.KEYCODE_1; + int currentPage = mCandidatesContainer.getCurrentPage(); + if (activePos < mDecInfo.getCurrentPageSize(currentPage)) { + activePos = activePos + + mDecInfo.getCurrentPageStart(currentPage); + if (activePos >= 0) { + chooseAndUpdate(activePos); + } + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (!realAction) return true; + if (mInputModeSwitcher.isEnterNoramlState()) { + commitResultText(mDecInfo.getOrigianlSplStr().toString()); + resetToIdleState(false); + } else { + commitResultText(mDecInfo + .getCurrentFullSent(mCandidatesContainer + .getActiveCandiatePos())); + sendKeyChar('\n'); + resetToIdleState(false); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER + || keyCode == KeyEvent.KEYCODE_SPACE) { + if (!realAction) return true; + chooseCandidate(-1); + return true; + } else if (keyCode == KeyEvent.KEYCODE_BACK) { + if (!realAction) return true; + resetToIdleState(false); + requestHideSelf(0); + return true; + } + return false; + } + + private boolean processStatePredict(int keyChar, int keyCode, + KeyEvent event, boolean realAction) { + if (!realAction) return true; + + // If ALT key is pressed, input alternative key. + if (event.isAltPressed()) { + char fullwidth_char = KeyMapDream.getChineseLabel(keyCode); + if (0 != fullwidth_char) { + commitResultText(mDecInfo.getCandidate(mCandidatesContainer + .getActiveCandiatePos()) + + String.valueOf(fullwidth_char)); + resetToIdleState(false); + } + return true; + } + + // In this status, when user presses keys in [a..z], the status will + // change to input state. + if (keyChar >= 'a' && keyChar <= 'z') { + changeToStateInput(true); + mDecInfo.addSplChar((char) keyChar, true); + chooseAndUpdate(-1); + } else if (keyChar == ',' || keyChar == '.') { + inputCommaPeriod("", keyChar, true, ImeState.STATE_IDLE); + } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN + || keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { + mCandidatesContainer.activeCurseBackward(); + } + if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + mCandidatesContainer.activeCurseForward(); + } + if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { + mCandidatesContainer.pageBackward(false, true); + } + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + mCandidatesContainer.pageForward(false, true); + } + } else if (keyCode == KeyEvent.KEYCODE_DEL) { + resetToIdleState(false); + } else if (keyCode == KeyEvent.KEYCODE_BACK) { + resetToIdleState(false); + requestHideSelf(0); + } else if (keyCode >= KeyEvent.KEYCODE_1 + && keyCode <= KeyEvent.KEYCODE_9) { + int activePos = keyCode - KeyEvent.KEYCODE_1; + int currentPage = mCandidatesContainer.getCurrentPage(); + if (activePos < mDecInfo.getCurrentPageSize(currentPage)) { + activePos = activePos + + mDecInfo.getCurrentPageStart(currentPage); + if (activePos >= 0) { + chooseAndUpdate(activePos); + } + } + } else if (keyCode == KeyEvent.KEYCODE_ENTER) { + sendKeyChar('\n'); + resetToIdleState(false); + } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER + || keyCode == KeyEvent.KEYCODE_SPACE) { + chooseCandidate(-1); + } + + return true; + } + + private boolean processStateEditComposing(int keyChar, int keyCode, + KeyEvent event, boolean realAction) { + if (!realAction) return true; + + ComposingView.ComposingStatus cmpsvStatus = + mComposingView.getComposingStatus(); + + // If ALT key is pressed, input alternative key. But if the + // alternative key is quote key, it will be used for input a splitter + // in Pinyin string. + if (event.isAltPressed()) { + if ('\'' != event.getUnicodeChar(event.getMetaState())) { + char fullwidth_char = KeyMapDream.getChineseLabel(keyCode); + if (0 != fullwidth_char) { + String retStr; + if (ComposingView.ComposingStatus.SHOW_STRING_LOWERCASE == + cmpsvStatus) { + retStr = mDecInfo.getOrigianlSplStr().toString(); + } else { + retStr = mDecInfo.getComposingStr(); + } + commitResultText(retStr + String.valueOf(fullwidth_char)); + resetToIdleState(false); + } + return true; + } else { + keyChar = '\''; + } + } + + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + if (!mDecInfo.selectionFinished()) { + changeToStateInput(true); + } + } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + mComposingView.moveCursor(keyCode); + } else if ((keyCode == KeyEvent.KEYCODE_ENTER && mInputModeSwitcher + .isEnterNoramlState()) + || keyCode == KeyEvent.KEYCODE_DPAD_CENTER + || keyCode == KeyEvent.KEYCODE_SPACE) { + if (ComposingView.ComposingStatus.SHOW_STRING_LOWERCASE == cmpsvStatus) { + String str = mDecInfo.getOrigianlSplStr().toString(); + if (!tryInputRawUnicode(str)) { + commitResultText(str); + } + } else if (ComposingView.ComposingStatus.EDIT_PINYIN == cmpsvStatus) { + String str = mDecInfo.getComposingStr(); + if (!tryInputRawUnicode(str)) { + commitResultText(str); + } + } else { + commitResultText(mDecInfo.getComposingStr()); + } + resetToIdleState(false); + } else if (keyCode == KeyEvent.KEYCODE_ENTER + && !mInputModeSwitcher.isEnterNoramlState()) { + String retStr; + if (!mDecInfo.isCandidatesListEmpty()) { + retStr = mDecInfo.getCurrentFullSent(mCandidatesContainer + .getActiveCandiatePos()); + } else { + retStr = mDecInfo.getComposingStr(); + } + commitResultText(retStr); + sendKeyChar('\n'); + resetToIdleState(false); + } else if (keyCode == KeyEvent.KEYCODE_BACK) { + resetToIdleState(false); + requestHideSelf(0); + return true; + } else { + return processSurfaceChange(keyChar, keyCode); + } + return true; + } + + private boolean tryInputRawUnicode(String str) { + if (str.length() > 7) { + if (str.substring(0, 7).compareTo("unicode") == 0) { + try { + String digitStr = str.substring(7); + int startPos = 0; + int radix = 10; + if (digitStr.length() > 2 && digitStr.charAt(0) == '0' + && digitStr.charAt(1) == 'x') { + startPos = 2; + radix = 16; + } + digitStr = digitStr.substring(startPos); + int unicode = Integer.parseInt(digitStr, radix); + if (unicode > 0) { + char low = (char) (unicode & 0x0000ffff); + char high = (char) ((unicode & 0xffff0000) >> 16); + commitResultText(String.valueOf(low)); + if (0 != high) { + commitResultText(String.valueOf(high)); + } + } + return true; + } catch (NumberFormatException e) { + return false; + } + } else if (str.substring(str.length() - 7, str.length()).compareTo( + "unicode") == 0) { + String resultStr = ""; + for (int pos = 0; pos < str.length() - 7; pos++) { + if (pos > 0) { + resultStr += " "; + } + + resultStr += "0x" + Integer.toHexString(str.charAt(pos)); + } + commitResultText(String.valueOf(resultStr)); + return true; + } + } + return false; + } + + private boolean processSurfaceChange(int keyChar, int keyCode) { + if (mDecInfo.isSplStrFull() && KeyEvent.KEYCODE_DEL != keyCode) { + return true; + } + + if ((keyChar >= 'a' && keyChar <= 'z') + || (keyChar == '\'' && !mDecInfo.charBeforeCursorIsSeparator()) + || (((keyChar >= '0' && keyChar <= '9') || keyChar == ' ') && ImeState.STATE_COMPOSING == mImeState)) { + mDecInfo.addSplChar((char) keyChar, false); + chooseAndUpdate(-1); + } else if (keyCode == KeyEvent.KEYCODE_DEL) { + mDecInfo.prepareDeleteBeforeCursor(); + chooseAndUpdate(-1); + } + return true; + } + + private void changeToStateComposing(boolean updateUi) { + mImeState = ImeState.STATE_COMPOSING; + if (!updateUi) return; + + if (null != mSkbContainer && mSkbContainer.isShown()) { + mSkbContainer.toggleCandidateMode(true); + } + } + + private void changeToStateInput(boolean updateUi) { + mImeState = ImeState.STATE_INPUT; + if (!updateUi) return; + + if (null != mSkbContainer && mSkbContainer.isShown()) { + mSkbContainer.toggleCandidateMode(true); + } + showCandidateWindow(true); + } + + private void simulateKeyEventDownUp(int keyCode) { + InputConnection ic = getCurrentInputConnection(); + if (null == ic) return; + + ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); + ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); + } + + private void commitResultText(String resultText) { + InputConnection ic = getCurrentInputConnection(); + if (null != ic) ic.commitText(resultText, 1); + if (null != mComposingView) { + mComposingView.setVisibility(View.INVISIBLE); + mComposingView.invalidate(); + } + } + + private void updateComposingText(boolean visible) { + if (!visible) { + mComposingView.setVisibility(View.INVISIBLE); + } else { + mComposingView.setDecodingInfo(mDecInfo, mImeState); + mComposingView.setVisibility(View.VISIBLE); + } + mComposingView.invalidate(); + } + + private void inputCommaPeriod(String preEdit, int keyChar, + boolean dismissCandWindow, ImeState nextState) { + if (keyChar == ',') + preEdit += '\uff0c'; + else if (keyChar == '.') + preEdit += '\u3002'; + else + return; + commitResultText(preEdit); + if (dismissCandWindow) resetCandidateWindow(); + mImeState = nextState; + } + + private void resetToIdleState(boolean resetInlineText) { + if (ImeState.STATE_IDLE == mImeState) return; + + mImeState = ImeState.STATE_IDLE; + mDecInfo.reset(); + + if (null != mComposingView) mComposingView.reset(); + if (resetInlineText) commitResultText(""); + resetCandidateWindow(); + } + + private void chooseAndUpdate(int candId) { + if (!mInputModeSwitcher.isChineseText()) { + String choice = mDecInfo.getCandidate(candId); + if (null != choice) { + commitResultText(choice); + } + resetToIdleState(false); + return; + } + + if (ImeState.STATE_PREDICT != mImeState) { + // Get result candidate list, if choice_id < 0, do a new decoding. + // If choice_id >=0, select the candidate, and get the new candidate + // list. + mDecInfo.chooseDecodingCandidate(candId); + } else { + // Choose a prediction item. + mDecInfo.choosePredictChoice(candId); + } + + if (mDecInfo.getComposingStr().length() > 0) { + String resultStr; + resultStr = mDecInfo.getComposingStrActivePart(); + + // choiceId >= 0 means user finishes a choice selection. + if (candId >= 0 && mDecInfo.canDoPrediction()) { + commitResultText(resultStr); + mImeState = ImeState.STATE_PREDICT; + if (null != mSkbContainer && mSkbContainer.isShown()) { + mSkbContainer.toggleCandidateMode(false); + } + // Try to get the prediction list. + if (Settings.getPrediction()) { + InputConnection ic = getCurrentInputConnection(); + if (null != ic) { + CharSequence cs = ic.getTextBeforeCursor(3, 0); + if (null != cs) { + mDecInfo.preparePredicts(cs); + } + } + } else { + mDecInfo.resetCandidates(); + } + + if (mDecInfo.mCandidatesList.size() > 0) { + showCandidateWindow(false); + } else { + resetToIdleState(false); + } + } else { + if (ImeState.STATE_IDLE == mImeState) { + if (mDecInfo.getSplStrDecodedLen() == 0) { + changeToStateComposing(true); + } else { + changeToStateInput(true); + } + } else { + if (mDecInfo.selectionFinished()) { + changeToStateComposing(true); + } + } + showCandidateWindow(true); + } + } else { + resetToIdleState(false); + } + } + + // If activeCandNo is less than 0, get the current active candidate number + // from candidate view, otherwise use activeCandNo. + private void chooseCandidate(int activeCandNo) { + if (activeCandNo < 0) { + activeCandNo = mCandidatesContainer.getActiveCandiatePos(); + } + if (activeCandNo >= 0) { + chooseAndUpdate(activeCandNo); + } + } + + private boolean startPinyinDecoderService() { + if (null == mDecInfo.mIPinyinDecoderService) { + Intent serviceIntent = new Intent(); + serviceIntent.setClass(this, PinyinDecoderService.class); + + if (null == mPinyinDecoderServiceConnection) { + mPinyinDecoderServiceConnection = new PinyinDecoderServiceConnection(); + } + + // Bind service + if (bindService(serviceIntent, mPinyinDecoderServiceConnection, + Context.BIND_AUTO_CREATE)) { + return true; + } else { + return false; + } + } + return true; + } + + @Override + public View onCreateCandidatesView() { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onCreateCandidatesView."); + } + + LayoutInflater inflater = getLayoutInflater(); + // Inflate the floating container view + mFloatingContainer = (LinearLayout) inflater.inflate( + R.layout.floating_container, null); + + // The first child is the composing view. + mComposingView = (ComposingView) mFloatingContainer.getChildAt(0); + + mCandidatesContainer = (CandidatesContainer) inflater.inflate( + R.layout.candidates_container, null); + + // Create balloon hint for candidates view. + mCandidatesBalloon = new BalloonHint(this, mCandidatesContainer, + MeasureSpec.UNSPECIFIED); + mCandidatesBalloon.setBalloonBackground(getResources().getDrawable( + R.drawable.candidate_balloon_bg)); + mCandidatesContainer.initialize(mChoiceNotifier, mCandidatesBalloon, + mGestureDetectorCandidates); + + // The floating window + if (null != mFloatingWindow && mFloatingWindow.isShowing()) { + mFloatingWindowTimer.cancelShowing(); + mFloatingWindow.dismiss(); + } + mFloatingWindow = new PopupWindow(this); + mFloatingWindow.setClippingEnabled(false); + mFloatingWindow.setBackgroundDrawable(null); + mFloatingWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + mFloatingWindow.setContentView(mFloatingContainer); + + setCandidatesViewShown(true); + return mCandidatesContainer; + } + + public void responseSoftKeyEvent(SoftKey sKey) { + if (null == sKey) return; + + InputConnection ic = getCurrentInputConnection(); + if (ic == null) return; + + int keyCode = sKey.getKeyCode(); + // Process some general keys, including KEYCODE_DEL, KEYCODE_SPACE, + // KEYCODE_ENTER and KEYCODE_DPAD_CENTER. + if (sKey.isKeyCodeKey()) { + if (processFunctionKeys(keyCode, true)) return; + } + + if (sKey.isUserDefKey()) { + updateIcon(mInputModeSwitcher.switchModeForUserKey(keyCode)); + resetToIdleState(false); + mSkbContainer.updateInputMode(); + } else { + if (sKey.isKeyCodeKey()) { + KeyEvent eDown = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, + keyCode, 0, 0, 0, 0, KeyEvent.FLAG_SOFT_KEYBOARD); + KeyEvent eUp = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, + 0, 0, 0, 0, KeyEvent.FLAG_SOFT_KEYBOARD); + + onKeyDown(keyCode, eDown); + onKeyUp(keyCode, eUp); + } else if (sKey.isUniStrKey()) { + boolean kUsed = false; + String keyLabel = sKey.getKeyLabel(); + if (mInputModeSwitcher.isChineseTextWithSkb() + && (ImeState.STATE_INPUT == mImeState || ImeState.STATE_COMPOSING == mImeState)) { + if (mDecInfo.length() > 0 && keyLabel.length() == 1 + && keyLabel.charAt(0) == '\'') { + processSurfaceChange('\'', 0); + kUsed = true; + } + } + if (!kUsed) { + if (ImeState.STATE_INPUT == mImeState) { + commitResultText(mDecInfo + .getCurrentFullSent(mCandidatesContainer + .getActiveCandiatePos())); + } else if (ImeState.STATE_COMPOSING == mImeState) { + commitResultText(mDecInfo.getComposingStr()); + } + commitResultText(keyLabel); + resetToIdleState(false); + } + } + + // If the current soft keyboard is not sticky, IME needs to go + // back to the previous soft keyboard automatically. + if (!mSkbContainer.isCurrentSkbSticky()) { + updateIcon(mInputModeSwitcher.requestBackToPreviousSkb()); + resetToIdleState(false); + mSkbContainer.updateInputMode(); + } + } + } + + private void showCandidateWindow(boolean showComposingView) { + if (mEnvironment.needDebug()) { + Log.d(TAG, "Candidates window is shown. Parent = " + + mCandidatesContainer); + } + + setCandidatesViewShown(true); + + if (null != mSkbContainer) mSkbContainer.requestLayout(); + + if (null == mCandidatesContainer) { + resetToIdleState(false); + return; + } + + updateComposingText(showComposingView); + mCandidatesContainer.showCandidates(mDecInfo, + ImeState.STATE_COMPOSING != mImeState); + mFloatingWindowTimer.postShowFloatingWindow(); + } + + private void dismissCandidateWindow() { + if (mEnvironment.needDebug()) { + Log.d(TAG, "Candidates window is to be dismissed"); + } + if (null == mCandidatesContainer) return; + try { + mFloatingWindowTimer.cancelShowing(); + mFloatingWindow.dismiss(); + } catch (Exception e) { + Log.e(TAG, "Fail to show the PopupWindow."); + } + setCandidatesViewShown(false); + + if (null != mSkbContainer && mSkbContainer.isShown()) { + mSkbContainer.toggleCandidateMode(false); + } + } + + private void resetCandidateWindow() { + if (mEnvironment.needDebug()) { + Log.d(TAG, "Candidates window is to be reset"); + } + if (null == mCandidatesContainer) return; + try { + mFloatingWindowTimer.cancelShowing(); + mFloatingWindow.dismiss(); + } catch (Exception e) { + Log.e(TAG, "Fail to show the PopupWindow."); + } + + if (null != mSkbContainer && mSkbContainer.isShown()) { + mSkbContainer.toggleCandidateMode(false); + } + + mDecInfo.resetCandidates(); + + if (null != mCandidatesContainer && mCandidatesContainer.isShown()) { + showCandidateWindow(false); + } + } + + private void updateIcon(int iconId) { + if (iconId > 0) { + showStatusIcon(iconId); + } else { + hideStatusIcon(); + } + } + + @Override + public View onCreateInputView() { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onCreateInputView."); + } + LayoutInflater inflater = getLayoutInflater(); + mSkbContainer = (SkbContainer) inflater.inflate(R.layout.skb_container, + null); + mSkbContainer.setService(this); + mSkbContainer.setInputModeSwitcher(mInputModeSwitcher); + mSkbContainer.setGestureDetector(mGestureDetectorSkb); + return mSkbContainer; + } + + @Override + public void onStartInput(EditorInfo editorInfo, boolean restarting) { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onStartInput " + " ccontentType: " + + String.valueOf(editorInfo.inputType) + " Restarting:" + + String.valueOf(restarting)); + } + updateIcon(mInputModeSwitcher.requestInputWithHkb(editorInfo)); + resetToIdleState(false); + } + + @Override + public void onStartInputView(EditorInfo editorInfo, boolean restarting) { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onStartInputView " + " contentType: " + + String.valueOf(editorInfo.inputType) + " Restarting:" + + String.valueOf(restarting)); + } + updateIcon(mInputModeSwitcher.requestInputWithSkb(editorInfo)); + resetToIdleState(false); + mSkbContainer.updateInputMode(); + setCandidatesViewShown(false); + } + + @Override + public void onFinishInputView(boolean finishingInput) { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onFinishInputView."); + } + resetToIdleState(false); + super.onFinishInputView(finishingInput); + } + + @Override + public void onFinishInput() { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onFinishInput."); + } + resetToIdleState(false); + super.onFinishInput(); + } + + @Override + public void onFinishCandidatesView(boolean finishingInput) { + if (mEnvironment.needDebug()) { + Log.d(TAG, "onFinishCandidateView."); + } + resetToIdleState(false); + super.onFinishCandidatesView(finishingInput); + } + + @Override public void onDisplayCompletions(CompletionInfo[] completions) { + if (!isFullscreenMode()) return; + if (null == completions || completions.length <= 0) return; + if (null == mSkbContainer || !mSkbContainer.isShown()) return; + + if (!mInputModeSwitcher.isChineseText() || + ImeState.STATE_IDLE == mImeState || + ImeState.STATE_PREDICT == mImeState) { + mImeState = ImeState.STATE_APP_COMPLETION; + mDecInfo.prepareAppCompletions(completions); + showCandidateWindow(false); + } + } + + private void onChoiceTouched(int activeCandNo) { + if (mImeState == ImeState.STATE_COMPOSING) { + changeToStateInput(true); + } else if (mImeState == ImeState.STATE_INPUT + || mImeState == ImeState.STATE_PREDICT) { + chooseCandidate(activeCandNo); + } else if (mImeState == ImeState.STATE_APP_COMPLETION) { + if (null != mDecInfo.mAppCompletions && activeCandNo >= 0 && + activeCandNo < mDecInfo.mAppCompletions.length) { + CompletionInfo ci = mDecInfo.mAppCompletions[activeCandNo]; + if (null != ci) { + InputConnection ic = getCurrentInputConnection(); + ic.commitCompletion(ci); + } + } + resetToIdleState(false); + } + } + + @Override + public void requestHideSelf(int flags) { + if (mEnvironment.needDebug()) { + Log.d(TAG, "DimissSoftInput."); + } + dismissCandidateWindow(); + if (null != mSkbContainer && mSkbContainer.isShown()) { + mSkbContainer.dismissPopups(); + } + super.requestHideSelf(flags); + } + + public void showOptionsMenu() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setCancelable(true); + builder.setIcon(R.drawable.app_icon); + builder.setNegativeButton(android.R.string.cancel, null); + CharSequence itemSettings = getString(R.string.ime_settings_activity_name); + CharSequence itemInputMethod = getString(com.android.internal.R.string.inputMethod); + builder.setItems(new CharSequence[] {itemSettings, itemInputMethod}, + new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface di, int position) { + di.dismiss(); + switch (position) { + case 0: + launchSettings(); + break; + case 1: + InputMethodManager.getInstance(PinyinIME.this) + .showInputMethodPicker(); + break; + } + } + }); + builder.setTitle(getString(R.string.ime_name)); + mOptionsDialog = builder.create(); + Window window = mOptionsDialog.getWindow(); + WindowManager.LayoutParams lp = window.getAttributes(); + lp.token = mSkbContainer.getWindowToken(); + lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; + window.setAttributes(lp); + window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + mOptionsDialog.show(); + } + + private void launchSettings() { + Intent intent = new Intent(); + intent.setClass(PinyinIME.this, SettingsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + + private class PopupTimer extends Handler implements Runnable { + private int mParentLocation[] = new int[2]; + + void postShowFloatingWindow() { + mFloatingContainer.measure(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + mFloatingWindow.setWidth(mFloatingContainer.getMeasuredWidth()); + mFloatingWindow.setHeight(mFloatingContainer.getMeasuredHeight()); + post(this); + } + + void cancelShowing() { + if (mFloatingWindow.isShowing()) { + mFloatingWindow.dismiss(); + } + removeCallbacks(this); + } + + public void run() { + mCandidatesContainer.getLocationInWindow(mParentLocation); + + if (!mFloatingWindow.isShowing()) { + mFloatingWindow.showAtLocation(mCandidatesContainer, + Gravity.LEFT | Gravity.TOP, mParentLocation[0], + mParentLocation[1] -mFloatingWindow.getHeight()); + } else { + mFloatingWindow + .update(mParentLocation[0], + mParentLocation[1] - mFloatingWindow.getHeight(), + mFloatingWindow.getWidth(), + mFloatingWindow.getHeight()); + } + } + } + + /** + * Used to notify IME that the user selects a candidate or performs an + * gesture. + */ + public class ChoiceNotifier extends Handler implements + CandidateViewListener { + PinyinIME mIme; + + ChoiceNotifier(PinyinIME ime) { + mIme = ime; + } + + public void onClickChoice(int choiceId) { + if (choiceId >= 0) { + mIme.onChoiceTouched(choiceId); + } + } + + public void onToLeftGesture() { + if (ImeState.STATE_COMPOSING == mImeState) { + changeToStateInput(true); + } + mCandidatesContainer.pageForward(true, false); + } + + public void onToRightGesture() { + if (ImeState.STATE_COMPOSING == mImeState) { + changeToStateInput(true); + } + mCandidatesContainer.pageBackward(true, false); + } + + public void onToTopGesture() { + } + + public void onToBottomGesture() { + } + } + + public class OnGestureListener extends + GestureDetector.SimpleOnGestureListener { + /** + * When user presses and drags, the minimum x-distance to make a + * response to the drag event. + */ + private static final int MIN_X_FOR_DRAG = 60; + + /** + * When user presses and drags, the minimum y-distance to make a + * response to the drag event. + */ + private static final int MIN_Y_FOR_DRAG = 40; + + /** + * Velocity threshold for a screen-move gesture. If the minimum + * x-velocity is less than it, no gesture. + */ + static private final float VELOCITY_THRESHOLD_X1 = 0.3f; + + /** + * Velocity threshold for a screen-move gesture. If the maximum + * x-velocity is less than it, no gesture. + */ + static private final float VELOCITY_THRESHOLD_X2 = 0.7f; + + /** + * Velocity threshold for a screen-move gesture. If the minimum + * y-velocity is less than it, no gesture. + */ + static private final float VELOCITY_THRESHOLD_Y1 = 0.2f; + + /** + * Velocity threshold for a screen-move gesture. If the maximum + * y-velocity is less than it, no gesture. + */ + static private final float VELOCITY_THRESHOLD_Y2 = 0.45f; + + /** If it false, we will not response detected gestures. */ + private boolean mReponseGestures; + + /** The minimum X velocity observed in the gesture. */ + private float mMinVelocityX = Float.MAX_VALUE; + + /** The minimum Y velocity observed in the gesture. */ + private float mMinVelocityY = Float.MAX_VALUE; + + /** The first down time for the series of touch events for an action. */ + private long mTimeDown; + + /** The last time when onScroll() is called. */ + private long mTimeLastOnScroll; + + /** This flag used to indicate that this gesture is not a gesture. */ + private boolean mNotGesture; + + /** This flag used to indicate that this gesture has been recognized. */ + private boolean mGestureRecognized; + + public OnGestureListener(boolean reponseGestures) { + mReponseGestures = reponseGestures; + } + + @Override + public boolean onDown(MotionEvent e) { + mMinVelocityX = Integer.MAX_VALUE; + mMinVelocityY = Integer.MAX_VALUE; + mTimeDown = e.getEventTime(); + mTimeLastOnScroll = mTimeDown; + mNotGesture = false; + mGestureRecognized = false; + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + if (mNotGesture) return false; + if (mGestureRecognized) return true; + + if (Math.abs(e1.getX() - e2.getX()) < MIN_X_FOR_DRAG + && Math.abs(e1.getY() - e2.getY()) < MIN_Y_FOR_DRAG) + return false; + + long timeNow = e2.getEventTime(); + long spanTotal = timeNow - mTimeDown; + long spanThis = timeNow - mTimeLastOnScroll; + if (0 == spanTotal) spanTotal = 1; + if (0 == spanThis) spanThis = 1; + + float vXTotal = (e2.getX() - e1.getX()) / spanTotal; + float vYTotal = (e2.getY() - e1.getY()) / spanTotal; + + // The distances are from the current point to the previous one. + float vXThis = -distanceX / spanThis; + float vYThis = -distanceY / spanThis; + + float kX = vXTotal * vXThis; + float kY = vYTotal * vYThis; + float k1 = kX + kY; + float k2 = Math.abs(kX) + Math.abs(kY); + + if (k1 / k2 < 0.8) { + mNotGesture = true; + return false; + } + float absVXTotal = Math.abs(vXTotal); + float absVYTotal = Math.abs(vYTotal); + if (absVXTotal < mMinVelocityX) { + mMinVelocityX = absVXTotal; + } + if (absVYTotal < mMinVelocityY) { + mMinVelocityY = absVYTotal; + } + + if (mMinVelocityX < VELOCITY_THRESHOLD_X1 + && mMinVelocityY < VELOCITY_THRESHOLD_Y1) { + mNotGesture = true; + return false; + } + + if (vXTotal > VELOCITY_THRESHOLD_X2 + && absVYTotal < VELOCITY_THRESHOLD_Y2) { + if (mReponseGestures) onDirectionGesture(Gravity.RIGHT); + mGestureRecognized = true; + } else if (vXTotal < -VELOCITY_THRESHOLD_X2 + && absVYTotal < VELOCITY_THRESHOLD_Y2) { + if (mReponseGestures) onDirectionGesture(Gravity.LEFT); + mGestureRecognized = true; + } else if (vYTotal > VELOCITY_THRESHOLD_Y2 + && absVXTotal < VELOCITY_THRESHOLD_X2) { + if (mReponseGestures) onDirectionGesture(Gravity.BOTTOM); + mGestureRecognized = true; + } else if (vYTotal < -VELOCITY_THRESHOLD_Y2 + && absVXTotal < VELOCITY_THRESHOLD_X2) { + if (mReponseGestures) onDirectionGesture(Gravity.TOP); + mGestureRecognized = true; + } + + mTimeLastOnScroll = timeNow; + return mGestureRecognized; + } + + @Override + public boolean onFling(MotionEvent me1, MotionEvent me2, + float velocityX, float velocityY) { + return mGestureRecognized; + } + + public void onDirectionGesture(int gravity) { + if (Gravity.NO_GRAVITY == gravity) { + return; + } + + if (Gravity.LEFT == gravity || Gravity.RIGHT == gravity) { + if (mCandidatesContainer.isShown()) { + if (Gravity.LEFT == gravity) { + mCandidatesContainer.pageForward(true, true); + } else { + mCandidatesContainer.pageBackward(true, true); + } + return; + } + } + } + } + + /** + * Connection used for binding to the Pinyin decoding service. + */ + public class PinyinDecoderServiceConnection implements ServiceConnection { + public void onServiceConnected(ComponentName name, IBinder service) { + mDecInfo.mIPinyinDecoderService = IPinyinDecoderService.Stub + .asInterface(service); + } + + public void onServiceDisconnected(ComponentName name) { + } + } + + public enum ImeState { + STATE_BYPASS, STATE_IDLE, STATE_INPUT, STATE_COMPOSING, STATE_PREDICT, + STATE_APP_COMPLETION + } + + public class DecodingInfo { + /** + * Maximum length of the Pinyin string + */ + private static final int PY_STRING_MAX = 28; + + /** + * Maximum number of candidates to display in one page. + */ + private static final int MAX_PAGE_SIZE_DISPLAY = 10; + + /** + * Spelling (Pinyin) string. + */ + private StringBuffer mSurface; + + /** + * Byte buffer used as the Pinyin string parameter for native function + * call. + */ + private byte mPyBuf[]; + + /** + * The length of surface string successfully decoded by engine. + */ + private int mSurfaceDecodedLen; + + /** + * Composing string. + */ + private String mComposingStr; + + /** + * Length of the active composing string. + */ + private int mActiveCmpsLen; + + /** + * Composing string for display, it is copied from mComposingStr, and + * add spaces between spellings. + **/ + private String mComposingStrDisplay; + + /** + * Length of the active composing string for display. + */ + private int mActiveCmpsDisplayLen; + + /** + * The first full sentence choice. + */ + private String mFullSent; + + /** + * Number of characters which have been fixed. + */ + private int mFixedLen; + + /** + * If this flag is true, selection is finished. + */ + private boolean mFinishSelection; + + /** + * The starting position for each spelling. The first one is the number + * of the real starting position elements. + */ + private int mSplStart[]; + + /** + * Editing cursor in mSurface. + */ + private int mCursorPos; + + /** + * Remote Pinyin-to-Hanzi decoding engine service. + */ + private IPinyinDecoderService mIPinyinDecoderService; + + /** + * The complication information suggested by application. + */ + private CompletionInfo[] mAppCompletions; + + /** + * The total number of choices for display. The list may only contains + * the first part. If user tries to navigate to next page which is not + * in the result list, we need to get these items. + **/ + public int mTotalChoicesNum; + + /** + * Candidate list. The first one is the full-sentence candidate. + */ + public List mCandidatesList = new Vector(); + + /** + * Element i stores the starting position of page i. + */ + public Vector mPageStart = new Vector(); + + /** + * Element i stores the number of characters to page i. + */ + public Vector mCnToPage = new Vector(); + + /** + * The position to delete in Pinyin string. If it is less than 0, IME + * will do an incremental search, otherwise IME will do a deletion + * operation. if {@link #mIsPosInSpl} is true, IME will delete the whole + * string for mPosDelSpl-th spelling, otherwise it will only delete + * mPosDelSpl-th character in the Pinyin string. + */ + public int mPosDelSpl = -1; + + /** + * If {@link #mPosDelSpl} is big than or equal to 0, this member is used + * to indicate that whether the postion is counted in spelling id or + * character. + */ + public boolean mIsPosInSpl; + + public DecodingInfo() { + mSurface = new StringBuffer(); + mSurfaceDecodedLen = 0; + } + + public void reset() { + mSurface.delete(0, mSurface.length()); + mSurfaceDecodedLen = 0; + mCursorPos = 0; + mFullSent = ""; + mFixedLen = 0; + mFinishSelection = false; + mComposingStr = ""; + mComposingStrDisplay = ""; + mActiveCmpsLen = 0; + mActiveCmpsDisplayLen = 0; + + resetCandidates(); + } + + public boolean isCandidatesListEmpty() { + return mCandidatesList.size() == 0; + } + + public boolean isSplStrFull() { + if (mSurface.length() >= PY_STRING_MAX - 1) return true; + return false; + } + + public void addSplChar(char ch, boolean reset) { + if (reset) { + mSurface.delete(0, mSurface.length()); + mSurfaceDecodedLen = 0; + mCursorPos = 0; + try { + mIPinyinDecoderService.imResetSearch(); + } catch (RemoteException e) { + } + } + mSurface.insert(mCursorPos, ch); + mCursorPos++; + } + + // Prepare to delete before cursor. We may delete a spelling char if + // the cursor is in the range of unfixed part, delete a whole spelling + // if the cursor in inside the range of the fixed part. + // This function only marks the position used to delete. + public void prepareDeleteBeforeCursor() { + if (mCursorPos > 0) { + int pos; + for (pos = 0; pos < mFixedLen; pos++) { + if (mSplStart[pos + 2] >= mCursorPos + && mSplStart[pos + 1] < mCursorPos) { + mPosDelSpl = pos; + mCursorPos = mSplStart[pos + 1]; + mIsPosInSpl = true; + break; + } + } + if (mPosDelSpl < 0) { + mPosDelSpl = mCursorPos - 1; + mCursorPos--; + mIsPosInSpl = false; + } + } + } + + public int length() { + return mSurface.length(); + } + + public char charAt(int index) { + return mSurface.charAt(index); + } + + public StringBuffer getOrigianlSplStr() { + return mSurface; + } + + public int getSplStrDecodedLen() { + return mSurfaceDecodedLen; + } + + public int[] getSplStart() { + return mSplStart; + } + + public String getComposingStr() { + return mComposingStr; + } + + public String getComposingStrActivePart() { + assert (mActiveCmpsLen <= mComposingStr.length()); + return mComposingStr.substring(0, mActiveCmpsLen); + } + + public int getActiveCmpsLen() { + return mActiveCmpsLen; + } + + public String getComposingStrForDisplay() { + return mComposingStrDisplay; + } + + public int getActiveCmpsDisplayLen() { + return mActiveCmpsDisplayLen; + } + + public String getFullSent() { + return mFullSent; + } + + public String getCurrentFullSent(int activeCandPos) { + try { + String retStr = mFullSent.substring(0, mFixedLen); + retStr += mCandidatesList.get(activeCandPos); + return retStr; + } catch (Exception e) { + return ""; + } + } + + public void resetCandidates() { + mCandidatesList.clear(); + mTotalChoicesNum = 0; + + mPageStart.clear(); + mPageStart.add(0); + mCnToPage.clear(); + mCnToPage.add(0); + } + + public boolean candidatesFromApp() { + return ImeState.STATE_APP_COMPLETION == mImeState; + } + + public boolean canDoPrediction() { + return mComposingStr.length() == mFixedLen; + } + + public boolean selectionFinished() { + return mFinishSelection; + } + + // After the user chooses a candidate, input method will do a + // re-decoding and give the new candidate list. + // If candidate id is less than 0, means user is inputting Pinyin, + // not selecting any choice. + private void chooseDecodingCandidate(int candId) { + if (mImeState != ImeState.STATE_PREDICT) { + resetCandidates(); + int totalChoicesNum = 0; + try { + if (candId < 0) { + if (length() == 0) { + totalChoicesNum = 0; + } else { + if (mPyBuf == null) + mPyBuf = new byte[PY_STRING_MAX]; + for (int i = 0; i < length(); i++) + mPyBuf[i] = (byte) charAt(i); + mPyBuf[length()] = 0; + + if (mPosDelSpl < 0) { + totalChoicesNum = mIPinyinDecoderService + .imSearch(mPyBuf, length()); + } else { + boolean clear_fixed_this_step = true; + if (ImeState.STATE_COMPOSING == mImeState) { + clear_fixed_this_step = false; + } + totalChoicesNum = mIPinyinDecoderService + .imDelSearch(mPosDelSpl, mIsPosInSpl, + clear_fixed_this_step); + mPosDelSpl = -1; + } + } + } else { + totalChoicesNum = mIPinyinDecoderService + .imChoose(candId); + } + } catch (RemoteException e) { + } + updateDecInfoForSearch(totalChoicesNum); + } + } + + private void updateDecInfoForSearch(int totalChoicesNum) { + mTotalChoicesNum = totalChoicesNum; + if (mTotalChoicesNum < 0) { + mTotalChoicesNum = 0; + return; + } + + try { + String pyStr; + + mSplStart = mIPinyinDecoderService.imGetSplStart(); + pyStr = mIPinyinDecoderService.imGetPyStr(false); + mSurfaceDecodedLen = mIPinyinDecoderService.imGetPyStrLen(true); + assert (mSurfaceDecodedLen <= pyStr.length()); + + mFullSent = mIPinyinDecoderService.imGetChoice(0); + mFixedLen = mIPinyinDecoderService.imGetFixedLen(); + + // Update the surface string to the one kept by engine. + mSurface.replace(0, mSurface.length(), pyStr); + + if (mCursorPos > mSurface.length()) + mCursorPos = mSurface.length(); + mComposingStr = mFullSent.substring(0, mFixedLen) + + mSurface.substring(mSplStart[mFixedLen + 1]); + + mActiveCmpsLen = mComposingStr.length(); + if (mSurfaceDecodedLen > 0) { + mActiveCmpsLen = mActiveCmpsLen + - (mSurface.length() - mSurfaceDecodedLen); + } + + // Prepare the display string. + if (0 == mSurfaceDecodedLen) { + mComposingStrDisplay = mComposingStr; + mActiveCmpsDisplayLen = mComposingStr.length(); + } else { + mComposingStrDisplay = mFullSent.substring(0, mFixedLen); + for (int pos = mFixedLen + 1; pos < mSplStart.length - 1; pos++) { + mComposingStrDisplay += mSurface.substring( + mSplStart[pos], mSplStart[pos + 1]); + if (mSplStart[pos + 1] < mSurfaceDecodedLen) { + mComposingStrDisplay += " "; + } + } + mActiveCmpsDisplayLen = mComposingStrDisplay.length(); + if (mSurfaceDecodedLen < mSurface.length()) { + mComposingStrDisplay += mSurface + .substring(mSurfaceDecodedLen); + } + } + + if (mSplStart.length == mFixedLen + 2) { + mFinishSelection = true; + } else { + mFinishSelection = false; + } + } catch (RemoteException e) { + Log.w(TAG, "PinyinDecoderService died", e); + } catch (Exception e) { + mTotalChoicesNum = 0; + mComposingStr = ""; + } + // Prepare page 0. + if (!mFinishSelection) { + preparePage(0); + } + } + + private void choosePredictChoice(int choiceId) { + if (ImeState.STATE_PREDICT != mImeState || choiceId < 0 + || choiceId >= mTotalChoicesNum) { + return; + } + + String tmp = mCandidatesList.get(choiceId); + + resetCandidates(); + + mCandidatesList.add(tmp); + mTotalChoicesNum = 1; + + mSurface.replace(0, mSurface.length(), ""); + mCursorPos = 0; + mFullSent = tmp; + mFixedLen = tmp.length(); + mComposingStr = mFullSent; + mActiveCmpsLen = mFixedLen; + + mFinishSelection = true; + } + + public String getCandidate(int candId) { + // Only loaded items can be gotten, so we use mCandidatesList.size() + // instead mTotalChoiceNum. + if (candId < 0 || candId > mCandidatesList.size()) { + return null; + } + return mCandidatesList.get(candId); + } + + private void getCandiagtesForCache() { + int fetchStart = mCandidatesList.size(); + int fetchSize = mTotalChoicesNum - fetchStart; + if (fetchSize > MAX_PAGE_SIZE_DISPLAY) { + fetchSize = MAX_PAGE_SIZE_DISPLAY; + } + try { + List newList = null; + if (ImeState.STATE_INPUT == mImeState || + ImeState.STATE_IDLE == mImeState || + ImeState.STATE_COMPOSING == mImeState){ + newList = mIPinyinDecoderService.imGetChoiceList( + fetchStart, fetchSize, mFixedLen); + } else if (ImeState.STATE_PREDICT == mImeState) { + newList = mIPinyinDecoderService.imGetPredictList( + fetchStart, fetchSize); + } else if (ImeState.STATE_APP_COMPLETION == mImeState) { + newList = new ArrayList(); + if (null != mAppCompletions) { + for (int pos = fetchStart; pos < fetchSize; pos++) { + CompletionInfo ci = mAppCompletions[pos]; + if (null != ci) { + CharSequence s = ci.getText(); + if (null != s) newList.add(s.toString()); + } + } + } + } + mCandidatesList.addAll(newList); + } catch (RemoteException e) { + Log.w(TAG, "PinyinDecoderService died", e); + } + } + + public boolean pageReady(int pageNo) { + // If the page number is less than 0, return false + if (pageNo < 0) return false; + + // Page pageNo's ending information is not ready. + if (mPageStart.size() <= pageNo + 1) { + return false; + } + + return true; + } + + public boolean preparePage(int pageNo) { + // If the page number is less than 0, return false + if (pageNo < 0) return false; + + // Make sure the starting information for page pageNo is ready. + if (mPageStart.size() <= pageNo) { + return false; + } + + // Page pageNo's ending information is also ready. + if (mPageStart.size() > pageNo + 1) { + return true; + } + + // If cached items is enough for page pageNo. + if (mCandidatesList.size() - mPageStart.elementAt(pageNo) >= MAX_PAGE_SIZE_DISPLAY) { + return true; + } + + // Try to get more items from engine + getCandiagtesForCache(); + + // Try to find if there are available new items to display. + // If no new item, return false; + if (mPageStart.elementAt(pageNo) >= mCandidatesList.size()) { + return false; + } + + // If there are new items, return true; + return true; + } + + public void preparePredicts(CharSequence history) { + if (null == history) return; + + resetCandidates(); + + if (Settings.getPrediction()) { + String preEdit = history.toString(); + int predictNum = 0; + if (null != preEdit) { + try { + mTotalChoicesNum = mIPinyinDecoderService + .imGetPredictsNum(preEdit); + } catch (RemoteException e) { + return; + } + } + } + + preparePage(0); + mFinishSelection = false; + } + + private void prepareAppCompletions(CompletionInfo completions[]) { + resetCandidates(); + mAppCompletions = completions; + mTotalChoicesNum = completions.length; + preparePage(0); + mFinishSelection = false; + return; + } + + public int getCurrentPageSize(int currentPage) { + if (mPageStart.size() <= currentPage + 1) return 0; + return mPageStart.elementAt(currentPage + 1) + - mPageStart.elementAt(currentPage); + } + + public int getCurrentPageStart(int currentPage) { + if (mPageStart.size() < currentPage + 1) return mTotalChoicesNum; + return mPageStart.elementAt(currentPage); + } + + public boolean pageForwardable(int currentPage) { + if (mPageStart.size() <= currentPage + 1) return false; + if (mPageStart.elementAt(currentPage + 1) >= mTotalChoicesNum) { + return false; + } + return true; + } + + public boolean pageBackwardable(int currentPage) { + if (currentPage > 0) return true; + return false; + } + + public boolean charBeforeCursorIsSeparator() { + int len = mSurface.length(); + if (mCursorPos > len) return false; + if (mCursorPos > 0 && mSurface.charAt(mCursorPos - 1) == '\'') { + return true; + } + return false; + } + + public int getCursorPos() { + return mCursorPos; + } + + public int getCursorPosInCmps() { + int cursorPos = mCursorPos; + int fixedLen = 0; + + for (int hzPos = 0; hzPos < mFixedLen; hzPos++) { + if (mCursorPos >= mSplStart[hzPos + 2]) { + cursorPos -= mSplStart[hzPos + 2] - mSplStart[hzPos + 1]; + cursorPos += 1; + } + } + return cursorPos; + } + + public int getCursorPosInCmpsDisplay() { + int cursorPos = getCursorPosInCmps(); + // +2 is because: one for mSplStart[0], which is used for other + // purpose(The length of the segmentation string), and another + // for the first spelling which does not need a space before it. + for (int pos = mFixedLen + 2; pos < mSplStart.length - 1; pos++) { + if (mCursorPos <= mSplStart[pos]) { + break; + } else { + cursorPos++; + } + } + return cursorPos; + } + + public void moveCursorToEdge(boolean left) { + if (left) + mCursorPos = 0; + else + mCursorPos = mSurface.length(); + } + + // Move cursor. If offset is 0, this function can be used to adjust + // the cursor into the bounds of the string. + public void moveCursor(int offset) { + if (offset > 1 || offset < -1) return; + + if (offset != 0) { + int hzPos = 0; + for (hzPos = 0; hzPos <= mFixedLen; hzPos++) { + if (mCursorPos == mSplStart[hzPos + 1]) { + if (offset < 0) { + if (hzPos > 0) { + offset = mSplStart[hzPos] + - mSplStart[hzPos + 1]; + } + } else { + if (hzPos < mFixedLen) { + offset = mSplStart[hzPos + 2] + - mSplStart[hzPos + 1]; + } + } + break; + } + } + } + mCursorPos += offset; + if (mCursorPos < 0) { + mCursorPos = 0; + } else if (mCursorPos > mSurface.length()) { + mCursorPos = mSurface.length(); + } + } + + public int getSplNum() { + return mSplStart[0]; + } + + public int getFixedLen() { + return mFixedLen; + } + } +} diff --git a/src/com/android/inputmethod/pinyin/Settings.java b/src/com/android/inputmethod/pinyin/Settings.java new file mode 100644 index 0000000..c05f605 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/Settings.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; + +/** + * Class used to maintain settings. + */ +public class Settings { + private static final String ANDPY_CONFS_KEYSOUND_KEY = "Sound"; + private static final String ANDPY_CONFS_VIBRATE_KEY = "Vibrate"; + private static final String ANDPY_CONFS_PREDICTION_KEY = "Prediction"; + + private static boolean mKeySound; + private static boolean mVibrate; + private static boolean mPrediction; + + private static Settings mInstance = null; + + private static int mRefCount = 0; + + private static SharedPreferences mSharedPref = null; + + protected Settings(SharedPreferences pref) { + mSharedPref = pref; + initConfs(); + } + + public static Settings getInstance(SharedPreferences pref) { + if (mInstance == null) { + mInstance = new Settings(pref); + } + assert (pref == mSharedPref); + mRefCount++; + return mInstance; + } + + public static void writeBack() { + Editor editor = mSharedPref.edit(); + editor.putBoolean(ANDPY_CONFS_VIBRATE_KEY, mVibrate); + editor.putBoolean(ANDPY_CONFS_KEYSOUND_KEY, mKeySound); + editor.putBoolean(ANDPY_CONFS_PREDICTION_KEY, mPrediction); + editor.commit(); + } + + public static void releaseInstance() { + mRefCount--; + if (mRefCount == 0) { + mInstance = null; + } + } + + private void initConfs() { + mKeySound = mSharedPref.getBoolean(ANDPY_CONFS_KEYSOUND_KEY, true); + mVibrate = mSharedPref.getBoolean(ANDPY_CONFS_VIBRATE_KEY, false); + mPrediction = mSharedPref.getBoolean(ANDPY_CONFS_PREDICTION_KEY, true); + } + + public static boolean getKeySound() { + return mKeySound; + } + + public static void setKeySound(boolean v) { + if (mKeySound == v) return; + mKeySound = v; + } + + public static boolean getVibrate() { + return mVibrate; + } + + public static void setVibrate(boolean v) { + if (mVibrate == v) return; + mVibrate = v; + } + + public static boolean getPrediction() { + return mPrediction; + } + + public static void setPrediction(boolean v) { + if (mPrediction == v) return; + mPrediction = v; + } +} diff --git a/src/com/android/inputmethod/pinyin/SettingsActivity.java b/src/com/android/inputmethod/pinyin/SettingsActivity.java new file mode 100644 index 0000000..7d23d8e --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SettingsActivity.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import java.util.List; + +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceGroup; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import com.android.inputmethod.pinyin.Settings; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; + +/** + * Setting activity of Pinyin IME. + */ +public class SettingsActivity extends PreferenceActivity implements + Preference.OnPreferenceChangeListener { + + private static String TAG = "SettingsActivity"; + + private CheckBoxPreference mKeySoundPref; + private CheckBoxPreference mVibratePref; + private CheckBoxPreference mPredictionPref; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.settings); + + PreferenceScreen prefSet = getPreferenceScreen(); + + mKeySoundPref = (CheckBoxPreference) prefSet + .findPreference(getString(R.string.setting_sound_key)); + mVibratePref = (CheckBoxPreference) prefSet + .findPreference(getString(R.string.setting_vibrate_key)); + mPredictionPref = (CheckBoxPreference) prefSet + .findPreference(getString(R.string.setting_prediction_key)); + + prefSet.setOnPreferenceChangeListener(this); + + Settings.getInstance(PreferenceManager + .getDefaultSharedPreferences(getApplicationContext())); + + updatePreference(prefSet, getString(R.string.setting_advanced_key)); + + updateWidgets(); + } + + @Override + protected void onResume() { + super.onResume(); + updateWidgets(); + } + + @Override + protected void onDestroy() { + Settings.releaseInstance(); + super.onDestroy(); + } + + @Override + protected void onPause() { + super.onPause(); + Settings.setKeySound(mKeySoundPref.isChecked()); + Settings.setVibrate(mVibratePref.isChecked()); + Settings.setPrediction(mPredictionPref.isChecked()); + + Settings.writeBack(); + } + + public boolean onPreferenceChange(Preference preference, Object newValue) { + return true; + } + + private void updateWidgets() { + mKeySoundPref.setChecked(Settings.getKeySound()); + mVibratePref.setChecked(Settings.getVibrate()); + mPredictionPref.setChecked(Settings.getPrediction()); + } + + public void updatePreference(PreferenceGroup parentPref, String prefKey) { + Preference preference = parentPref.findPreference(prefKey); + if (preference == null) { + return; + } + Intent intent = preference.getIntent(); + if (intent != null) { + PackageManager pm = getPackageManager(); + List list = pm.queryIntentActivities(intent, 0); + int listSize = list.size(); + if (listSize == 0) + parentPref.removePreference(preference); + } + } +} diff --git a/src/com/android/inputmethod/pinyin/SkbContainer.java b/src/com/android/inputmethod/pinyin/SkbContainer.java new file mode 100644 index 0000000..2294860 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SkbContainer.java @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.content.Context; +import android.content.res.Resources; +import android.inputmethodservice.InputMethodService; +import android.os.Handler; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.widget.PopupWindow; +import android.widget.RelativeLayout; +import android.widget.ViewFlipper; + +/** + * The top container to host soft keyboard view(s). + */ +public class SkbContainer extends RelativeLayout implements OnTouchListener { + /** + * For finger touch, user tends to press the bottom part of the target key, + * or he/she even presses the area out of it, so it is necessary to make a + * simple bias correction. If the input method runs on emulator, no bias + * correction will be used. + */ + private static final int Y_BIAS_CORRECTION = -10; + + /** + * Used to skip these move events whose position is too close to the + * previous touch events. + */ + private static final int MOVE_TOLERANCE = 6; + + /** + * If this member is true, PopupWindow is used to show on-key highlight + * effect. + */ + private static boolean POPUPWINDOW_FOR_PRESSED_UI = false; + + /** + * The current soft keyboard layout. + * + * @see com.android.inputmethod.pinyin.InputModeSwitcher for detailed layout + * definitions. + */ + private int mSkbLayout = 0; + + /** + * The input method service. + */ + private InputMethodService mService; + + /** + * Input mode switcher used to switch between different modes like Chinese, + * English, etc. + */ + private InputModeSwitcher mInputModeSwitcher; + + /** + * The gesture detector. + */ + private GestureDetector mGestureDetector; + + private Environment mEnvironment; + + private ViewFlipper mSkbFlipper; + + /** + * The popup balloon hint for key press/release. + */ + private BalloonHint mBalloonPopup; + + /** + * The on-key balloon hint for key press/release. + */ + private BalloonHint mBalloonOnKey = null; + + /** The major sub soft keyboard. */ + private SoftKeyboardView mMajorView; + + /** + * The last parameter when function {@link #toggleCandidateMode(boolean)} + * was called. + */ + private boolean mLastCandidatesShowing; + + /** Used to indicate whether a popup soft keyboard is shown. */ + private boolean mPopupSkbShow = false; + + /** + * Used to indicate whether a popup soft keyboard is just shown, and waits + * for the touch event to release. After the release, the popup window can + * response to touch events. + **/ + private boolean mPopupSkbNoResponse = false; + + /** Popup sub keyboard. */ + private PopupWindow mPopupSkb; + + /** The view of the popup sub soft keyboard. */ + private SoftKeyboardView mPopupSkbView; + + private int mPopupX; + + private int mPopupY; + + /** + * When user presses a key, a timer is started, when it times out, it is + * necessary to detect whether user still holds the key. + */ + private volatile boolean mWaitForTouchUp = false; + + /** + * When user drags on the soft keyboard and the distance is enough, this + * drag will be recognized as a gesture and a gesture-based action will be + * taken, in this situation, ignore the consequent events. + */ + private volatile boolean mDiscardEvent = false; + + /** + * For finger touch, user tends to press the bottom part of the target key, + * or he/she even presses the area out of it, so it is necessary to make a + * simple bias correction in Y. + */ + private int mYBiasCorrection = 0; + + /** + * The x coordination of the last touch event. + */ + private int mXLast; + + /** + * The y coordination of the last touch event. + */ + private int mYLast; + + /** + * The soft keyboard view. + */ + private SoftKeyboardView mSkv; + + /** + * The position of the soft keyboard view in the container. + */ + private int mSkvPosInContainer[] = new int[2]; + + /** + * The key pressed by user. + */ + private SoftKey mSoftKeyDown = null; + + /** + * Used to timeout a press if user holds the key for a long time. + */ + private LongPressTimer mLongPressTimer; + + /** + * For temporary use. + */ + private int mXyPosTmp[] = new int[2]; + + public SkbContainer(Context context, AttributeSet attrs) { + super(context, attrs); + + mEnvironment = Environment.getInstance(); + + mLongPressTimer = new LongPressTimer(this); + + // If it runs on an emulator, no bias correction + if ("1".equals(SystemProperties.get("ro.kernel.qemu"))) { + mYBiasCorrection = 0; + } else { + mYBiasCorrection = Y_BIAS_CORRECTION; + } + mBalloonPopup = new BalloonHint(context, this, MeasureSpec.AT_MOST); + if (POPUPWINDOW_FOR_PRESSED_UI) { + mBalloonOnKey = new BalloonHint(context, this, MeasureSpec.AT_MOST); + } + + mPopupSkb = new PopupWindow(mContext); + mPopupSkb.setBackgroundDrawable(null); + mPopupSkb.setClippingEnabled(false); + } + + public void setService(InputMethodService service) { + mService = service; + } + + public void setInputModeSwitcher(InputModeSwitcher inputModeSwitcher) { + mInputModeSwitcher = inputModeSwitcher; + } + + public void setGestureDetector(GestureDetector gestureDetector) { + mGestureDetector = gestureDetector; + } + + public boolean isCurrentSkbSticky() { + if (null == mMajorView) return true; + SoftKeyboard skb = mMajorView.getSoftKeyboard(); + if (null != skb) { + return skb.getStickyFlag(); + } + return true; + } + + public void toggleCandidateMode(boolean candidatesShowing) { + if (null == mMajorView || !mInputModeSwitcher.isChineseText() + || mLastCandidatesShowing == candidatesShowing) return; + mLastCandidatesShowing = candidatesShowing; + + SoftKeyboard skb = mMajorView.getSoftKeyboard(); + if (null == skb) return; + + int state = mInputModeSwitcher.getTooggleStateForCnCand(); + if (!candidatesShowing) { + skb.disableToggleState(state, false); + skb.enableToggleStates(mInputModeSwitcher.getToggleStates()); + } else { + skb.enableToggleState(state, false); + } + + mMajorView.invalidate(); + } + + public void updateInputMode() { + int skbLayout = mInputModeSwitcher.getSkbLayout(); + if (mSkbLayout != skbLayout) { + mSkbLayout = skbLayout; + updateSkbLayout(); + } + + mLastCandidatesShowing = false; + + if (null == mMajorView) return; + + SoftKeyboard skb = mMajorView.getSoftKeyboard(); + if (null == skb) return; + skb.enableToggleStates(mInputModeSwitcher.getToggleStates()); + invalidate(); + return; + } + + private void updateSkbLayout() { + int screenWidth = mEnvironment.getScreenWidth(); + int keyHeight = mEnvironment.getKeyHeight(); + int skbHeight = mEnvironment.getSkbHeight(); + + Resources r = mContext.getResources(); + if (null == mSkbFlipper) { + mSkbFlipper = (ViewFlipper) findViewById(R.id.alpha_floatable); + } + mMajorView = (SoftKeyboardView) mSkbFlipper.getChildAt(0); + + SoftKeyboard majorSkb = null; + SkbPool skbPool = SkbPool.getInstance(); + + switch (mSkbLayout) { + case R.xml.skb_qwerty: + majorSkb = skbPool.getSoftKeyboard(R.xml.skb_qwerty, + R.xml.skb_qwerty, screenWidth, skbHeight, mContext); + break; + + case R.xml.skb_sym1: + majorSkb = skbPool.getSoftKeyboard(R.xml.skb_sym1, R.xml.skb_sym1, + screenWidth, skbHeight, mContext); + break; + + case R.xml.skb_sym2: + majorSkb = skbPool.getSoftKeyboard(R.xml.skb_sym2, R.xml.skb_sym2, + screenWidth, skbHeight, mContext); + break; + + case R.xml.skb_smiley: + majorSkb = skbPool.getSoftKeyboard(R.xml.skb_smiley, + R.xml.skb_smiley, screenWidth, skbHeight, mContext); + break; + + case R.xml.skb_phone: + majorSkb = skbPool.getSoftKeyboard(R.xml.skb_phone, + R.xml.skb_phone, screenWidth, skbHeight, mContext); + break; + default: + } + + if (null == majorSkb || !mMajorView.setSoftKeyboard(majorSkb)) { + return; + } + mMajorView.setBalloonHint(mBalloonOnKey, mBalloonPopup, false); + mMajorView.invalidate(); + } + + private void responseKeyEvent(SoftKey sKey) { + if (null == sKey) return; + ((PinyinIME) mService).responseSoftKeyEvent(sKey); + return; + } + + private SoftKeyboardView inKeyboardView(int x, int y, + int positionInParent[]) { + if (mPopupSkbShow) { + if (mPopupX <= x && mPopupX + mPopupSkb.getWidth() > x + && mPopupY <= y && mPopupY + mPopupSkb.getHeight() > y) { + positionInParent[0] = mPopupX; + positionInParent[1] = mPopupY; + mPopupSkbView.setOffsetToSkbContainer(positionInParent); + return mPopupSkbView; + } + return null; + } + + return mMajorView; + } + + private void popupSymbols() { + int popupResId = mSoftKeyDown.getPopupResId(); + if (popupResId > 0) { + int skbContainerWidth = getWidth(); + int skbContainerHeight = getHeight(); + // The paddings of the background are not included. + int miniSkbWidth = (int) (skbContainerWidth * 0.8); + int miniSkbHeight = (int) (skbContainerHeight * 0.23); + + SkbPool skbPool = SkbPool.getInstance(); + SoftKeyboard skb = skbPool.getSoftKeyboard(popupResId, popupResId, + miniSkbWidth, miniSkbHeight, mContext); + if (null == skb) return; + + mPopupX = (skbContainerWidth - skb.getSkbTotalWidth()) / 2; + mPopupY = (skbContainerHeight - skb.getSkbTotalHeight()) / 2; + + if (null == mPopupSkbView) { + mPopupSkbView = new SoftKeyboardView(mContext, null); + mPopupSkbView.onMeasure(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + } + mPopupSkbView.setOnTouchListener(this); + mPopupSkbView.setSoftKeyboard(skb); + mPopupSkbView.setBalloonHint(mBalloonOnKey, mBalloonPopup, true); + + mPopupSkb.setContentView(mPopupSkbView); + mPopupSkb.setWidth(skb.getSkbCoreWidth() + + mPopupSkbView.getPaddingLeft() + + mPopupSkbView.getPaddingRight()); + mPopupSkb.setHeight(skb.getSkbCoreHeight() + + mPopupSkbView.getPaddingTop() + + mPopupSkbView.getPaddingBottom()); + + getLocationInWindow(mXyPosTmp); + mPopupSkb.showAtLocation(this, Gravity.NO_GRAVITY, mPopupX, mPopupY + + mXyPosTmp[1]); + mPopupSkbShow = true; + mPopupSkbNoResponse = true; + // Invalidate itself to dim the current soft keyboards. + dimSoftKeyboard(true); + resetKeyPress(0); + } + } + + private void dimSoftKeyboard(boolean dimSkb) { + mMajorView.dimSoftKeyboard(dimSkb); + } + + private void dismissPopupSkb() { + mPopupSkb.dismiss(); + mPopupSkbShow = false; + dimSoftKeyboard(false); + resetKeyPress(0); + } + + private void resetKeyPress(long delay) { + mLongPressTimer.removeTimer(); + + if (null != mSkv) { + mSkv.resetKeyPress(delay); + } + } + + public boolean handleBack(boolean realAction) { + if (mPopupSkbShow) { + if (!realAction) return true; + + dismissPopupSkb(); + mDiscardEvent = true; + return true; + } + return false; + } + + public void dismissPopups() { + handleBack(true); + resetKeyPress(0); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Environment env = Environment.getInstance(); + int measuredWidth = env.getScreenWidth(); + int measuredHeight = getPaddingTop(); + measuredHeight += env.getSkbHeight(); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth, + MeasureSpec.EXACTLY); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(measuredHeight, + MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + + if (mSkbFlipper.isFlipping()) { + resetKeyPress(0); + return true; + } + + int x = (int) event.getX(); + int y = (int) event.getY(); + // Bias correction + y = y + mYBiasCorrection; + + // Ignore short-distance movement event to get better performance. + if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (Math.abs(x - mXLast) <= MOVE_TOLERANCE + && Math.abs(y - mYLast) <= MOVE_TOLERANCE) { + return true; + } + } + + mXLast = x; + mYLast = y; + + if (!mPopupSkbShow) { + if (mGestureDetector.onTouchEvent(event)) { + resetKeyPress(0); + mDiscardEvent = true; + return true; + } + } + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + resetKeyPress(0); + + mWaitForTouchUp = true; + mDiscardEvent = false; + + mSkv = null; + mSoftKeyDown = null; + mSkv = inKeyboardView(x, y, mSkvPosInContainer); + if (null != mSkv) { + mSoftKeyDown = mSkv.onKeyPress(x - mSkvPosInContainer[0], y + - mSkvPosInContainer[1], mLongPressTimer, false); + } + break; + + case MotionEvent.ACTION_MOVE: + if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) { + break; + } + if (mDiscardEvent) { + resetKeyPress(0); + break; + } + + if (mPopupSkbShow && mPopupSkbNoResponse) { + break; + } + + SoftKeyboardView skv = inKeyboardView(x, y, mSkvPosInContainer); + if (null != skv) { + if (skv != mSkv) { + mSkv = skv; + mSoftKeyDown = mSkv.onKeyPress(x - mSkvPosInContainer[0], y + - mSkvPosInContainer[1], mLongPressTimer, true); + } else if (null != skv) { + if (null != mSkv) { + mSoftKeyDown = mSkv.onKeyMove( + x - mSkvPosInContainer[0], y + - mSkvPosInContainer[1]); + if (null == mSoftKeyDown) { + mDiscardEvent = true; + } + } + } + } + break; + + case MotionEvent.ACTION_UP: + if (mDiscardEvent) { + resetKeyPress(0); + break; + } + + mWaitForTouchUp = false; + + // The view which got the {@link MotionEvent#ACTION_DOWN} event is + // always used to handle this event. + if (null != mSkv) { + mSkv.onKeyRelease(x - mSkvPosInContainer[0], y + - mSkvPosInContainer[1]); + } + + if (!mPopupSkbShow || !mPopupSkbNoResponse) { + responseKeyEvent(mSoftKeyDown); + } + + if (mSkv == mPopupSkbView && !mPopupSkbNoResponse) { + dismissPopupSkb(); + } + mPopupSkbNoResponse = false; + break; + + case MotionEvent.ACTION_CANCEL: + break; + } + + if (null == mSkv) { + return false; + } + + return true; + } + + // Function for interface OnTouchListener, it is used to handle touch events + // which will be delivered to the popup soft keyboard view. + public boolean onTouch(View v, MotionEvent event) { + // Translate the event to fit to the container. + MotionEvent newEv = MotionEvent.obtain(event.getDownTime(), event + .getEventTime(), event.getAction(), event.getX() + mPopupX, + event.getY() + mPopupY, event.getPressure(), event.getSize(), + event.getMetaState(), event.getXPrecision(), event + .getYPrecision(), event.getDeviceId(), event + .getEdgeFlags()); + boolean ret = onTouchEvent(newEv); + return ret; + } + + class LongPressTimer extends Handler implements Runnable { + /** + * When user presses a key for a long time, the timeout interval to + * generate first {@link #LONG_PRESS_KEYNUM1} key events. + */ + public static final int LONG_PRESS_TIMEOUT1 = 500; + + /** + * When user presses a key for a long time, after the first + * {@link #LONG_PRESS_KEYNUM1} key events, this timeout interval will be + * used. + */ + private static final int LONG_PRESS_TIMEOUT2 = 100; + + /** + * When user presses a key for a long time, after the first + * {@link #LONG_PRESS_KEYNUM2} key events, this timeout interval will be + * used. + */ + private static final int LONG_PRESS_TIMEOUT3 = 100; + + /** + * When user presses a key for a long time, after the first + * {@link #LONG_PRESS_KEYNUM1} key events, timeout interval + * {@link #LONG_PRESS_TIMEOUT2} will be used instead. + */ + public static final int LONG_PRESS_KEYNUM1 = 1; + + /** + * When user presses a key for a long time, after the first + * {@link #LONG_PRESS_KEYNUM2} key events, timeout interval + * {@link #LONG_PRESS_TIMEOUT3} will be used instead. + */ + public static final int LONG_PRESS_KEYNUM2 = 3; + + SkbContainer mSkbContainer; + + private int mResponseTimes = 0; + + public LongPressTimer(SkbContainer skbContainer) { + mSkbContainer = skbContainer; + } + + public void startTimer() { + postAtTime(this, SystemClock.uptimeMillis() + LONG_PRESS_TIMEOUT1); + mResponseTimes = 0; + } + + public boolean removeTimer() { + removeCallbacks(this); + return true; + } + + public void run() { + if (mWaitForTouchUp) { + mResponseTimes++; + if (mSoftKeyDown.repeatable()) { + if (mSoftKeyDown.isUserDefKey()) { + if (1 == mResponseTimes) { + if (mInputModeSwitcher + .tryHandleLongPressSwitch(mSoftKeyDown.mKeyCode)) { + mDiscardEvent = true; + resetKeyPress(0); + } + } + } else { + responseKeyEvent(mSoftKeyDown); + long timeout; + if (mResponseTimes < LONG_PRESS_KEYNUM1) { + timeout = LONG_PRESS_TIMEOUT1; + } else if (mResponseTimes < LONG_PRESS_KEYNUM2) { + timeout = LONG_PRESS_TIMEOUT2; + } else { + timeout = LONG_PRESS_TIMEOUT3; + } + postAtTime(this, SystemClock.uptimeMillis() + timeout); + } + } else { + if (1 == mResponseTimes) { + popupSymbols(); + } + } + } + } + } +} diff --git a/src/com/android/inputmethod/pinyin/SkbPool.java b/src/com/android/inputmethod/pinyin/SkbPool.java new file mode 100644 index 0000000..4c46951 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SkbPool.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import java.util.Vector; + +import android.content.Context; + +/** + * Class used to cache previously loaded soft keyboard layouts. + */ +public class SkbPool { + private static SkbPool mInstance = null; + + private Vector mSkbTemplates = new Vector(); + private Vector mSoftKeyboards = new Vector(); + + private SkbPool() { + } + + public static SkbPool getInstance() { + if (null == mInstance) mInstance = new SkbPool(); + return mInstance; + } + + public void resetCachedSkb() { + mSoftKeyboards.clear(); + } + + public SkbTemplate getSkbTemplate(int skbTemplateId, Context context) { + for (int i = 0; i < mSkbTemplates.size(); i++) { + SkbTemplate t = mSkbTemplates.elementAt(i); + if (t.getSkbTemplateId() == skbTemplateId) { + return t; + } + } + + if (null != context) { + XmlKeyboardLoader xkbl = new XmlKeyboardLoader(context); + SkbTemplate t = xkbl.loadSkbTemplate(skbTemplateId); + if (null != t) { + mSkbTemplates.add(t); + return t; + } + } + return null; + } + + // Try to find the keyboard in the pool with the cache id. If there is no + // keyboard found, try to load it with the given xml id. + public SoftKeyboard getSoftKeyboard(int skbCacheId, int skbXmlId, + int skbWidth, int skbHeight, Context context) { + for (int i = 0; i < mSoftKeyboards.size(); i++) { + SoftKeyboard skb = mSoftKeyboards.elementAt(i); + if (skb.getCacheId() == skbCacheId && skb.getSkbXmlId() == skbXmlId) { + skb.setSkbCoreSize(skbWidth, skbHeight); + skb.setNewlyLoadedFlag(false); + return skb; + } + } + if (null != context) { + XmlKeyboardLoader xkbl = new XmlKeyboardLoader(context); + SoftKeyboard skb = xkbl.loadKeyboard(skbXmlId, skbWidth, skbHeight); + if (skb != null) { + if (skb.getCacheFlag()) { + skb.setCacheId(skbCacheId); + mSoftKeyboards.add(skb); + } + } + return skb; + } + return null; + } +} diff --git a/src/com/android/inputmethod/pinyin/SkbTemplate.java b/src/com/android/inputmethod/pinyin/SkbTemplate.java new file mode 100644 index 0000000..9ab53ff --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SkbTemplate.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.graphics.drawable.Drawable; + +import java.util.Vector; + +/** + * Key icon definition. It is defined in soft keyboard template. A soft keyboard + * can refer to such an icon in its xml file directly to improve performance. + */ +class KeyIconRecord { + int keyCode; + Drawable icon; + Drawable iconPopup; +} + + +/** + * Default definition for a certain key. It is defined in soft keyboard + * template. A soft keyboard can refer to a default key in its xml file. Nothing + * of the key can be overwritten, including the size. + */ +class KeyRecord { + int keyId; + SoftKey softKey; +} + + +/** + * Soft keyboard template used by soft keyboards to share common resources. In + * this way, memory cost is reduced. + */ +public class SkbTemplate { + private int mSkbTemplateId; + private Drawable mSkbBg; + private Drawable mBalloonBg; + private Drawable mPopupBg; + private float mXMargin = 0; + private float mYMargin = 0; + /** Key type list. */ + private Vector mKeyTypeList = new Vector(); + + /** + * Default key icon list. It is only for keys which do not have popup icons. + */ + private Vector mKeyIconRecords = new Vector(); + + /** + * Default key list. + */ + private Vector mKeyRecords = new Vector(); + + public SkbTemplate(int skbTemplateId) { + mSkbTemplateId = skbTemplateId; + } + + public int getSkbTemplateId() { + return mSkbTemplateId; + } + + public void setBackgrounds(Drawable skbBg, Drawable balloonBg, + Drawable popupBg) { + mSkbBg = skbBg; + mBalloonBg = balloonBg; + mPopupBg = popupBg; + } + + public Drawable getSkbBackground() { + return mSkbBg; + } + + public Drawable getBalloonBackground() { + return mBalloonBg; + } + + public Drawable getPopupBackground() { + return mPopupBg; + } + + public void setMargins(float xMargin, float yMargin) { + mXMargin = xMargin; + mYMargin = yMargin; + } + + public float getXMargin() { + return mXMargin; + } + + public float getYMargin() { + return mYMargin; + } + + public SoftKeyType createKeyType(int id, Drawable bg, Drawable hlBg) { + return new SoftKeyType(id, bg, hlBg); + } + + public boolean addKeyType(SoftKeyType keyType) { + // The newly added item should have the right id. + if (mKeyTypeList.size() != keyType.mKeyTypeId) return false; + mKeyTypeList.add(keyType); + return true; + } + + public SoftKeyType getKeyType(int typeId) { + if (typeId < 0 || typeId > mKeyTypeList.size()) return null; + return mKeyTypeList.elementAt(typeId); + } + + public void addDefaultKeyIcons(int keyCode, Drawable icon, + Drawable iconPopup) { + if (null == icon || null == iconPopup) return; + + KeyIconRecord iconRecord = new KeyIconRecord(); + iconRecord.icon = icon; + iconRecord.iconPopup = iconPopup; + iconRecord.keyCode = keyCode; + + int size = mKeyIconRecords.size(); + int pos = 0; + while (pos < size) { + if (mKeyIconRecords.get(pos).keyCode >= keyCode) break; + pos++; + } + mKeyIconRecords.add(pos, iconRecord); + } + + public Drawable getDefaultKeyIcon(int keyCode) { + int size = mKeyIconRecords.size(); + int pos = 0; + while (pos < size) { + KeyIconRecord iconRecord = mKeyIconRecords.get(pos); + if (iconRecord.keyCode < keyCode) { + pos++; + continue; + } + if (iconRecord.keyCode == keyCode) { + return iconRecord.icon; + } + return null; + } + return null; + } + + public Drawable getDefaultKeyIconPopup(int keyCode) { + int size = mKeyIconRecords.size(); + int pos = 0; + while (pos < size) { + KeyIconRecord iconRecord = mKeyIconRecords.get(pos); + if (iconRecord.keyCode < keyCode) { + pos++; + continue; + } + if (iconRecord.keyCode == keyCode) { + return iconRecord.iconPopup; + } + return null; + } + return null; + } + + public void addDefaultKey(int keyId, SoftKey softKey) { + if (null == softKey) return; + + KeyRecord keyRecord = new KeyRecord(); + keyRecord.keyId = keyId; + keyRecord.softKey = softKey; + + int size = mKeyRecords.size(); + int pos = 0; + while (pos < size) { + if (mKeyRecords.get(pos).keyId >= keyId) break; + pos++; + } + mKeyRecords.add(pos, keyRecord); + } + + public SoftKey getDefaultKey(int keyId) { + int size = mKeyRecords.size(); + int pos = 0; + while (pos < size) { + KeyRecord keyRecord = mKeyRecords.get(pos); + if (keyRecord.keyId < keyId) { + pos++; + continue; + } + if (keyRecord.keyId == keyId) { + return keyRecord.softKey; + } + return null; + } + return null; + } +} + + +class SoftKeyType { + public static final int KEYTYPE_ID_NORMAL_KEY = 0; + + public int mKeyTypeId; + public Drawable mKeyBg; + public Drawable mKeyHlBg; + public int mColor; + public int mColorHl; + public int mColorBalloon; + + SoftKeyType(int id, Drawable bg, Drawable hlBg) { + mKeyTypeId = id; + mKeyBg = bg; + mKeyHlBg = hlBg; + } + + public void setColors(int color, int colorHl, int colorBalloon) { + mColor = color; + mColorHl = colorHl; + mColorBalloon = colorBalloon; + } +} diff --git a/src/com/android/inputmethod/pinyin/SoftKey.java b/src/com/android/inputmethod/pinyin/SoftKey.java new file mode 100644 index 0000000..67eaf29 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SoftKey.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.graphics.drawable.Drawable; + +/** + * Class for soft keys which defined in the keyboard xml file. A soft key can be + * a basic key or a toggling key. + * + * @see com.android.inputmethod.pinyin.SoftKeyToggle + */ +public class SoftKey { + protected static final int KEYMASK_REPEAT = 0x10000000; + protected static final int KEYMASK_BALLOON = 0x20000000; + + /** + * For a finger touch device, after user presses a key, there will be some + * consequent moving events because of the changing in touching pressure. If + * the moving distance in x is within this threshold, the moving events will + * be ignored. + */ + public static final int MAX_MOVE_TOLERANCE_X = 0; + + /** + * For a finger touch device, after user presses a key, there will be some + * consequent moving events because of the changing in touching pressure. If + * the moving distance in y is within this threshold, the moving events will + * be ignored. + */ + public static final int MAX_MOVE_TOLERANCE_Y = 0; + + /** + * Used to indicate the type and attributes of this key. the lowest 8 bits + * should be reserved for SoftkeyToggle. + */ + protected int mKeyMask; + + protected SoftKeyType mKeyType; + + protected Drawable mKeyIcon; + + protected Drawable mKeyIconPopup; + + protected String mKeyLabel; + + protected int mKeyCode; + + /** + * If this value is not 0, this key can be used to popup a sub soft keyboard + * when user presses it for some time. + */ + public int mPopupSkbId; + + public float mLeftF; + public float mRightF; + public float mTopF; + public float mBottomF; + public int mLeft; + public int mRight; + public int mTop; + public int mBottom; + + public void setKeyType(SoftKeyType keyType, Drawable keyIcon, + Drawable keyIconPopup) { + mKeyType = keyType; + mKeyIcon = keyIcon; + mKeyIconPopup = keyIconPopup; + } + + // The caller guarantees that all parameters are in [0, 1] + public void setKeyDimensions(float left, float top, float right, + float bottom) { + mLeftF = left; + mTopF = top; + mRightF = right; + mBottomF = bottom; + } + + public void setKeyAttribute(int keyCode, String label, boolean repeat, + boolean balloon) { + mKeyCode = keyCode; + mKeyLabel = label; + + if (repeat) { + mKeyMask |= KEYMASK_REPEAT; + } else { + mKeyMask &= (~KEYMASK_REPEAT); + } + + if (balloon) { + mKeyMask |= KEYMASK_BALLOON; + } else { + mKeyMask &= (~KEYMASK_BALLOON); + } + } + + public void setPopupSkbId(int popupSkbId) { + mPopupSkbId = popupSkbId; + } + + // Call after setKeyDimensions(). The caller guarantees that the + // keyboard with and height are valid. + public void setSkbCoreSize(int skbWidth, int skbHeight) { + mLeft = (int) (mLeftF * skbWidth); + mRight = (int) (mRightF * skbWidth); + mTop = (int) (mTopF * skbHeight); + mBottom = (int) (mBottomF * skbHeight); + } + + public Drawable getKeyIcon() { + return mKeyIcon; + } + + public Drawable getKeyIconPopup() { + if (null != mKeyIconPopup) { + return mKeyIconPopup; + } + return mKeyIcon; + } + + public int getKeyCode() { + return mKeyCode; + } + + public String getKeyLabel() { + return mKeyLabel; + } + + public void changeCase(boolean upperCase) { + if (null != mKeyLabel) { + if (upperCase) + mKeyLabel = mKeyLabel.toUpperCase(); + else + mKeyLabel = mKeyLabel.toLowerCase(); + } + } + + public Drawable getKeyBg() { + return mKeyType.mKeyBg; + } + + public Drawable getKeyHlBg() { + return mKeyType.mKeyHlBg; + } + + public int getColor() { + return mKeyType.mColor; + } + + public int getColorHl() { + return mKeyType.mColorHl; + } + + public int getColorBalloon() { + return mKeyType.mColorBalloon; + } + + public boolean isKeyCodeKey() { + if (mKeyCode > 0) return true; + return false; + } + + public boolean isUserDefKey() { + if (mKeyCode < 0) return true; + return false; + } + + public boolean isUniStrKey() { + if (null != mKeyLabel && mKeyCode == 0) return true; + return false; + } + + public boolean needBalloon() { + return (mKeyMask & KEYMASK_BALLOON) != 0; + } + + public boolean repeatable() { + return (mKeyMask & KEYMASK_REPEAT) != 0; + } + + public int getPopupResId() { + return mPopupSkbId; + } + + public int width() { + return mRight - mLeft; + } + + public int height() { + return mBottom - mTop; + } + + public boolean moveWithinKey(int x, int y) { + if (mLeft - MAX_MOVE_TOLERANCE_X <= x + && mTop - MAX_MOVE_TOLERANCE_Y <= y + && mRight + MAX_MOVE_TOLERANCE_X > x + && mBottom + MAX_MOVE_TOLERANCE_Y > y) { + return true; + } + return false; + } + + @Override + public String toString() { + String str = "\n"; + str += " keyCode: " + String.valueOf(mKeyCode) + "\n"; + str += " keyMask: " + String.valueOf(mKeyMask) + "\n"; + str += " keyLabel: " + (mKeyLabel == null ? "null" : mKeyLabel) + "\n"; + str += " popupResId: " + String.valueOf(mPopupSkbId) + "\n"; + str += " Position: " + String.valueOf(mLeftF) + ", " + + String.valueOf(mTopF) + ", " + String.valueOf(mRightF) + ", " + + String.valueOf(mBottomF) + "\n"; + return str; + } +} diff --git a/src/com/android/inputmethod/pinyin/SoftKeyToggle.java b/src/com/android/inputmethod/pinyin/SoftKeyToggle.java new file mode 100644 index 0000000..89ff2fe --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SoftKeyToggle.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.graphics.drawable.Drawable; + +/** + * Class for soft keys which defined in the keyboard xml file. A soft key can be + * a basic key or a toggling key. + * + * @see com.android.inputmethod.pinyin.SoftKey + */ +public class SoftKeyToggle extends SoftKey { + /** + * The current state number is stored in the lowest 8 bits of mKeyMask, this + * mask is used to get the state number. If the current state is 0, the + * normal state is enabled; if the current state is more than 0, a toggle + * state in the toggle state chain will be enabled. + */ + private static final int KEYMASK_TOGGLE_STATE = 0x000000ff; + + private ToggleState mToggleState; + + public int getToggleStateId() { + return (mKeyMask & KEYMASK_TOGGLE_STATE); + } + + // The state id should be valid, and less than 255. + // If resetIfNotFound is true and there is no such toggle state with the + // given id, the key state will be reset. + // If the key state is newly changed (enabled to the given state, or + // reseted) and needs re-draw, return true. + public boolean enableToggleState(int stateId, boolean resetIfNotFound) { + int oldStateId = (mKeyMask & KEYMASK_TOGGLE_STATE); + if (oldStateId == stateId) return false; + + mKeyMask &= (~KEYMASK_TOGGLE_STATE); + if (stateId > 0) { + mKeyMask |= (KEYMASK_TOGGLE_STATE & stateId); + if (getToggleState() == null) { + mKeyMask &= (~KEYMASK_TOGGLE_STATE); + if (!resetIfNotFound && oldStateId > 0) { + mKeyMask |= (KEYMASK_TOGGLE_STATE & oldStateId); + } + return resetIfNotFound; + } else { + return true; + } + } else { + return true; + } + } + + // The state id should be valid, and less than 255. + // If resetIfNotFound is true and there is no such toggle state with the + // given id, the key state will be reset. + // If the key state is newly changed and needs re-draw, return true. + public boolean disableToggleState(int stateId, boolean resetIfNotFound) { + int oldStateId = (mKeyMask & KEYMASK_TOGGLE_STATE); + if (oldStateId == stateId) { + mKeyMask &= (~KEYMASK_TOGGLE_STATE); + return stateId != 0; + } + + if (resetIfNotFound) { + mKeyMask &= (~KEYMASK_TOGGLE_STATE); + return oldStateId != 0; + } + return false; + } + + // Clear any toggle state. If the key needs re-draw, return true. + public boolean disableAllToggleStates() { + int oldStateId = (mKeyMask & KEYMASK_TOGGLE_STATE); + mKeyMask &= (~KEYMASK_TOGGLE_STATE); + return oldStateId != 0; + } + + @Override + public Drawable getKeyIcon() { + ToggleState state = getToggleState(); + if (null != state) return state.mKeyIcon; + return super.getKeyIcon(); + } + + @Override + public Drawable getKeyIconPopup() { + ToggleState state = getToggleState(); + if (null != state) { + if (null != state.mKeyIconPopup) { + return state.mKeyIconPopup; + } else { + return state.mKeyIcon; + } + } + return super.getKeyIconPopup(); + } + + @Override + public int getKeyCode() { + ToggleState state = getToggleState(); + if (null != state) return state.mKeyCode; + return mKeyCode; + } + + @Override + public String getKeyLabel() { + ToggleState state = getToggleState(); + if (null != state) return state.mKeyLabel; + return mKeyLabel; + } + + @Override + public Drawable getKeyBg() { + ToggleState state = getToggleState(); + if (null != state && null != state.mKeyType) { + return state.mKeyType.mKeyBg; + } + return mKeyType.mKeyBg; + } + + @Override + public Drawable getKeyHlBg() { + ToggleState state = getToggleState(); + if (null != state && null != state.mKeyType) { + return state.mKeyType.mKeyHlBg; + } + return mKeyType.mKeyHlBg; + } + + @Override + public int getColor() { + ToggleState state = getToggleState(); + if (null != state && null != state.mKeyType) { + return state.mKeyType.mColor; + } + return mKeyType.mColor; + } + + @Override + public int getColorHl() { + ToggleState state = getToggleState(); + if (null != state && null != state.mKeyType) { + return state.mKeyType.mColorHl; + } + return mKeyType.mColorHl; + } + + @Override + public int getColorBalloon() { + ToggleState state = getToggleState(); + if (null != state && null != state.mKeyType) { + return state.mKeyType.mColorBalloon; + } + return mKeyType.mColorBalloon; + } + + @Override + public boolean isKeyCodeKey() { + ToggleState state = getToggleState(); + if (null != state) { + if (state.mKeyCode > 0) return true; + return false; + } + return super.isKeyCodeKey(); + } + + @Override + public boolean isUserDefKey() { + ToggleState state = getToggleState(); + if (null != state) { + if (state.mKeyCode < 0) return true; + return false; + } + return super.isUserDefKey(); + } + + @Override + public boolean isUniStrKey() { + ToggleState state = getToggleState(); + if (null != state) { + if (null != state.mKeyLabel && state.mKeyCode == 0) { + return true; + } + return false; + } + return super.isUniStrKey(); + } + + @Override + public boolean needBalloon() { + ToggleState state = getToggleState(); + if (null != state) { + return (state.mIdAndFlags & KEYMASK_BALLOON) != 0; + } + return super.needBalloon(); + } + + @Override + public boolean repeatable() { + ToggleState state = getToggleState(); + if (null != state) { + return (state.mIdAndFlags & KEYMASK_REPEAT) != 0; + } + return super.repeatable(); + } + + @Override + public void changeCase(boolean lowerCase) { + ToggleState state = getToggleState(); + if (null != state && null != state.mKeyLabel) { + if (lowerCase) + state.mKeyLabel = state.mKeyLabel.toLowerCase(); + else + state.mKeyLabel = state.mKeyLabel.toUpperCase(); + } + } + + public ToggleState createToggleState() { + return new ToggleState(); + } + + public boolean setToggleStates(ToggleState rootState) { + if (null == rootState) return false; + mToggleState = rootState; + return true; + } + + private ToggleState getToggleState() { + int stateId = (mKeyMask & KEYMASK_TOGGLE_STATE); + if (0 == stateId) return null; + + ToggleState state = mToggleState; + while ((null != state) + && (state.mIdAndFlags & KEYMASK_TOGGLE_STATE) != stateId) { + state = state.mNextState; + } + return state; + } + + public class ToggleState { + // The id should be bigger than 0; + private int mIdAndFlags; + public SoftKeyType mKeyType; + public int mKeyCode; + public Drawable mKeyIcon; + public Drawable mKeyIconPopup; + public String mKeyLabel; + public ToggleState mNextState; + + public void setStateId(int stateId) { + mIdAndFlags |= (stateId & KEYMASK_TOGGLE_STATE); + } + + public void setStateFlags(boolean repeat, boolean balloon) { + if (repeat) { + mIdAndFlags |= KEYMASK_REPEAT; + } else { + mIdAndFlags &= (~KEYMASK_REPEAT); + } + + if (balloon) { + mIdAndFlags |= KEYMASK_BALLOON; + } else { + mIdAndFlags &= (~KEYMASK_BALLOON); + } + } + } +} diff --git a/src/com/android/inputmethod/pinyin/SoftKeyboard.java b/src/com/android/inputmethod/pinyin/SoftKeyboard.java new file mode 100644 index 0000000..b8cc504 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SoftKeyboard.java @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import com.android.inputmethod.pinyin.InputModeSwitcher.ToggleStates; + +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.KeyEvent; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class used to represent a soft keyboard definition, including the height, the + * background image, the image for high light, the keys, etc. + */ +public class SoftKeyboard { + /** The XML resource id for this soft keyboard. */ + private int mSkbXmlId; + + /** Do we need to cache this soft keyboard? */ + private boolean mCacheFlag; + + /** + * After user switches to this soft keyboard, if this flag is true, this + * soft keyboard will be kept unless explicit switching operation is + * performed, otherwise IME will switch back to the previous keyboard layout + * whenever user clicks on any none-function key. + **/ + private boolean mStickyFlag; + + /** + * The cache id for this soft keyboard. It is used to identify it in the + * soft keyboard pool. + */ + private int mCacheId; + + /** + * Used to indicate whether this soft keyboard is newly loaded from an XML + * file or is just gotten from the soft keyboard pool. + */ + private boolean mNewlyLoadedFlag = true; + + /** The width of the soft keyboard. */ + private int mSkbCoreWidth; + + /** The height of the soft keyboard. */ + private int mSkbCoreHeight; + + /** The soft keyboard template for this soft keyboard. */ + private SkbTemplate mSkbTemplate; + + /** Used to indicate whether this soft keyboard is a QWERTY keyboard. */ + private boolean mIsQwerty; + + /** + * When {@link #mIsQwerty} is true, this member is Used to indicate that the + * soft keyboard should be displayed in uppercase. + */ + private boolean mIsQwertyUpperCase; + + /** + * The id of the rows which are enabled. Rows with id + * {@link KeyRow#ALWAYS_SHOW_ROW_ID} are always enabled. + */ + private int mEnabledRowId; + + /** + * Rows in this soft keyboard. Each row has a id. Only matched rows will be + * enabled. + */ + private List mKeyRows; + + /** + * Background of the soft keyboard. If it is null, the one in the soft + * keyboard template will be used. + **/ + public Drawable mSkbBg; + + /** + * Background for key balloon. If it is null, the one in the soft keyboard + * template will be used. + **/ + private Drawable mBalloonBg; + + /** + * Background for popup mini soft keyboard. If it is null, the one in the + * soft keyboard template will be used. + **/ + private Drawable mPopupBg; + + /** The left and right margin of a key. */ + private float mKeyXMargin = 0; + + /** The top and bottom margin of a key. */ + private float mKeyYMargin = 0; + + private Rect mTmpRect = new Rect(); + + public SoftKeyboard(int skbXmlId, SkbTemplate skbTemplate, int skbWidth, + int skbHeight) { + mSkbXmlId = skbXmlId; + mSkbTemplate = skbTemplate; + mSkbCoreWidth = skbWidth; + mSkbCoreHeight = skbHeight; + } + + public void setFlags(boolean cacheFlag, boolean stickyFlag, + boolean isQwerty, boolean isQwertyUpperCase) { + mCacheFlag = cacheFlag; + mStickyFlag = stickyFlag; + mIsQwerty = isQwerty; + mIsQwertyUpperCase = isQwertyUpperCase; + } + + public boolean getCacheFlag() { + return mCacheFlag; + } + + public void setCacheId(int cacheId) { + mCacheId = cacheId; + } + + public boolean getStickyFlag() { + return mStickyFlag; + } + + public void setSkbBackground(Drawable skbBg) { + mSkbBg = skbBg; + } + + public void setPopupBackground(Drawable popupBg) { + mPopupBg = popupBg; + } + + public void setKeyBalloonBackground(Drawable balloonBg) { + mBalloonBg = balloonBg; + } + + public void setKeyMargins(float xMargin, float yMargin) { + mKeyXMargin = xMargin; + mKeyYMargin = yMargin; + } + + public int getCacheId() { + return mCacheId; + } + + public void reset() { + if (null != mKeyRows) mKeyRows.clear(); + } + + public void setNewlyLoadedFlag(boolean newlyLoadedFlag) { + mNewlyLoadedFlag = newlyLoadedFlag; + } + + public boolean getNewlyLoadedFlag() { + return mNewlyLoadedFlag; + } + + public void beginNewRow(int rowId, float yStartingPos) { + if (null == mKeyRows) mKeyRows = new ArrayList(); + KeyRow keyRow = new KeyRow(); + keyRow.mRowId = rowId; + keyRow.mTopF = yStartingPos; + keyRow.mBottomF = yStartingPos; + keyRow.mSoftKeys = new ArrayList(); + mKeyRows.add(keyRow); + } + + public boolean addSoftKey(SoftKey softKey) { + if (mKeyRows.size() == 0) return false; + KeyRow keyRow = mKeyRows.get(mKeyRows.size() - 1); + if (null == keyRow) return false; + List softKeys = keyRow.mSoftKeys; + + softKey.setSkbCoreSize(mSkbCoreWidth, mSkbCoreHeight); + softKeys.add(softKey); + if (softKey.mTopF < keyRow.mTopF) { + keyRow.mTopF = softKey.mTopF; + } + if (softKey.mBottomF > keyRow.mBottomF) { + keyRow.mBottomF = softKey.mBottomF; + } + return true; + } + + public int getSkbXmlId() { + return mSkbXmlId; + } + + // Set the size of the soft keyboard core. In other words, the background's + // padding are not counted. + public void setSkbCoreSize(int skbCoreWidth, int skbCoreHeight) { + if (null == mKeyRows + || (skbCoreWidth == mSkbCoreWidth && skbCoreHeight == mSkbCoreHeight)) { + return; + } + for (int row = 0; row < mKeyRows.size(); row++) { + KeyRow keyRow = mKeyRows.get(row); + keyRow.mBottom = (int) (skbCoreHeight * keyRow.mBottomF); + keyRow.mTop = (int) (skbCoreHeight * keyRow.mTopF); + + List softKeys = keyRow.mSoftKeys; + for (int i = 0; i < softKeys.size(); i++) { + SoftKey softKey = softKeys.get(i); + softKey.setSkbCoreSize(skbCoreWidth, skbCoreHeight); + } + } + mSkbCoreWidth = skbCoreWidth; + mSkbCoreHeight = skbCoreHeight; + } + + public int getSkbCoreWidth() { + return mSkbCoreWidth; + } + + public int getSkbCoreHeight() { + return mSkbCoreHeight; + } + + public int getSkbTotalWidth() { + Rect padding = getPadding(); + return mSkbCoreWidth + padding.left + padding.right; + } + + public int getSkbTotalHeight() { + Rect padding = getPadding(); + return mSkbCoreHeight + padding.top + padding.bottom; + } + + public int getKeyXMargin() { + Environment env = Environment.getInstance(); + return (int) (mKeyXMargin * mSkbCoreWidth * env.getKeyXMarginFactor()); + } + + public int getKeyYMargin() { + Environment env = Environment.getInstance(); + return (int) (mKeyYMargin * mSkbCoreHeight * env.getKeyYMarginFactor()); + } + + public Drawable getSkbBackground() { + if (null != mSkbBg) return mSkbBg; + return mSkbTemplate.getSkbBackground(); + } + + public Drawable getBalloonBackground() { + if (null != mBalloonBg) return mBalloonBg; + return mSkbTemplate.getBalloonBackground(); + } + + public Drawable getPopupBackground() { + if (null != mPopupBg) return mPopupBg; + return mSkbTemplate.getPopupBackground(); + } + + public int getRowNum() { + if (null != mKeyRows) { + return mKeyRows.size(); + } + return 0; + } + + public KeyRow getKeyRowForDisplay(int row) { + if (null != mKeyRows && mKeyRows.size() > row) { + KeyRow keyRow = mKeyRows.get(row); + if (KeyRow.ALWAYS_SHOW_ROW_ID == keyRow.mRowId + || keyRow.mRowId == mEnabledRowId) { + return keyRow; + } + } + return null; + } + + public SoftKey getKey(int row, int location) { + if (null != mKeyRows && mKeyRows.size() > row) { + List softKeys = mKeyRows.get(row).mSoftKeys; + if (softKeys.size() > location) { + return softKeys.get(location); + } + } + return null; + } + + public SoftKey mapToKey(int x, int y) { + if (null == mKeyRows) { + return null; + } + // If the position is inside the rectangle of a certain key, return that + // key. + int rowNum = mKeyRows.size(); + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mKeyRows.get(row); + if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId + && keyRow.mRowId != mEnabledRowId) continue; + if (keyRow.mTop > y && keyRow.mBottom <= y) continue; + + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int i = 0; i < keyNum; i++) { + SoftKey sKey = softKeys.get(i); + if (sKey.mLeft <= x && sKey.mTop <= y && sKey.mRight > x + && sKey.mBottom > y) { + return sKey; + } + } + } + + // If the position is outside the rectangles of all keys, find the + // nearest one. + SoftKey nearestKey = null; + float nearestDis = Float.MAX_VALUE; + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mKeyRows.get(row); + if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId + && keyRow.mRowId != mEnabledRowId) continue; + if (keyRow.mTop > y && keyRow.mBottom <= y) continue; + + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int i = 0; i < keyNum; i++) { + SoftKey sKey = softKeys.get(i); + int disx = (sKey.mLeft + sKey.mRight) / 2 - x; + int disy = (sKey.mTop + sKey.mBottom) / 2 - y; + float dis = disx * disx + disy * disy; + if (dis < nearestDis) { + nearestDis = dis; + nearestKey = sKey; + } + } + } + return nearestKey; + } + + public void switchQwertyMode(int toggle_state_id, boolean upperCase) { + if (!mIsQwerty) return; + + int rowNum = mKeyRows.size(); + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mKeyRows.get(row); + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int i = 0; i < keyNum; i++) { + SoftKey sKey = softKeys.get(i); + if (sKey instanceof SoftKeyToggle) { + ((SoftKeyToggle) sKey).enableToggleState(toggle_state_id, + true); + } + if (sKey.mKeyCode >= KeyEvent.KEYCODE_A + && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) { + sKey.changeCase(upperCase); + } + } + } + } + + public void enableToggleState(int toggleStateId, boolean resetIfNotFound) { + int rowNum = mKeyRows.size(); + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mKeyRows.get(row); + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int i = 0; i < keyNum; i++) { + SoftKey sKey = softKeys.get(i); + if (sKey instanceof SoftKeyToggle) { + ((SoftKeyToggle) sKey).enableToggleState(toggleStateId, + resetIfNotFound); + } + } + } + } + + public void disableToggleState(int toggleStateId, boolean resetIfNotFound) { + int rowNum = mKeyRows.size(); + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mKeyRows.get(row); + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int i = 0; i < keyNum; i++) { + SoftKey sKey = softKeys.get(i); + if (sKey instanceof SoftKeyToggle) { + ((SoftKeyToggle) sKey).disableToggleState(toggleStateId, + resetIfNotFound); + } + } + } + } + + public void enableToggleStates(ToggleStates toggleStates) { + if (null == toggleStates) return; + + enableRow(toggleStates.mRowIdToEnable); + + boolean isQwerty = toggleStates.mQwerty; + boolean isQwertyUpperCase = toggleStates.mQwertyUpperCase; + boolean needUpdateQwerty = (isQwerty && mIsQwerty && (mIsQwertyUpperCase != isQwertyUpperCase)); + int states[] = toggleStates.mKeyStates; + int statesNum = toggleStates.mKeyStatesNum; + + int rowNum = mKeyRows.size(); + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mKeyRows.get(row); + if (KeyRow.ALWAYS_SHOW_ROW_ID != keyRow.mRowId + && keyRow.mRowId != mEnabledRowId) { + continue; + } + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int keyPos = 0; keyPos < keyNum; keyPos++) { + SoftKey sKey = softKeys.get(keyPos); + if (sKey instanceof SoftKeyToggle) { + for (int statePos = 0; statePos < statesNum; statePos++) { + ((SoftKeyToggle) sKey).enableToggleState( + states[statePos], statePos == 0); + } + if (0 == statesNum) { + ((SoftKeyToggle) sKey).disableAllToggleStates(); + } + } + if (needUpdateQwerty) { + if (sKey.mKeyCode >= KeyEvent.KEYCODE_A + && sKey.mKeyCode <= KeyEvent.KEYCODE_Z) { + sKey.changeCase(isQwertyUpperCase); + } + } + } + } + mIsQwertyUpperCase = isQwertyUpperCase; + } + + private Rect getPadding() { + mTmpRect.set(0, 0, 0, 0); + Drawable skbBg = getSkbBackground(); + if (null == skbBg) return mTmpRect; + skbBg.getPadding(mTmpRect); + return mTmpRect; + } + + /** + * Enable a row with the give toggle Id. Rows with other toggle ids (except + * the id {@link KeyRow#ALWAYS_SHOW_ROW_ID}) will be disabled. + * + * @param rowId The row id to enable. + * @return True if the soft keyboard requires redrawing. + */ + private boolean enableRow(int rowId) { + if (KeyRow.ALWAYS_SHOW_ROW_ID == rowId) return false; + + boolean enabled = false; + int rowNum = mKeyRows.size(); + for (int row = rowNum - 1; row >= 0; row--) { + if (mKeyRows.get(row).mRowId == rowId) { + enabled = true; + break; + } + } + if (enabled) { + mEnabledRowId = rowId; + } + return enabled; + } + + @Override + public String toString() { + String str = "------------------SkbInfo----------------------\n"; + String endStr = "-----------------------------------------------\n"; + str += "Width: " + String.valueOf(mSkbCoreWidth) + "\n"; + str += "Height: " + String.valueOf(mSkbCoreHeight) + "\n"; + str += "KeyRowNum: " + mKeyRows == null ? "0" : String.valueOf(mKeyRows + .size()) + + "\n"; + if (null == mKeyRows) return str + endStr; + int rowNum = mKeyRows.size(); + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mKeyRows.get(row); + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int i = 0; i < softKeys.size(); i++) { + str += "-key " + String.valueOf(i) + ":" + + softKeys.get(i).toString(); + } + } + return str + endStr; + } + + public String toShortString() { + return super.toString(); + } + + class KeyRow { + static final int ALWAYS_SHOW_ROW_ID = -1; + static final int DEFAULT_ROW_ID = 0; + + List mSoftKeys; + /** + * If the row id is {@link #ALWAYS_SHOW_ROW_ID}, this row will always be + * enabled. + */ + int mRowId; + float mTopF; + float mBottomF; + int mTop; + int mBottom; + } +} diff --git a/src/com/android/inputmethod/pinyin/SoftKeyboardView.java b/src/com/android/inputmethod/pinyin/SoftKeyboardView.java new file mode 100644 index 0000000..5543f33 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SoftKeyboardView.java @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow; + +import java.util.List; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Paint.FontMetricsInt; +import android.graphics.drawable.Drawable; +import android.os.Vibrator; +import android.util.AttributeSet; +import android.view.View; + +/** + * Class used to show a soft keyboard. + * + * A soft keyboard view should not handle touch event itself, because we do bias + * correction, need a global strategy to map an event into a proper view to + * achieve better user experience. + */ +public class SoftKeyboardView extends View { + /** + * The definition of the soft keyboard for the current this soft keyboard + * view. + */ + private SoftKeyboard mSoftKeyboard; + + /** + * The popup balloon hint for key press/release. + */ + private BalloonHint mBalloonPopup; + + /** + * The on-key balloon hint for key press/release. If it is null, on-key + * highlight will be drawn on th soft keyboard view directly. + */ + private BalloonHint mBalloonOnKey; + + /** Used to play key sounds. */ + private SoundManager mSoundManager; + + /** The last key pressed. */ + private SoftKey mSoftKeyDown; + + /** Used to indicate whether the user is holding on a key. */ + private boolean mKeyPressed = false; + + /** + * The location offset of the view to the keyboard container. + */ + private int mOffsetToSkbContainer[] = new int[2]; + + /** + * The location of the desired hint view to the keyboard container. + */ + private int mHintLocationToSkbContainer[] = new int[2]; + + /** + * Text size for normal key. + */ + private int mNormalKeyTextSize; + + /** + * Text size for function key. + */ + private int mFunctionKeyTextSize; + + /** + * Long press timer used to response long-press. + */ + private SkbContainer.LongPressTimer mLongPressTimer; + + /** + * Repeated events for long press + */ + private boolean mRepeatForLongPress = false; + + /** + * If this parameter is true, the balloon will never be dismissed even if + * user moves a lot from the pressed point. + */ + private boolean mMovingNeverHidePopupBalloon = false; + + /** Vibration for key press. */ + private Vibrator mVibrator; + + /** Vibration pattern for key press. */ + protected long[] mVibratePattern = new long[] {1, 20}; + + /** + * The dirty rectangle used to mark the area to re-draw during key press and + * release. Currently, whenever we can invalidate(Rect), view will call + * onDraw() and we MUST draw the whole view. This dirty information is for + * future use. + */ + private Rect mDirtyRect = new Rect(); + + private Paint mPaint; + private FontMetricsInt mFmi; + private boolean mDimSkb; + + public SoftKeyboardView(Context context, AttributeSet attrs) { + super(context, attrs); + + mSoundManager = SoundManager.getInstance(mContext); + + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mFmi = mPaint.getFontMetricsInt(); + } + + public boolean setSoftKeyboard(SoftKeyboard softSkb) { + if (null == softSkb) { + return false; + } + mSoftKeyboard = softSkb; + Drawable bg = softSkb.getSkbBackground(); + if (null != bg) setBackgroundDrawable(bg); + return true; + } + + public SoftKeyboard getSoftKeyboard() { + return mSoftKeyboard; + } + + public void resizeKeyboard(int skbWidth, int skbHeight) { + mSoftKeyboard.setSkbCoreSize(skbWidth, skbHeight); + } + + public void setBalloonHint(BalloonHint balloonOnKey, + BalloonHint balloonPopup, boolean movingNeverHidePopup) { + mBalloonOnKey = balloonOnKey; + mBalloonPopup = balloonPopup; + mMovingNeverHidePopupBalloon = movingNeverHidePopup; + } + + public void setOffsetToSkbContainer(int offsetToSkbContainer[]) { + mOffsetToSkbContainer[0] = offsetToSkbContainer[0]; + mOffsetToSkbContainer[1] = offsetToSkbContainer[1]; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int measuredWidth = 0; + int measuredHeight = 0; + if (null != mSoftKeyboard) { + measuredWidth = mSoftKeyboard.getSkbCoreWidth(); + measuredHeight = mSoftKeyboard.getSkbCoreHeight(); + measuredWidth += mPaddingLeft + mPaddingRight; + measuredHeight += mPaddingTop + mPaddingBottom; + } + setMeasuredDimension(measuredWidth, measuredHeight); + } + + private void showBalloon(BalloonHint balloon, int balloonLocationToSkb[], + boolean movePress) { + long delay = BalloonHint.TIME_DELAY_SHOW; + if (movePress) delay = 0; + if (balloon.needForceDismiss()) { + balloon.delayedDismiss(0); + } + if (!balloon.isShowing()) { + balloon.delayedShow(delay, balloonLocationToSkb); + } else { + balloon.delayedUpdate(delay, balloonLocationToSkb, balloon + .getWidth(), balloon.getHeight()); + } + long b = System.currentTimeMillis(); + } + + public void resetKeyPress(long balloonDelay) { + if (!mKeyPressed) return; + mKeyPressed = false; + if (null != mBalloonOnKey) { + mBalloonOnKey.delayedDismiss(balloonDelay); + } else { + if (null != mSoftKeyDown) { + if (mDirtyRect.isEmpty()) { + mDirtyRect.set(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, + mSoftKeyDown.mRight, mSoftKeyDown.mBottom); + } + invalidate(mDirtyRect); + } else { + invalidate(); + } + } + mBalloonPopup.delayedDismiss(balloonDelay); + } + + // If movePress is true, means that this function is called because user + // moves his finger to this button. If movePress is false, means that this + // function is called when user just presses this key. + public SoftKey onKeyPress(int x, int y, + SkbContainer.LongPressTimer longPressTimer, boolean movePress) { + mKeyPressed = false; + boolean moveWithinPreviousKey = false; + if (movePress) { + SoftKey newKey = mSoftKeyboard.mapToKey(x, y); + if (newKey == mSoftKeyDown) moveWithinPreviousKey = true; + mSoftKeyDown = newKey; + } else { + mSoftKeyDown = mSoftKeyboard.mapToKey(x, y); + } + if (moveWithinPreviousKey || null == mSoftKeyDown) return mSoftKeyDown; + mKeyPressed = true; + + if (!movePress) { + tryPlayKeyDown(); + tryVibrate(); + } + + mLongPressTimer = longPressTimer; + + if (!movePress) { + if (mSoftKeyDown.getPopupResId() > 0 || mSoftKeyDown.repeatable()) { + mLongPressTimer.startTimer(); + } + } else { + mLongPressTimer.removeTimer(); + } + + int desired_width; + int desired_height; + float textSize; + Environment env = Environment.getInstance(); + + if (null != mBalloonOnKey) { + Drawable keyHlBg = mSoftKeyDown.getKeyHlBg(); + mBalloonOnKey.setBalloonBackground(keyHlBg); + + // Prepare the on-key balloon + int keyXMargin = mSoftKeyboard.getKeyXMargin(); + int keyYMargin = mSoftKeyboard.getKeyYMargin(); + desired_width = mSoftKeyDown.width() - 2 * keyXMargin; + desired_height = mSoftKeyDown.height() - 2 * keyYMargin; + textSize = env + .getKeyTextSize(SoftKeyType.KEYTYPE_ID_NORMAL_KEY != mSoftKeyDown.mKeyType.mKeyTypeId); + Drawable icon = mSoftKeyDown.getKeyIcon(); + if (null != icon) { + mBalloonOnKey.setBalloonConfig(icon, desired_width, + desired_height); + } else { + mBalloonOnKey.setBalloonConfig(mSoftKeyDown.getKeyLabel(), + textSize, true, mSoftKeyDown.getColorHl(), + desired_width, desired_height); + } + + mHintLocationToSkbContainer[0] = mPaddingLeft + mSoftKeyDown.mLeft + - (mBalloonOnKey.getWidth() - mSoftKeyDown.width()) / 2; + mHintLocationToSkbContainer[0] += mOffsetToSkbContainer[0]; + mHintLocationToSkbContainer[1] = mPaddingTop + + (mSoftKeyDown.mBottom - keyYMargin) + - mBalloonOnKey.getHeight(); + mHintLocationToSkbContainer[1] += mOffsetToSkbContainer[1]; + showBalloon(mBalloonOnKey, mHintLocationToSkbContainer, movePress); + } else { + mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, + mSoftKeyDown.mRight, mSoftKeyDown.mBottom); + invalidate(mDirtyRect); + } + + // Prepare the popup balloon + if (mSoftKeyDown.needBalloon()) { + Drawable balloonBg = mSoftKeyboard.getBalloonBackground(); + mBalloonPopup.setBalloonBackground(balloonBg); + + desired_width = mSoftKeyDown.width() + env.getKeyBalloonWidthPlus(); + desired_height = mSoftKeyDown.height() + + env.getKeyBalloonHeightPlus(); + textSize = env + .getBalloonTextSize(SoftKeyType.KEYTYPE_ID_NORMAL_KEY != mSoftKeyDown.mKeyType.mKeyTypeId); + Drawable iconPopup = mSoftKeyDown.getKeyIconPopup(); + if (null != iconPopup) { + mBalloonPopup.setBalloonConfig(iconPopup, desired_width, + desired_height); + } else { + mBalloonPopup.setBalloonConfig(mSoftKeyDown.getKeyLabel(), + textSize, mSoftKeyDown.needBalloon(), mSoftKeyDown + .getColorBalloon(), desired_width, + desired_height); + } + + // The position to show. + mHintLocationToSkbContainer[0] = mPaddingLeft + mSoftKeyDown.mLeft + + -(mBalloonPopup.getWidth() - mSoftKeyDown.width()) / 2; + mHintLocationToSkbContainer[0] += mOffsetToSkbContainer[0]; + mHintLocationToSkbContainer[1] = mPaddingTop + mSoftKeyDown.mTop + - mBalloonPopup.getHeight(); + mHintLocationToSkbContainer[1] += mOffsetToSkbContainer[1]; + showBalloon(mBalloonPopup, mHintLocationToSkbContainer, movePress); + } else { + mBalloonPopup.delayedDismiss(0); + } + + if (mRepeatForLongPress) longPressTimer.startTimer(); + return mSoftKeyDown; + } + + public SoftKey onKeyRelease(int x, int y) { + mKeyPressed = false; + if (null == mSoftKeyDown) return null; + + mLongPressTimer.removeTimer(); + + if (null != mBalloonOnKey) { + mBalloonOnKey.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS); + } else { + mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, + mSoftKeyDown.mRight, mSoftKeyDown.mBottom); + invalidate(mDirtyRect); + } + + if (mSoftKeyDown.needBalloon()) { + mBalloonPopup.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS); + } + + if (mSoftKeyDown.moveWithinKey(x - mPaddingLeft, y - mPaddingTop)) { + return mSoftKeyDown; + } + return null; + } + + public SoftKey onKeyMove(int x, int y) { + if (null == mSoftKeyDown) return null; + + if (mSoftKeyDown.moveWithinKey(x - mPaddingLeft, y - mPaddingTop)) { + return mSoftKeyDown; + } + + // The current key needs to be updated. + mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop, + mSoftKeyDown.mRight, mSoftKeyDown.mBottom); + + if (mRepeatForLongPress) { + if (mMovingNeverHidePopupBalloon) { + return onKeyPress(x, y, mLongPressTimer, true); + } + + if (null != mBalloonOnKey) { + mBalloonOnKey.delayedDismiss(0); + } else { + invalidate(mDirtyRect); + } + + if (mSoftKeyDown.needBalloon()) { + mBalloonPopup.delayedDismiss(0); + } + + if (null != mLongPressTimer) { + mLongPressTimer.removeTimer(); + } + return onKeyPress(x, y, mLongPressTimer, true); + } else { + // When user moves between keys, repeated response is disabled. + return onKeyPress(x, y, mLongPressTimer, true); + } + } + + private void tryVibrate() { + if (!Settings.getVibrate()) { + return; + } + if (mVibrator == null) { + mVibrator = new Vibrator(); + } + mVibrator.vibrate(mVibratePattern, -1); + } + + private void tryPlayKeyDown() { + if (Settings.getKeySound()) { + mSoundManager.playKeyDown(); + } + } + + public void dimSoftKeyboard(boolean dimSkb) { + mDimSkb = dimSkb; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (null == mSoftKeyboard) return; + + canvas.translate(mPaddingLeft, mPaddingTop); + + Environment env = Environment.getInstance(); + mNormalKeyTextSize = env.getKeyTextSize(false); + mFunctionKeyTextSize = env.getKeyTextSize(true); + // Draw the last soft keyboard + int rowNum = mSoftKeyboard.getRowNum(); + int keyXMargin = mSoftKeyboard.getKeyXMargin(); + int keyYMargin = mSoftKeyboard.getKeyYMargin(); + for (int row = 0; row < rowNum; row++) { + KeyRow keyRow = mSoftKeyboard.getKeyRowForDisplay(row); + if (null == keyRow) continue; + List softKeys = keyRow.mSoftKeys; + int keyNum = softKeys.size(); + for (int i = 0; i < keyNum; i++) { + SoftKey softKey = softKeys.get(i); + if (SoftKeyType.KEYTYPE_ID_NORMAL_KEY == softKey.mKeyType.mKeyTypeId) { + mPaint.setTextSize(mNormalKeyTextSize); + } else { + mPaint.setTextSize(mFunctionKeyTextSize); + } + drawSoftKey(canvas, softKey, keyXMargin, keyYMargin); + } + } + + if (mDimSkb) { + mPaint.setColor(0xa0000000); + canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); + } + + mDirtyRect.setEmpty(); + } + + private void drawSoftKey(Canvas canvas, SoftKey softKey, int keyXMargin, + int keyYMargin) { + Drawable bg; + int textColor; + if (mKeyPressed && softKey == mSoftKeyDown) { + bg = softKey.getKeyHlBg(); + textColor = softKey.getColorHl(); + } else { + bg = softKey.getKeyBg(); + textColor = softKey.getColor(); + } + + if (null != bg) { + bg.setBounds(softKey.mLeft + keyXMargin, softKey.mTop + keyYMargin, + softKey.mRight - keyXMargin, softKey.mBottom - keyYMargin); + bg.draw(canvas); + } + + String keyLabel = softKey.getKeyLabel(); + Drawable keyIcon = softKey.getKeyIcon(); + if (null != keyIcon) { + Drawable icon = keyIcon; + int marginLeft = (softKey.width() - icon.getIntrinsicWidth()) / 2; + int marginRight = softKey.width() - icon.getIntrinsicWidth() + - marginLeft; + int marginTop = (softKey.height() - icon.getIntrinsicHeight()) / 2; + int marginBottom = softKey.height() - icon.getIntrinsicHeight() + - marginTop; + icon.setBounds(softKey.mLeft + marginLeft, + softKey.mTop + marginTop, softKey.mRight - marginRight, + softKey.mBottom - marginBottom); + icon.draw(canvas); + } else if (null != keyLabel) { + mPaint.setColor(textColor); + float x = softKey.mLeft + + (softKey.width() - mPaint.measureText(keyLabel)) / 2.0f; + int fontHeight = mFmi.bottom - mFmi.top; + float marginY = (softKey.height() - fontHeight) / 2.0f; + float y = softKey.mTop + marginY - mFmi.top + mFmi.bottom / 1.5f; + canvas.drawText(keyLabel, x, y + 1, mPaint); + } + } +} diff --git a/src/com/android/inputmethod/pinyin/SoundManager.java b/src/com/android/inputmethod/pinyin/SoundManager.java new file mode 100644 index 0000000..82be407 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/SoundManager.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import android.content.Context; +import android.media.AudioManager; + +/** + * Class used to manage related sound resources. + */ +public class SoundManager { + private static SoundManager mInstance = null; + private Context mContext; + private AudioManager mAudioManager; + private final float FX_VOLUME = 1.0f; + private boolean mSilentMode; + + private SoundManager(Context context) { + mContext = context; + updateRingerMode(); + } + + public void updateRingerMode() { + if (mAudioManager == null) { + mAudioManager = (AudioManager) mContext + .getSystemService(Context.AUDIO_SERVICE); + } + mSilentMode = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL); + } + + public static SoundManager getInstance(Context context) { + if (null == mInstance) { + if (null != context) { + mInstance = new SoundManager(context); + } + } + return mInstance; + } + + public void playKeyDown() { + if (mAudioManager == null) { + updateRingerMode(); + } + if (!mSilentMode) { + int sound = AudioManager.FX_KEYPRESS_STANDARD; + mAudioManager.playSoundEffect(sound, FX_VOLUME); + } + } +} diff --git a/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java b/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java new file mode 100644 index 0000000..fd192a3 --- /dev/null +++ b/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java @@ -0,0 +1,835 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.pinyin; + +import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; + +import java.io.IOException; +import java.util.regex.Pattern; + +import org.xmlpull.v1.XmlPullParserException; + +/** + * Class used to load a soft keyboard or a soft keyboard template from xml + * files. + */ +public class XmlKeyboardLoader { + /** + * The tag used to define an xml-based soft keyboard template. + */ + private static final String XMLTAG_SKB_TEMPLATE = "skb_template"; + + /** + * The tag used to indicate the soft key type which is defined inside the + * {@link #XMLTAG_SKB_TEMPLATE} element in the xml file. file. + */ + private static final String XMLTAG_KEYTYPE = "key_type"; + + /** + * The tag used to define a default key icon for enter/delete/space keys. It + * is defined inside the {@link #XMLTAG_SKB_TEMPLATE} element in the xml + * file. + */ + private static final String XMLTAG_KEYICON = "key_icon"; + + /** + * Attribute tag of the left and right margin for a key. A key's width + * should be larger than double of this value. Defined inside + * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. + */ + private static final String XMLATTR_KEY_XMARGIN = "key_xmargin"; + + /** + * Attribute tag of the top and bottom margin for a key. A key's height + * should be larger than double of this value. Defined inside + * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. + */ + private static final String XMLATTR_KEY_YMARGIN = "key_ymargin"; + + /** + * Attribute tag of the keyboard background image. Defined inside + * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. + */ + private static final String XMLATTR_SKB_BG = "skb_bg"; + + /** + * Attribute tag of the balloon background image for key press. Defined + * inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}. + */ + private static final String XMLATTR_BALLOON_BG = "balloon_bg"; + + /** + * Attribute tag of the popup balloon background image for key press or + * popup mini keyboard. Defined inside {@link #XMLTAG_SKB_TEMPLATE} and + * {@link #XMLTAG_KEYBOARD}. + */ + private static final String XMLATTR_POPUP_BG = "popup_bg"; + + /** + * Attribute tag of the color to draw key label. Defined inside + * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}. + */ + private static final String XMLATTR_COLOR = "color"; + + /** + * Attribute tag of the color to draw key's highlighted label. Defined + * inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}. + */ + private static final String XMLATTR_COLOR_HIGHLIGHT = "color_highlight"; + + /** + * Attribute tag of the color to draw key's label in the popup balloon. + * Defined inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}. + */ + private static final String XMLATTR_COLOR_BALLOON = "color_balloon"; + + /** + * Attribute tag of the id of {@link #XMLTAG_KEYTYPE} and + * {@link #XMLTAG_KEY}. Key types and keys defined in a soft keyboard + * template should have id, because a soft keyboard needs the id to refer to + * these default definitions. If a key defined in {@link #XMLTAG_KEYBOARD} + * does not id, that means the key is newly defined; if it has id (and only + * has id), the id is used to find the default definition from the soft + * keyboard template. + */ + private static final String XMLATTR_ID = "id"; + + /** + * Attribute tag of the key background for a specified key type. Defined + * inside {@link #XMLTAG_KEYTYPE}. + */ + private static final String XMLATTR_KEYTYPE_BG = "bg"; + + /** + * Attribute tag of the key high-light background for a specified key type. + * Defined inside {@link #XMLTAG_KEYTYPE}. + */ + private static final String XMLATTR_KEYTYPE_HLBG = "hlbg"; + + /** + * Attribute tag of the starting x-position of an element. It can be defined + * in {@link #XMLTAG_ROW} and {@link #XMLTAG_KEY} in {XMLTAG_SKB_TEMPLATE}. + * If not defined, 0 will be used. For a key defined in + * {@link #XMLTAG_KEYBOARD}, it always use its previous keys information to + * calculate its own position. + */ + private static final String XMLATTR_START_POS_X = "start_pos_x"; + + /** + * Attribute tag of the starting y-position of an element. It can be defined + * in {@link #XMLTAG_ROW} and {@link #XMLTAG_KEY} in {XMLTAG_SKB_TEMPLATE}. + * If not defined, 0 will be used. For a key defined in + * {@link #XMLTAG_KEYBOARD}, it always use its previous keys information to + * calculate its own position. + */ + private static final String XMLATTR_START_POS_Y = "start_pos_y"; + + /** + * Attribute tag of a row's id. Defined {@link #XMLTAG_ROW}. If not defined, + * -1 will be used. Rows with id -1 will be enabled always, rows with same + * row id will be enabled when the id is the same to the activated id of the + * soft keyboard. + */ + private static final String XMLATTR_ROW_ID = "row_id"; + + /** The tag used to indicate the keyboard element in the xml file. */ + private static final String XMLTAG_KEYBOARD = "keyboard"; + + /** The tag used to indicate the row element in the xml file. */ + private static final String XMLTAG_ROW = "row"; + + /** The tag used to indicate key-array element in the xml file. */ + private static final String XMLTAG_KEYS = "keys"; + + /** + * The tag used to indicate a key element in the xml file. If the element is + * defined in a soft keyboard template, it should have an id. If it is + * defined in a soft keyboard, id is not required. + */ + private static final String XMLTAG_KEY = "key"; + + /** The tag used to indicate a key's toggle element in the xml file. */ + private static final String XMLTAG_TOGGLE_STATE = "toggle_state"; + + /** + * Attribute tag of the toggle state id for toggle key. Defined inside + * {@link #XMLTAG_TOGGLE_STATE} + */ + private static final String XMLATTR_TOGGLE_STATE_ID = "state_id"; + + /** Attribute tag of key template for the soft keyboard. */ + private static final String XMLATTR_SKB_TEMPLATE = "skb_template"; + + /** + * Attribute tag used to indicate whether this soft keyboard needs to be + * cached in memory for future use. {@link #DEFAULT_SKB_CACHE_FLAG} + * specifies the default value. + */ + private static final String XMLATTR_SKB_CACHE_FLAG = "skb_cache_flag"; + + /** + * Attribute tag used to indicate whether this soft keyboard is sticky. A + * sticky soft keyboard will keep the current layout unless user makes a + * switch explicitly. A none sticky soft keyboard will automatically goes + * back to the previous keyboard after click a none-function key. + * {@link #DEFAULT_SKB_STICKY_FLAG} specifies the default value. + */ + private static final String XMLATTR_SKB_STICKY_FLAG = "skb_sticky_flag"; + + /** Attribute tag to indicate whether it is a QWERTY soft keyboard. */ + private static final String XMLATTR_QWERTY = "qwerty"; + + /** + * When the soft keyboard is a QWERTY one, this attribute tag to get the + * information that whether it is defined in upper case. + */ + private static final String XMLATTR_QWERTY_UPPERCASE = "qwerty_uppercase"; + + /** Attribute tag of key type. */ + private static final String XMLATTR_KEY_TYPE = "key_type"; + + /** Attribute tag of key width. */ + private static final String XMLATTR_KEY_WIDTH = "width"; + + /** Attribute tag of key height. */ + private static final String XMLATTR_KEY_HEIGHT = "height"; + + /** Attribute tag of the key's repeating ability. */ + private static final String XMLATTR_KEY_REPEAT = "repeat"; + + /** Attribute tag of the key's behavior for balloon. */ + private static final String XMLATTR_KEY_BALLOON = "balloon"; + + /** Attribute tag of the key splitter in a key array. */ + private static final String XMLATTR_KEY_SPLITTER = "splitter"; + + /** Attribute tag of the key labels in a key array. */ + private static final String XMLATTR_KEY_LABELS = "labels"; + + /** Attribute tag of the key codes in a key array. */ + private static final String XMLATTR_KEY_CODES = "codes"; + + /** Attribute tag of the key label in a key. */ + private static final String XMLATTR_KEY_LABEL = "label"; + + /** Attribute tag of the key code in a key. */ + private static final String XMLATTR_KEY_CODE = "code"; + + /** Attribute tag of the key icon in a key. */ + private static final String XMLATTR_KEY_ICON = "icon"; + + /** Attribute tag of the key's popup icon in a key. */ + private static final String XMLATTR_KEY_ICON_POPUP = "icon_popup"; + + /** The id for a mini popup soft keyboard. */ + private static final String XMLATTR_KEY_POPUP_SKBID = "popup_skb"; + + private static boolean DEFAULT_SKB_CACHE_FLAG = true; + + private static boolean DEFAULT_SKB_STICKY_FLAG = true; + + /** + * The key type id for invalid key type. It is also used to generate next + * valid key type id by adding 1. + */ + private static final int KEYTYPE_ID_LAST = -1; + + private Context mContext; + + private Resources mResources; + + /** The event type in parsing the xml file. */ + private int mXmlEventType; + + /** + * The current soft keyboard template used by the current soft keyboard + * under loading. + **/ + private SkbTemplate mSkbTemplate; + + /** The x position for the next key. */ + float mKeyXPos; + + /** The y position for the next key. */ + float mKeyYPos; + + /** The width of the keyboard to load. */ + int mSkbWidth; + + /** The height of the keyboard to load. */ + int mSkbHeight; + + /** Key margin in x-way. */ + float mKeyXMargin = 0; + + /** Key margin in y-way. */ + float mKeyYMargin = 0; + + /** + * Used to indicate whether next event has been fetched during processing + * the the current event. + */ + boolean mNextEventFetched = false; + + String mAttrTmp; + + class KeyCommonAttributes { + XmlResourceParser mXrp; + int keyType; + float keyWidth; + float keyHeight; + boolean repeat; + boolean balloon; + + KeyCommonAttributes(XmlResourceParser xrp) { + mXrp = xrp; + balloon = true; + } + + // Make sure the default object is not null. + boolean getAttributes(KeyCommonAttributes defAttr) { + keyType = getInteger(mXrp, XMLATTR_KEY_TYPE, defAttr.keyType); + keyWidth = getFloat(mXrp, XMLATTR_KEY_WIDTH, defAttr.keyWidth); + keyHeight = getFloat(mXrp, XMLATTR_KEY_HEIGHT, defAttr.keyHeight); + repeat = getBoolean(mXrp, XMLATTR_KEY_REPEAT, defAttr.repeat); + balloon = getBoolean(mXrp, XMLATTR_KEY_BALLOON, defAttr.balloon); + if (keyType < 0 || keyWidth <= 0 || keyHeight <= 0) { + return false; + } + return true; + } + } + + public XmlKeyboardLoader(Context context) { + mContext = context; + mResources = mContext.getResources(); + } + + public SkbTemplate loadSkbTemplate(int resourceId) { + if (null == mContext || 0 == resourceId) { + return null; + } + Resources r = mResources; + XmlResourceParser xrp = r.getXml(resourceId); + + KeyCommonAttributes attrDef = new KeyCommonAttributes(xrp); + KeyCommonAttributes attrKey = new KeyCommonAttributes(xrp); + + mSkbTemplate = new SkbTemplate(resourceId); + int lastKeyTypeId = KEYTYPE_ID_LAST; + int globalColor = 0; + int globalColorHl = 0; + int globalColorBalloon = 0; + try { + mXmlEventType = xrp.next(); + while (mXmlEventType != XmlResourceParser.END_DOCUMENT) { + mNextEventFetched = false; + if (mXmlEventType == XmlResourceParser.START_TAG) { + String attribute = xrp.getName(); + if (XMLTAG_SKB_TEMPLATE.compareTo(attribute) == 0) { + Drawable skbBg = getDrawable(xrp, XMLATTR_SKB_BG, null); + Drawable balloonBg = getDrawable(xrp, + XMLATTR_BALLOON_BG, null); + Drawable popupBg = getDrawable(xrp, XMLATTR_POPUP_BG, + null); + if (null == skbBg || null == balloonBg + || null == popupBg) { + return null; + } + mSkbTemplate.setBackgrounds(skbBg, balloonBg, popupBg); + + float xMargin = getFloat(xrp, XMLATTR_KEY_XMARGIN, 0); + float yMargin = getFloat(xrp, XMLATTR_KEY_YMARGIN, 0); + mSkbTemplate.setMargins(xMargin, yMargin); + + // Get default global colors. + globalColor = getColor(xrp, XMLATTR_COLOR, 0); + globalColorHl = getColor(xrp, XMLATTR_COLOR_HIGHLIGHT, + 0xffffffff); + globalColorBalloon = getColor(xrp, + XMLATTR_COLOR_BALLOON, 0xffffffff); + } else if (XMLTAG_KEYTYPE.compareTo(attribute) == 0) { + int id = getInteger(xrp, XMLATTR_ID, KEYTYPE_ID_LAST); + Drawable bg = getDrawable(xrp, XMLATTR_KEYTYPE_BG, null); + Drawable hlBg = getDrawable(xrp, XMLATTR_KEYTYPE_HLBG, + null); + int color = getColor(xrp, XMLATTR_COLOR, globalColor); + int colorHl = getColor(xrp, XMLATTR_COLOR_HIGHLIGHT, + globalColorHl); + int colorBalloon = getColor(xrp, XMLATTR_COLOR_BALLOON, + globalColorBalloon); + if (id != lastKeyTypeId + 1) { + return null; + } + SoftKeyType keyType = mSkbTemplate.createKeyType(id, + bg, hlBg); + keyType.setColors(color, colorHl, colorBalloon); + if (!mSkbTemplate.addKeyType(keyType)) { + return null; + } + lastKeyTypeId = id; + } else if (XMLTAG_KEYICON.compareTo(attribute) == 0) { + int keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0); + Drawable icon = getDrawable(xrp, XMLATTR_KEY_ICON, null); + Drawable iconPopup = getDrawable(xrp, + XMLATTR_KEY_ICON_POPUP, null); + if (null != icon && null != iconPopup) { + mSkbTemplate.addDefaultKeyIcons(keyCode, icon, + iconPopup); + } + } else if (XMLTAG_KEY.compareTo(attribute) == 0) { + int keyId = this.getInteger(xrp, XMLATTR_ID, -1); + if (-1 == keyId) return null; + + if (!attrKey.getAttributes(attrDef)) { + return null; + } + + // Update the key position for the key. + mKeyXPos = getFloat(xrp, XMLATTR_START_POS_X, 0); + mKeyYPos = getFloat(xrp, XMLATTR_START_POS_Y, 0); + + SoftKey softKey = getSoftKey(xrp, attrKey); + if (null == softKey) return null; + mSkbTemplate.addDefaultKey(keyId, softKey); + } + } + // Get the next tag. + if (!mNextEventFetched) mXmlEventType = xrp.next(); + } + xrp.close(); + return mSkbTemplate; + } catch (XmlPullParserException e) { + // Log.e(TAG, "Ill-formatted keyboard template resource file"); + } catch (IOException e) { + // Log.e(TAG, "Unable to keyboard template resource file"); + } + return null; + } + + public SoftKeyboard loadKeyboard(int resourceId, int skbWidth, int skbHeight) { + if (null == mContext) return null; + Resources r = mResources; + SkbPool skbPool = SkbPool.getInstance(); + XmlResourceParser xrp = mContext.getResources().getXml(resourceId); + mSkbTemplate = null; + SoftKeyboard softKeyboard = null; + Drawable skbBg; + Drawable popupBg; + Drawable balloonBg; + SoftKey softKey = null; + + KeyCommonAttributes attrDef = new KeyCommonAttributes(xrp); + KeyCommonAttributes attrSkb = new KeyCommonAttributes(xrp); + KeyCommonAttributes attrRow = new KeyCommonAttributes(xrp); + KeyCommonAttributes attrKeys = new KeyCommonAttributes(xrp); + KeyCommonAttributes attrKey = new KeyCommonAttributes(xrp); + + mKeyXPos = 0; + mKeyYPos = 0; + mSkbWidth = skbWidth; + mSkbHeight = skbHeight; + + try { + mKeyXMargin = 0; + mKeyYMargin = 0; + mXmlEventType = xrp.next(); + while (mXmlEventType != XmlResourceParser.END_DOCUMENT) { + mNextEventFetched = false; + if (mXmlEventType == XmlResourceParser.START_TAG) { + String attr = xrp.getName(); + // 1. Is it the root element, "keyboard"? + if (XMLTAG_KEYBOARD.compareTo(attr) == 0) { + // 1.1 Get the keyboard template id. + int skbTemplateId = xrp.getAttributeResourceValue(null, + XMLATTR_SKB_TEMPLATE, 0); + + // 1.2 Try to get the template from pool. If it is not + // in, the pool will try to load it. + mSkbTemplate = skbPool.getSkbTemplate(skbTemplateId, + mContext); + + if (null == mSkbTemplate + || !attrSkb.getAttributes(attrDef)) { + return null; + } + + boolean cacheFlag = getBoolean(xrp, + XMLATTR_SKB_CACHE_FLAG, DEFAULT_SKB_CACHE_FLAG); + boolean stickyFlag = getBoolean(xrp, + XMLATTR_SKB_STICKY_FLAG, + DEFAULT_SKB_STICKY_FLAG); + boolean isQwerty = getBoolean(xrp, XMLATTR_QWERTY, + false); + boolean isQwertyUpperCase = getBoolean(xrp, + XMLATTR_QWERTY_UPPERCASE, false); + + softKeyboard = new SoftKeyboard(resourceId, + mSkbTemplate, mSkbWidth, mSkbHeight); + softKeyboard.setFlags(cacheFlag, stickyFlag, isQwerty, + isQwertyUpperCase); + + mKeyXMargin = getFloat(xrp, XMLATTR_KEY_XMARGIN, + mSkbTemplate.getXMargin()); + mKeyYMargin = getFloat(xrp, XMLATTR_KEY_YMARGIN, + mSkbTemplate.getYMargin()); + skbBg = getDrawable(xrp, XMLATTR_SKB_BG, null); + popupBg = getDrawable(xrp, XMLATTR_POPUP_BG, null); + balloonBg = getDrawable(xrp, XMLATTR_BALLOON_BG, null); + if (null != skbBg) { + softKeyboard.setSkbBackground(skbBg); + } + if (null != popupBg) { + softKeyboard.setPopupBackground(popupBg); + } + if (null != balloonBg) { + softKeyboard.setKeyBalloonBackground(balloonBg); + } + softKeyboard.setKeyMargins(mKeyXMargin, mKeyYMargin); + } else if (XMLTAG_ROW.compareTo(attr) == 0) { + if (!attrRow.getAttributes(attrSkb)) { + return null; + } + // Get the starting positions for the row. + mKeyXPos = getFloat(xrp, XMLATTR_START_POS_X, 0); + mKeyYPos = getFloat(xrp, XMLATTR_START_POS_Y, mKeyYPos); + int rowId = getInteger(xrp, XMLATTR_ROW_ID, + KeyRow.ALWAYS_SHOW_ROW_ID); + softKeyboard.beginNewRow(rowId, mKeyYPos); + } else if (XMLTAG_KEYS.compareTo(attr) == 0) { + if (null == softKeyboard) return null; + if (!attrKeys.getAttributes(attrRow)) { + return null; + } + + String splitter = xrp.getAttributeValue(null, + XMLATTR_KEY_SPLITTER); + splitter = Pattern.quote(splitter); + String labels = xrp.getAttributeValue(null, + XMLATTR_KEY_LABELS); + String codes = xrp.getAttributeValue(null, + XMLATTR_KEY_CODES); + if (null == splitter || null == labels) { + return null; + } + String labelArr[] = labels.split(splitter); + String codeArr[] = null; + if (null != codes) { + codeArr = codes.split(splitter); + if (labelArr.length != codeArr.length) { + return null; + } + } + + for (int i = 0; i < labelArr.length; i++) { + softKey = new SoftKey(); + int keyCode = 0; + if (null != codeArr) { + keyCode = Integer.valueOf(codeArr[i]); + } + softKey.setKeyAttribute(keyCode, labelArr[i], + attrKeys.repeat, attrKeys.balloon); + + softKey.setKeyType(mSkbTemplate + .getKeyType(attrKeys.keyType), null, null); + + float left, right, top, bottom; + left = mKeyXPos; + + right = left + attrKeys.keyWidth; + top = mKeyYPos; + bottom = top + attrKeys.keyHeight; + + if (right - left < 2 * mKeyXMargin) return null; + if (bottom - top < 2 * mKeyYMargin) return null; + + softKey.setKeyDimensions(left, top, right, bottom); + softKeyboard.addSoftKey(softKey); + mKeyXPos = right; + if ((int) mKeyXPos * mSkbWidth > mSkbWidth) { + return null; + } + } + } else if (XMLTAG_KEY.compareTo(attr) == 0) { + if (null == softKeyboard) { + return null; + } + if (!attrKey.getAttributes(attrRow)) { + return null; + } + + int keyId = this.getInteger(xrp, XMLATTR_ID, -1); + if (keyId >= 0) { + softKey = mSkbTemplate.getDefaultKey(keyId); + } else { + softKey = getSoftKey(xrp, attrKey); + } + if (null == softKey) return null; + + // Update the position for next key. + mKeyXPos = softKey.mRightF; + if ((int) mKeyXPos * mSkbWidth > mSkbWidth) { + return null; + } + // If the current xml event type becomes a starting tag, + // it indicates that we have parsed too much to get + // toggling states, and we started a new row. In this + // case, the row starting position information should + // be updated. + if (mXmlEventType == XmlResourceParser.START_TAG) { + attr = xrp.getName(); + if (XMLTAG_ROW.compareTo(attr) == 0) { + mKeyYPos += attrRow.keyHeight; + if ((int) mKeyYPos * mSkbHeight > mSkbHeight) { + return null; + } + } + } + softKeyboard.addSoftKey(softKey); + } + } else if (mXmlEventType == XmlResourceParser.END_TAG) { + String attr = xrp.getName(); + if (XMLTAG_ROW.compareTo(attr) == 0) { + mKeyYPos += attrRow.keyHeight; + if ((int) mKeyYPos * mSkbHeight > mSkbHeight) { + return null; + } + } + } + + // Get the next tag. + if (!mNextEventFetched) mXmlEventType = xrp.next(); + } + xrp.close(); + softKeyboard.setSkbCoreSize(mSkbWidth, mSkbHeight); + return softKeyboard; + } catch (XmlPullParserException e) { + // Log.e(TAG, "Ill-formatted keybaord resource file"); + } catch (IOException e) { + // Log.e(TAG, "Unable to read keyboard resource file"); + } + return null; + } + + // Caller makes sure xrp and r are valid. + private SoftKey getSoftKey(XmlResourceParser xrp, + KeyCommonAttributes attrKey) throws XmlPullParserException, + IOException { + int keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0); + String keyLabel = getString(xrp, XMLATTR_KEY_LABEL, null); + Drawable keyIcon = getDrawable(xrp, XMLATTR_KEY_ICON, null); + Drawable keyIconPopup = getDrawable(xrp, XMLATTR_KEY_ICON_POPUP, null); + int popupSkbId = xrp.getAttributeResourceValue(null, + XMLATTR_KEY_POPUP_SKBID, 0); + + if (null == keyLabel && null == keyIcon) { + keyIcon = mSkbTemplate.getDefaultKeyIcon(keyCode); + keyIconPopup = mSkbTemplate.getDefaultKeyIconPopup(keyCode); + if (null == keyIcon || null == keyIconPopup) return null; + } + + // Dimension information must been initialized before + // getting toggle state, because mKeyYPos may be changed + // to next row when trying to get toggle state. + float left, right, top, bottom; + left = mKeyXPos; + right = left + attrKey.keyWidth; + top = mKeyYPos; + bottom = top + attrKey.keyHeight; + + if (right - left < 2 * mKeyXMargin) return null; + if (bottom - top < 2 * mKeyYMargin) return null; + + // Try to find if the next tag is + // {@link #XMLTAG_TOGGLE_STATE_OF_KEY}, if yes, try to + // create a toggle key. + boolean toggleKey = false; + mXmlEventType = xrp.next(); + mNextEventFetched = true; + + SoftKey softKey; + if (mXmlEventType == XmlResourceParser.START_TAG) { + mAttrTmp = xrp.getName(); + if (mAttrTmp.compareTo(XMLTAG_TOGGLE_STATE) == 0) { + toggleKey = true; + } + } + if (toggleKey) { + softKey = new SoftKeyToggle(); + if (!((SoftKeyToggle) softKey).setToggleStates(getToggleStates( + attrKey, (SoftKeyToggle) softKey, keyCode))) { + return null; + } + } else { + softKey = new SoftKey(); + } + + // Set the normal state + softKey.setKeyAttribute(keyCode, keyLabel, attrKey.repeat, + attrKey.balloon); + softKey.setPopupSkbId(popupSkbId); + softKey.setKeyType(mSkbTemplate.getKeyType(attrKey.keyType), keyIcon, + keyIconPopup); + + softKey.setKeyDimensions(left, top, right, bottom); + return softKey; + } + + private SoftKeyToggle.ToggleState getToggleStates( + KeyCommonAttributes attrKey, SoftKeyToggle softKey, int defKeyCode) + throws XmlPullParserException, IOException { + XmlResourceParser xrp = attrKey.mXrp; + int stateId = getInteger(xrp, XMLATTR_TOGGLE_STATE_ID, 0); + if (0 == stateId) return null; + + String keyLabel = getString(xrp, XMLATTR_KEY_LABEL, null); + int keyTypeId = getInteger(xrp, XMLATTR_KEY_TYPE, KEYTYPE_ID_LAST); + int keyCode; + if (null == keyLabel) { + keyCode = getInteger(xrp, XMLATTR_KEY_CODE, defKeyCode); + } else { + keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0); + } + Drawable icon = getDrawable(xrp, XMLATTR_KEY_ICON, null); + Drawable iconPopup = getDrawable(xrp, XMLATTR_KEY_ICON_POPUP, null); + if (null == icon && null == keyLabel) { + return null; + } + SoftKeyToggle.ToggleState rootState = softKey.createToggleState(); + rootState.setStateId(stateId); + rootState.mKeyType = null; + if (KEYTYPE_ID_LAST != keyTypeId) { + rootState.mKeyType = mSkbTemplate.getKeyType(keyTypeId); + } + rootState.mKeyCode = keyCode; + rootState.mKeyIcon = icon; + rootState.mKeyIconPopup = iconPopup; + rootState.mKeyLabel = keyLabel; + + boolean repeat = getBoolean(xrp, XMLATTR_KEY_REPEAT, attrKey.repeat); + boolean balloon = getBoolean(xrp, XMLATTR_KEY_BALLOON, attrKey.balloon); + rootState.setStateFlags(repeat, balloon); + + rootState.mNextState = null; + + // If there is another toggle state. + mXmlEventType = xrp.next(); + while (mXmlEventType != XmlResourceParser.START_TAG + && mXmlEventType != XmlResourceParser.END_DOCUMENT) { + mXmlEventType = xrp.next(); + } + if (mXmlEventType == XmlResourceParser.START_TAG) { + String attr = xrp.getName(); + if (attr.compareTo(XMLTAG_TOGGLE_STATE) == 0) { + SoftKeyToggle.ToggleState nextState = getToggleStates(attrKey, + softKey, defKeyCode); + if (null == nextState) return null; + rootState.mNextState = nextState; + } + } + + return rootState; + } + + private int getInteger(XmlResourceParser xrp, String name, int defValue) { + int resId = xrp.getAttributeResourceValue(null, name, 0); + String s; + if (resId == 0) { + s = xrp.getAttributeValue(null, name); + if (null == s) return defValue; + try { + int ret = Integer.valueOf(s); + return ret; + } catch (NumberFormatException e) { + return defValue; + } + } else { + return Integer.parseInt(mContext.getResources().getString(resId)); + } + } + + private int getColor(XmlResourceParser xrp, String name, int defValue) { + int resId = xrp.getAttributeResourceValue(null, name, 0); + String s; + if (resId == 0) { + s = xrp.getAttributeValue(null, name); + if (null == s) return defValue; + try { + int ret = Integer.valueOf(s); + return ret; + } catch (NumberFormatException e) { + return defValue; + } + } else { + return mContext.getResources().getColor(resId); + } + } + + private String getString(XmlResourceParser xrp, String name, String defValue) { + int resId = xrp.getAttributeResourceValue(null, name, 0); + if (resId == 0) { + return xrp.getAttributeValue(null, name); + } else { + return mContext.getResources().getString(resId); + } + } + + private float getFloat(XmlResourceParser xrp, String name, float defValue) { + int resId = xrp.getAttributeResourceValue(null, name, 0); + if (resId == 0) { + String s = xrp.getAttributeValue(null, name); + if (null == s) return defValue; + try { + float ret; + if (s.endsWith("%p")) { + ret = Float.parseFloat(s.substring(0, s.length() - 2)) / 100; + } else { + ret = Float.parseFloat(s); + } + return ret; + } catch (NumberFormatException e) { + return defValue; + } + } else { + return mContext.getResources().getDimension(resId); + } + } + + private boolean getBoolean(XmlResourceParser xrp, String name, + boolean defValue) { + String s = xrp.getAttributeValue(null, name); + if (null == s) return defValue; + try { + boolean ret = Boolean.parseBoolean(s); + return ret; + } catch (NumberFormatException e) { + return defValue; + } + } + + private Drawable getDrawable(XmlResourceParser xrp, String name, + Drawable defValue) { + int resId = xrp.getAttributeResourceValue(null, name, 0); + if (0 == resId) return defValue; + return mResources.getDrawable(resId); + } +} -- cgit v1.2.3