• 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         mDictionaryJP.addWordToUserDictionary(word);
528         mDictionaryJP.setInUseState( false );
529         return 0;
530     }
531 
532     /** @see jp.co.omronsoft.openwnn.WnnEngine#deleteWord */
deleteWord(WnnWord word)533     public boolean deleteWord(WnnWord word) {
534         mDictionaryJP.setInUseState( true );
535         mDictionaryJP.removeWordFromUserDictionary(word);
536         mDictionaryJP.setInUseState( false );
537         return false;
538     }
539 
540     /** @see jp.co.omronsoft.openwnn.WnnEngine#setPreferences */
setPreferences(SharedPreferences pref)541     public void setPreferences(SharedPreferences pref) {}
542 
543     /** @see jp.co.omronsoft.openwnn.WnnEngine#breakSequence */
breakSequence()544     public void breakSequence()  {
545         clearPreviousWord();
546     }
547 
548     /** @see jp.co.omronsoft.openwnn.WnnEngine#makeCandidateListOf */
makeCandidateListOf(int clausePosition)549     public int makeCandidateListOf(int clausePosition)  {
550         clearCandidates();
551 
552         if ((mConvertSentence == null) || (mConvertSentence.elements.size() <= clausePosition)) {
553             return 0;
554         }
555         mSingleClauseMode = true;
556         WnnClause clause = mConvertSentence.elements.get(clausePosition);
557         mInputHiragana = clause.stroke;
558         mInputRomaji = clause.candidate;
559 
560         return 1;
561     }
562 
563     /** @see jp.co.omronsoft.openwnn.WnnEngine#initializeDictionary */
initializeDictionary(int dictionary)564     public boolean initializeDictionary(int dictionary)  {
565         switch( dictionary ) {
566         case WnnEngine.DICTIONARY_TYPE_LEARN:
567             mDictionaryJP.setInUseState( true );
568             mDictionaryJP.clearLearnDictionary();
569             mDictionaryJP.setInUseState( false );
570             return true;
571 
572         case WnnEngine.DICTIONARY_TYPE_USER:
573             mDictionaryJP.setInUseState( true );
574             mDictionaryJP.clearUserDictionary();
575             mDictionaryJP.setInUseState( false );
576             return true;
577         }
578         return false;
579     }
580 
581     /** @see jp.co.omronsoft.openwnn.WnnEngine#initializeDictionary */
initializeDictionary(int dictionary, int type)582     public boolean initializeDictionary(int dictionary, int type) {
583     	return initializeDictionary(dictionary);
584     }
585 
586     /** @see jp.co.omronsoft.openwnn.WnnEngine#getUserDictionaryWords */
getUserDictionaryWords( )587     public WnnWord[] getUserDictionaryWords( ) {
588         /* get words in the user dictionary */
589         mDictionaryJP.setInUseState(true);
590         WnnWord[] result = mDictionaryJP.getUserDictionaryWords( );
591         mDictionaryJP.setInUseState(false);
592 
593         /* sort the array of words */
594         Arrays.sort(result, new WnnWordComparator());
595 
596         return result;
597     }
598 
599     /* {@link WnnWord} comparator for listing up words in the user dictionary */
600     private class WnnWordComparator implements java.util.Comparator {
compare(Object object1, Object object2)601         public int compare(Object object1, Object object2) {
602             WnnWord wnnWord1 = (WnnWord) object1;
603             WnnWord wnnWord2 = (WnnWord) object2;
604             return wnnWord1.stroke.compareTo(wnnWord2.stroke);
605         }
606     }
607 }
608