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