1 /* 2 * Copyright (C) 2008-2012 OMRON SOFTWARE Co., Ltd. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package jp.co.omronsoft.openwnn.JAJP; 18 19 import java.util.ArrayList; 20 import java.util.HashMap; 21 import java.util.Iterator; 22 import java.util.List; 23 import java.util.Arrays; 24 25 import jp.co.omronsoft.openwnn.CandidateFilter; 26 import jp.co.omronsoft.openwnn.ComposingText; 27 import jp.co.omronsoft.openwnn.OpenWnn; 28 import jp.co.omronsoft.openwnn.OpenWnnDictionaryImpl; 29 import jp.co.omronsoft.openwnn.StrSegmentClause; 30 import jp.co.omronsoft.openwnn.WnnClause; 31 import jp.co.omronsoft.openwnn.WnnDictionary; 32 import jp.co.omronsoft.openwnn.WnnEngine; 33 import jp.co.omronsoft.openwnn.WnnSentence; 34 import jp.co.omronsoft.openwnn.WnnWord; 35 import android.content.SharedPreferences; 36 import android.util.Log; 37 38 /** 39 * The OpenWnn engine class for Japanese IME. 40 * 41 * @author Copyright (C) 2009-2011 OMRON SOFTWARE CO., LTD. All Rights Reserved. 42 */ 43 public class OpenWnnEngineJAJP implements WnnEngine { 44 /** Current dictionary type */ 45 private int mDictType = DIC_LANG_INIT; 46 /** Dictionary type (default) */ 47 public static final int DIC_LANG_INIT = 0; 48 /** Dictionary type (Japanese standard) */ 49 public static final int DIC_LANG_JP = 0; 50 /** Dictionary type (English standard) */ 51 public static final int DIC_LANG_EN = 1; 52 /** Dictionary type (Japanese person's name) */ 53 public static final int DIC_LANG_JP_PERSON_NAME = 2; 54 /** Dictionary type (User dictionary) */ 55 public static final int DIC_USERDIC = 3; 56 /** Dictionary type (Japanese EISU-KANA conversion) */ 57 public static final int DIC_LANG_JP_EISUKANA = 4; 58 /** Dictionary type (e-mail/URI) */ 59 public static final int DIC_LANG_EN_EMAIL_ADDRESS = 5; 60 /** Dictionary type (Japanese postal address) */ 61 public static final int DIC_LANG_JP_POSTAL_ADDRESS = 6; 62 63 /** Type of the keyboard */ 64 private int mKeyboardType = KEYBOARD_UNDEF; 65 /** Keyboard type (not defined) */ 66 public static final int KEYBOARD_UNDEF = 0; 67 /** Keyboard type (12-keys) */ 68 public static final int KEYBOARD_KEYPAD12 = 1; 69 /** Keyboard type (Qwerty) */ 70 public static final int KEYBOARD_QWERTY = 2; 71 72 /** Score(frequency value) of word in the learning dictionary */ 73 public static final int FREQ_LEARN = 600; 74 /** Score(frequency value) of word in the user dictionary */ 75 public static final int FREQ_USER = 500; 76 77 /** Maximum limit length of output */ 78 public static final int MAX_OUTPUT_LENGTH = 50; 79 /** Limitation of predicted candidates */ 80 public static final int PREDICT_LIMIT = 100; 81 82 /** Limitation of candidates one-line */ 83 public static final int LIMIT_OF_CANDIDATES_1LINE = 500; 84 85 /** OpenWnn dictionary */ 86 private WnnDictionary mDictionaryJP; 87 88 /** Word list */ 89 private ArrayList<WnnWord> mConvResult; 90 91 /** HashMap for checking duplicate word */ 92 private HashMap<String, WnnWord> mCandTable; 93 94 /** Input string (Hiragana) */ 95 private String mInputHiragana; 96 97 /** Input string (Romaji) */ 98 private String mInputRomaji; 99 100 /** Number of output candidates */ 101 private int mOutputNum; 102 103 /** 104 * Where to get the next candidates from.<br> 105 * (0:prefix search from the dictionary, 1:single clause converter, 2:Kana converter) 106 */ 107 private int mGetCandidateFrom; 108 109 /** Previously selected word */ 110 private WnnWord mPreviousWord; 111 112 /** Converter for single/consecutive clause conversion */ 113 private OpenWnnClauseConverterJAJP mClauseConverter; 114 115 /** Kana converter (for EISU-KANA conversion) */ 116 private KanaConverter mKanaConverter; 117 118 /** Whether exact match search or prefix match search */ 119 private boolean mExactMatchMode; 120 121 /** Whether displaying single clause candidates or not */ 122 private boolean mSingleClauseMode; 123 124 /** A result of consecutive clause conversion */ 125 private WnnSentence mConvertSentence; 126 127 /** The candidate filter */ 128 private CandidateFilter mFilter = null; 129 130 /** 131 * Constructor 132 * 133 * @param writableDictionaryName Writable dictionary file name(null if not use) 134 */ OpenWnnEngineJAJP(String writableDictionaryName)135 public OpenWnnEngineJAJP(String writableDictionaryName) { 136 /* load Japanese dictionary library */ 137 mDictionaryJP = new OpenWnnDictionaryImpl( 138 "/data/data/jp.co.omronsoft.openwnn/lib/libWnnJpnDic.so", 139 writableDictionaryName ); 140 if (!mDictionaryJP.isActive()) { 141 mDictionaryJP = new OpenWnnDictionaryImpl( 142 "/system/lib/libWnnJpnDic.so", 143 writableDictionaryName ); 144 } 145 146 /* clear dictionary settings */ 147 mDictionaryJP.clearDictionary(); 148 mDictionaryJP.clearApproxPattern(); 149 mDictionaryJP.setInUseState(false); 150 151 /* work buffers */ 152 mConvResult = new ArrayList<WnnWord>(); 153 mCandTable = new HashMap<String, WnnWord>(); 154 155 /* converters */ 156 mClauseConverter = new OpenWnnClauseConverterJAJP(); 157 mKanaConverter = new KanaConverter(); 158 } 159 160 /** 161 * Set dictionary for prediction. 162 * 163 * @param strlen Length of input string 164 */ setDictionaryForPrediction(int strlen)165 private void setDictionaryForPrediction(int strlen) { 166 WnnDictionary dict = mDictionaryJP; 167 168 dict.clearDictionary(); 169 170 if (mDictType != DIC_LANG_JP_EISUKANA) { 171 dict.clearApproxPattern(); 172 if (strlen == 0) { 173 dict.setDictionary(2, 245, 245); 174 dict.setDictionary(3, 100, 244); 175 176 dict.setDictionary(WnnDictionary.INDEX_LEARN_DICTIONARY, FREQ_LEARN, FREQ_LEARN); 177 } else { 178 dict.setDictionary(0, 100, 400); 179 if (strlen > 1) { 180 dict.setDictionary(1, 100, 400); 181 } 182 dict.setDictionary(2, 245, 245); 183 dict.setDictionary(3, 100, 244); 184 185 dict.setDictionary(WnnDictionary.INDEX_USER_DICTIONARY, FREQ_USER, FREQ_USER); 186 dict.setDictionary(WnnDictionary.INDEX_LEARN_DICTIONARY, FREQ_LEARN, FREQ_LEARN); 187 if (mKeyboardType != KEYBOARD_QWERTY) { 188 dict.setApproxPattern(WnnDictionary.APPROX_PATTERN_JAJP_12KEY_NORMAL); 189 } 190 } 191 } 192 } 193 194 /** 195 * Get a candidate. 196 * 197 * @param index Index of a candidate. 198 * @return The candidate; {@code null} if there is no candidate. 199 */ getCandidate(int index)200 private WnnWord getCandidate(int index) { 201 WnnWord word; 202 203 if (mGetCandidateFrom == 0) { 204 if (mDictType == OpenWnnEngineJAJP.DIC_LANG_JP_EISUKANA) { 205 /* skip to Kana conversion if EISU-KANA conversion mode */ 206 mGetCandidateFrom = 2; 207 } else if (mSingleClauseMode) { 208 /* skip to single clause conversion if single clause conversion mode */ 209 mGetCandidateFrom = 1; 210 } else { 211 if (mConvResult.size() < PREDICT_LIMIT) { 212 /* get prefix matching words from the dictionaries */ 213 while (index >= mConvResult.size()) { 214 if ((word = mDictionaryJP.getNextWord()) == null) { 215 mGetCandidateFrom = 1; 216 break; 217 } 218 if (!mExactMatchMode || mInputHiragana.equals(word.stroke)) { 219 addCandidate(word); 220 if (mConvResult.size() >= PREDICT_LIMIT) { 221 mGetCandidateFrom = 1; 222 break; 223 } 224 } 225 } 226 } else { 227 mGetCandidateFrom = 1; 228 } 229 } 230 } 231 232 /* get candidates by single clause conversion */ 233 if (mGetCandidateFrom == 1) { 234 Iterator<?> convResult = mClauseConverter.convert(mInputHiragana); 235 if (convResult != null) { 236 while (convResult.hasNext()) { 237 addCandidate((WnnWord)convResult.next()); 238 } 239 } 240 /* end of candidates by single clause conversion */ 241 mGetCandidateFrom = 2; 242 } 243 244 /* get candidates from Kana converter */ 245 if (mGetCandidateFrom == 2) { 246 List<WnnWord> addCandidateList 247 = mKanaConverter.createPseudoCandidateList(mInputHiragana, mInputRomaji, mKeyboardType); 248 249 Iterator<WnnWord> it = addCandidateList.iterator(); 250 while(it.hasNext()) { 251 addCandidate(it.next()); 252 } 253 254 mGetCandidateFrom = 3; 255 } 256 257 if (index >= mConvResult.size()) { 258 return null; 259 } 260 return (WnnWord)mConvResult.get(index); 261 } 262 263 /** 264 * Add a candidate to the conversion result buffer. 265 * <br> 266 * This method adds a word to the result buffer if there is not 267 * the same one in the buffer and the length of the candidate 268 * string is not longer than {@code MAX_OUTPUT_LENGTH}. 269 * 270 * @param word A word to be add 271 * @return {@code true} if the word added; {@code false} if not. 272 */ addCandidate(WnnWord word)273 private boolean addCandidate(WnnWord word) { 274 if (word.candidate == null || mCandTable.containsKey(word.candidate) 275 || word.candidate.length() > MAX_OUTPUT_LENGTH) { 276 return false; 277 } 278 if (mFilter != null && !mFilter.isAllowed(word)) { 279 return false; 280 } 281 mCandTable.put(word.candidate, word); 282 mConvResult.add(word); 283 return true; 284 } 285 286 /** 287 * Clear work area that hold candidates information. 288 */ clearCandidates()289 private void clearCandidates() { 290 mConvResult.clear(); 291 mCandTable.clear(); 292 mOutputNum = 0; 293 mInputHiragana = null; 294 mInputRomaji = null; 295 mGetCandidateFrom = 0; 296 mSingleClauseMode = false; 297 } 298 299 /** 300 * Set dictionary type. 301 * 302 * @param type Type of dictionary 303 * @return {@code true} if the dictionary is changed; {@code false} if not. 304 */ setDictionary(int type)305 public boolean setDictionary(int type) { 306 mDictType = type; 307 return true; 308 } 309 310 /** 311 * Set the search key and the search mode from {@link ComposingText}. 312 * 313 * @param text Input text 314 * @param maxLen Maximum length to convert 315 * @return Length of the search key 316 */ setSearchKey(ComposingText text, int maxLen)317 private int setSearchKey(ComposingText text, int maxLen) { 318 String input = text.toString(ComposingText.LAYER1); 319 if (0 <= maxLen && maxLen <= input.length()) { 320 input = input.substring(0, maxLen); 321 mExactMatchMode = true; 322 } else { 323 mExactMatchMode = false; 324 } 325 326 if (input.length() == 0) { 327 mInputHiragana = ""; 328 mInputRomaji = ""; 329 return 0; 330 } 331 332 mInputHiragana = input; 333 mInputRomaji = text.toString(ComposingText.LAYER0); 334 335 return input.length(); 336 } 337 338 /** 339 * Clear the previous word's information. 340 */ clearPreviousWord()341 public void clearPreviousWord() { 342 mPreviousWord = null; 343 } 344 345 /** 346 * Set keyboard type. 347 * 348 * @param keyboardType Type of keyboard 349 */ setKeyboardType(int keyboardType)350 public void setKeyboardType(int keyboardType) { 351 mKeyboardType = keyboardType; 352 } 353 354 /** 355 * Set the candidate filter 356 * 357 * @param filter The candidate filter 358 */ setFilter(CandidateFilter filter)359 public void setFilter(CandidateFilter filter) { 360 mFilter = filter; 361 mClauseConverter.setFilter(filter); 362 } 363 364 /*********************************************************************** 365 * WnnEngine's interface 366 **********************************************************************/ 367 /** @see jp.co.omronsoft.openwnn.WnnEngine#init */ init()368 public void init() { 369 clearPreviousWord(); 370 mClauseConverter.setDictionary(mDictionaryJP); 371 mKanaConverter.setDictionary(mDictionaryJP); 372 } 373 374 /** @see jp.co.omronsoft.openwnn.WnnEngine#close */ close()375 public void close() {} 376 377 /** @see jp.co.omronsoft.openwnn.WnnEngine#predict */ predict(ComposingText text, int minLen, int maxLen)378 public int predict(ComposingText text, int minLen, int maxLen) { 379 clearCandidates(); 380 if (text == null) { return 0; } 381 382 /* set mInputHiragana and mInputRomaji */ 383 int len = setSearchKey(text, maxLen); 384 385 /* set dictionaries by the length of input */ 386 setDictionaryForPrediction(len); 387 388 /* search dictionaries */ 389 mDictionaryJP.setInUseState( true ); 390 391 if (len == 0) { 392 /* search by previously selected word */ 393 return mDictionaryJP.searchWord(WnnDictionary.SEARCH_LINK, WnnDictionary.ORDER_BY_FREQUENCY, 394 mInputHiragana, mPreviousWord); 395 } else { 396 if (mExactMatchMode) { 397 /* exact matching */ 398 mDictionaryJP.searchWord(WnnDictionary.SEARCH_EXACT, WnnDictionary.ORDER_BY_FREQUENCY, 399 mInputHiragana); 400 } else { 401 /* prefix matching */ 402 mDictionaryJP.searchWord(WnnDictionary.SEARCH_PREFIX, WnnDictionary.ORDER_BY_FREQUENCY, 403 mInputHiragana); 404 } 405 return 1; 406 } 407 } 408 409 /** @see jp.co.omronsoft.openwnn.WnnEngine#convert */ convert(ComposingText text)410 public int convert(ComposingText text) { 411 clearCandidates(); 412 413 if (text == null) { 414 return 0; 415 } 416 417 mDictionaryJP.setInUseState( true ); 418 419 int cursor = text.getCursor(ComposingText.LAYER1); 420 String input; 421 WnnClause head = null; 422 if (cursor > 0) { 423 /* convert previous part from cursor */ 424 input = text.toString(ComposingText.LAYER1, 0, cursor - 1); 425 Iterator headCandidates = mClauseConverter.convert(input); 426 if ((headCandidates == null) || (!headCandidates.hasNext())) { 427 return 0; 428 } 429 head = new WnnClause(input, (WnnWord)headCandidates.next()); 430 431 /* set the rest of input string */ 432 input = text.toString(ComposingText.LAYER1, cursor, text.size(ComposingText.LAYER1) - 1); 433 } else { 434 /* set whole of input string */ 435 input = text.toString(ComposingText.LAYER1); 436 } 437 438 WnnSentence sentence = null; 439 if (input.length() != 0) { 440 sentence = mClauseConverter.consecutiveClauseConvert(input); 441 } 442 if (head != null) { 443 sentence = new WnnSentence(head, sentence); 444 } 445 if (sentence == null) { 446 return 0; 447 } 448 449 StrSegmentClause[] ss = new StrSegmentClause[sentence.elements.size()]; 450 int pos = 0; 451 int idx = 0; 452 Iterator<WnnClause> it = sentence.elements.iterator(); 453 while(it.hasNext()) { 454 WnnClause clause = (WnnClause)it.next(); 455 int len = clause.stroke.length(); 456 ss[idx] = new StrSegmentClause(clause, pos, pos + len - 1); 457 pos += len; 458 idx += 1; 459 } 460 text.setCursor(ComposingText.LAYER2, text.size(ComposingText.LAYER2)); 461 text.replaceStrSegment(ComposingText.LAYER2, ss, 462 text.getCursor(ComposingText.LAYER2)); 463 mConvertSentence = sentence; 464 465 return 0; 466 } 467 468 /** @see jp.co.omronsoft.openwnn.WnnEngine#searchWords */ searchWords(String key)469 public int searchWords(String key) { 470 clearCandidates(); 471 return 0; 472 } 473 474 /** @see jp.co.omronsoft.openwnn.WnnEngine#searchWords */ searchWords(WnnWord word)475 public int searchWords(WnnWord word) { 476 clearCandidates(); 477 return 0; 478 } 479 480 /** @see jp.co.omronsoft.openwnn.WnnEngine#getNextCandidate */ getNextCandidate()481 public WnnWord getNextCandidate() { 482 if (mInputHiragana == null) { 483 return null; 484 } 485 WnnWord word = getCandidate(mOutputNum); 486 if (word != null) { 487 mOutputNum++; 488 } 489 return word; 490 } 491 492 /** @see jp.co.omronsoft.openwnn.WnnEngine#learn */ learn(WnnWord word)493 public boolean learn(WnnWord word) { 494 int ret = -1; 495 if (word.partOfSpeech.right == 0) { 496 word.partOfSpeech = mDictionaryJP.getPOS(WnnDictionary.POS_TYPE_MEISI); 497 } 498 499 WnnDictionary dict = mDictionaryJP; 500 if (word instanceof WnnSentence) { 501 Iterator<WnnClause> clauses = ((WnnSentence)word).elements.iterator(); 502 while (clauses.hasNext()) { 503 WnnWord wd = clauses.next(); 504 if (mPreviousWord != null) { 505 ret = dict.learnWord(wd, mPreviousWord); 506 } else { 507 ret = dict.learnWord(wd); 508 } 509 mPreviousWord = wd; 510 if (ret != 0) { 511 break; 512 } 513 } 514 } else { 515 if (mPreviousWord != null) { 516 ret = dict.learnWord(word, mPreviousWord); 517 } else { 518 ret = dict.learnWord(word); 519 } 520 mPreviousWord = word; 521 mClauseConverter.setDictionary(dict); 522 } 523 524 return (ret == 0); 525 } 526 527 /** @see jp.co.omronsoft.openwnn.WnnEngine#addWord */ addWord(WnnWord word)528 public int addWord(WnnWord word) { 529 mDictionaryJP.setInUseState( true ); 530 if (word.partOfSpeech.right == 0) { 531 word.partOfSpeech = mDictionaryJP.getPOS(WnnDictionary.POS_TYPE_MEISI); 532 } 533 mDictionaryJP.addWordToUserDictionary(word); 534 mDictionaryJP.setInUseState( false ); 535 return 0; 536 } 537 538 /** @see jp.co.omronsoft.openwnn.WnnEngine#deleteWord */ deleteWord(WnnWord word)539 public boolean deleteWord(WnnWord word) { 540 mDictionaryJP.setInUseState( true ); 541 mDictionaryJP.removeWordFromUserDictionary(word); 542 mDictionaryJP.setInUseState( false ); 543 return false; 544 } 545 546 /** @see jp.co.omronsoft.openwnn.WnnEngine#setPreferences */ setPreferences(SharedPreferences pref)547 public void setPreferences(SharedPreferences pref) {} 548 549 /** @see jp.co.omronsoft.openwnn.WnnEngine#breakSequence */ breakSequence()550 public void breakSequence() { 551 clearPreviousWord(); 552 } 553 554 /** @see jp.co.omronsoft.openwnn.WnnEngine#makeCandidateListOf */ makeCandidateListOf(int clausePosition)555 public int makeCandidateListOf(int clausePosition) { 556 clearCandidates(); 557 558 if ((mConvertSentence == null) || (mConvertSentence.elements.size() <= clausePosition)) { 559 return 0; 560 } 561 mSingleClauseMode = true; 562 WnnClause clause = mConvertSentence.elements.get(clausePosition); 563 mInputHiragana = clause.stroke; 564 mInputRomaji = clause.candidate; 565 566 return 1; 567 } 568 569 /** @see jp.co.omronsoft.openwnn.WnnEngine#initializeDictionary */ initializeDictionary(int dictionary)570 public boolean initializeDictionary(int dictionary) { 571 switch( dictionary ) { 572 case WnnEngine.DICTIONARY_TYPE_LEARN: 573 mDictionaryJP.setInUseState( true ); 574 mDictionaryJP.clearLearnDictionary(); 575 mDictionaryJP.setInUseState( false ); 576 return true; 577 578 case WnnEngine.DICTIONARY_TYPE_USER: 579 mDictionaryJP.setInUseState( true ); 580 mDictionaryJP.clearUserDictionary(); 581 mDictionaryJP.setInUseState( false ); 582 return true; 583 } 584 return false; 585 } 586 587 /** @see jp.co.omronsoft.openwnn.WnnEngine#initializeDictionary */ initializeDictionary(int dictionary, int type)588 public boolean initializeDictionary(int dictionary, int type) { 589 return initializeDictionary(dictionary); 590 } 591 592 /** @see jp.co.omronsoft.openwnn.WnnEngine#getUserDictionaryWords */ getUserDictionaryWords( )593 public WnnWord[] getUserDictionaryWords( ) { 594 /* get words in the user dictionary */ 595 mDictionaryJP.setInUseState(true); 596 WnnWord[] result = mDictionaryJP.getUserDictionaryWords( ); 597 mDictionaryJP.setInUseState(false); 598 599 /* sort the array of words */ 600 Arrays.sort(result, new WnnWordComparator()); 601 602 return result; 603 } 604 605 /* {@link WnnWord} comparator for listing up words in the user dictionary */ 606 private class WnnWordComparator implements java.util.Comparator { compare(Object object1, Object object2)607 public int compare(Object object1, Object object2) { 608 WnnWord wnnWord1 = (WnnWord) object1; 609 WnnWord wnnWord2 = (WnnWord) object2; 610 return wnnWord1.stroke.compareTo(wnnWord2.stroke); 611 } 612 } 613 } 614