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
17package jp.co.omronsoft.openwnn.EN;
18
19import java.util.HashMap;
20import java.util.ArrayList;
21
22import jp.co.omronsoft.openwnn.*;
23import android.content.SharedPreferences;
24
25/**
26 * The OpenWnn engine class for English IME.
27 *
28 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
29 */
30public class OpenWnnEngineEN implements WnnEngine {
31    /** Normal dictionary */
32    public static final int DICT_DEFAULT              = 0;
33    /** Dictionary for mistype correction */
34    public static final int DICT_FOR_CORRECT_MISTYPE  = 1;
35    /** Score(frequency value) of word in the learning dictionary */
36    public static final int FREQ_LEARN = 600;
37    /** Score(frequency value) of word in the user dictionary */
38    public static final int FREQ_USER = 500;
39    /** Limitation of predicted candidates */
40    public static final int PREDICT_LIMIT = 300;
41
42    /** OpenWnn dictionary */
43	private   WnnDictionary mDictionary;
44    /** Word list */
45    private ArrayList<WnnWord> mConvResult;
46    /** HashMap for checking duplicate word */
47    private HashMap<String, WnnWord> mCandTable;
48    /** Input string */
49    private String        mInputString;
50    /** Searching string */
51    private String        mSearchKey;
52    /** Number of output candidates */
53    private int           mOutputNum;
54    /** The candidate filter */
55    private CandidateFilter mFilter = null;
56
57    /**
58     * Candidate's case
59     * <br>
60     * CASE_LOWER: all letters are lower.<br>
61     * CASE_HEAD_UPPER: the first letter is upper; others are lower.<br>
62     * CASE_UPPER: all letters are upper.<br>
63     */
64    private int           mCandidateCase;
65    private static final int CASE_LOWER = 0;
66    private static final int CASE_UPPER = 1;
67    private static final int CASE_HEAD_UPPER = 3;
68
69    /**
70     * Constructor
71     *
72     * @param writableDictionaryName		Writable dictionary file name(null if not use)
73     */
74    public OpenWnnEngineEN(String writableDictionaryName) {
75        mConvResult = new ArrayList<WnnWord>();
76        mCandTable = new HashMap<String, WnnWord>();
77        mSearchKey = null;
78        mOutputNum = 0;
79
80        mDictionary = new OpenWnnDictionaryImpl(
81        		"/data/data/jp.co.omronsoft.openwnn/lib/libWnnEngDic.so",
82        		writableDictionaryName);
83        if (!mDictionary.isActive()) {
84        	mDictionary = new OpenWnnDictionaryImpl(
85        			"/system/lib/libWnnEngDic.so",
86        			writableDictionaryName);
87        }
88        mDictionary.clearDictionary( );
89
90        mDictionary.setDictionary(0, 400, 550);
91        mDictionary.setDictionary(1, 400, 550);
92        mDictionary.setDictionary(2, 400, 550);
93        mDictionary.setDictionary(WnnDictionary.INDEX_USER_DICTIONARY, FREQ_USER, FREQ_USER);
94        mDictionary.setDictionary(WnnDictionary.INDEX_LEARN_DICTIONARY, FREQ_LEARN, FREQ_LEARN);
95
96        mDictionary.setApproxPattern(WnnDictionary.APPROX_PATTERN_EN_QWERTY_NEAR);
97
98        mDictionary.setInUseState( false );
99    }
100
101    /**
102     * Get a candidate.
103     *
104     * @param index		Index of candidate
105     * @return			A candidate; {@code null} if no candidate for the index.
106     */
107    private WnnWord getCandidate(int index) {
108        WnnWord word;
109        /* search the candidate from the dictionaries */
110        while (mConvResult.size() < PREDICT_LIMIT && index >= mConvResult.size()) {
111            while ((word = mDictionary.getNextWord()) != null) {
112                /* adjust the case of letter */
113                char c = word.candidate.charAt(0);
114                if (mCandidateCase == CASE_LOWER) {
115                    if (Character.isLowerCase(c)) {
116                        break;
117                    }
118                } else if (mCandidateCase == CASE_HEAD_UPPER) {
119                    if (Character.isLowerCase(c)) {
120                        word.candidate = Character.toString(Character.toUpperCase(c)) + word.candidate.substring(1);
121                    }
122                    break;
123                } else {
124                    word.candidate = word.candidate.toUpperCase();
125                    break;
126                }
127            }
128            if (word == null) {
129                break;
130            }
131            /* check duplication */
132            addCandidate(word);
133        }
134
135        /* get the default candidates */
136        if (index >= mConvResult.size()) {
137            /* input string itself */
138            addCandidate(new WnnWord(mInputString, mSearchKey));
139
140            /* Capitalize the head of input */
141            if (mSearchKey.length() > 1) {
142                addCandidate(new WnnWord(mSearchKey.substring(0,1).toUpperCase() + mSearchKey.substring(1),
143                                         mSearchKey));
144            }
145
146            /* Capitalize all */
147            addCandidate(new WnnWord(mSearchKey.toUpperCase(), mSearchKey));
148        }
149
150        if (index >= mConvResult.size()) {
151            return null;
152        }
153        return mConvResult.get(index);
154    }
155
156    /**
157     * Add a word to the candidates list if there is no duplication.
158     *
159     * @param word		A word
160     * @return			{@code true} if the word is added to the list; {@code false} if not.
161     */
162    private boolean addCandidate(WnnWord word) {
163        if (word.candidate == null || mCandTable.containsKey(word.candidate)) {
164            return false;
165        }
166        if (mFilter != null && !mFilter.isAllowed(word)) {
167        	return false;
168        }
169        mCandTable.put(word.candidate, word);
170        mConvResult.add(word);
171        return true;
172    }
173
174    private void clearCandidates() {
175        mConvResult.clear();
176        mCandTable.clear();
177        mOutputNum = 0;
178        mSearchKey = null;
179    }
180
181    /**
182     * Set dictionary.
183     *
184     * @param type		Type of dictionary (DIC_DEFAULT or DIC_FOR_CORRECT_MISTYPE)
185     * @return			{@code true} if the dictionary is changed; {@code false} if not.
186     */
187    public boolean setDictionary(int type) {
188        if (type == DICT_FOR_CORRECT_MISTYPE) {
189            mDictionary.clearApproxPattern();
190            mDictionary.setApproxPattern(WnnDictionary.APPROX_PATTERN_EN_QWERTY_NEAR);
191        } else {
192            mDictionary.clearApproxPattern();
193        }
194        return true;
195    }
196
197    /**
198     * Set search key for the dictionary.
199     * <br>
200     * To search the dictionary, this method set the lower case of
201     * input string to the search key. And hold the input string's
202     * capitalization information to adjust the candidates
203     * capitalization later.
204     *
205     * @param input		Input string
206     * @return			{@code true} if the search key is set; {@code false} if not.
207     */
208    private boolean setSearchKey(String input) {
209        if (input.length() == 0) {
210            return false;
211        }
212
213        /* set mInputString */
214        mInputString = input;
215
216        /* set mSearchKey */
217        mSearchKey = input.toLowerCase();
218
219        /* set mCandidateCase */
220        if (Character.isUpperCase(input.charAt(0))) {
221            if (input.length() > 1 && Character.isUpperCase(input.charAt(1))) {
222                mCandidateCase = CASE_UPPER;
223            } else {
224                mCandidateCase = CASE_HEAD_UPPER;
225            }
226        } else {
227            mCandidateCase = CASE_LOWER;
228        }
229
230        return true;
231    }
232
233    /**
234     * Set the candidate filter
235     *
236     * @param filter	The candidate filter
237     */
238    public void setFilter(CandidateFilter filter) {
239    	mFilter = filter;
240    }
241
242    /***********************************************************************
243     * WnnEngine's interface
244     **********************************************************************/
245    /** @see jp.co.omronsoft.openwnn.WnnEngine#init */
246    public void init() {}
247
248    /** @see jp.co.omronsoft.openwnn.WnnEngine#close */
249    public void close() {}
250
251    /** @see jp.co.omronsoft.openwnn.WnnEngine#predict */
252    public int predict(ComposingText text, int minLen, int maxLen) {
253        clearCandidates();
254
255        if (text == null) { return 0; }
256
257        String input = text.toString(2);
258        if (!setSearchKey(input)) {
259            return 0;
260        }
261
262        /* set dictionaries by the length of input */
263        WnnDictionary dict = mDictionary;
264        dict.setInUseState( true );
265
266        dict.clearDictionary();
267        dict.setDictionary(0, 400, 550);
268        if (input.length() > 1) {
269            dict.setDictionary(1, 400, 550);
270        }
271        if (input.length() > 2) {
272            dict.setDictionary(2, 400, 550);
273        }
274        dict.setDictionary(WnnDictionary.INDEX_USER_DICTIONARY, FREQ_USER, FREQ_USER);
275        dict.setDictionary(WnnDictionary.INDEX_LEARN_DICTIONARY, FREQ_LEARN, FREQ_LEARN);
276
277        /* search dictionaries */
278        dict.searchWord(WnnDictionary.SEARCH_PREFIX, WnnDictionary.ORDER_BY_FREQUENCY, mSearchKey);
279        return 1;
280    }
281
282    /** @see jp.co.omronsoft.openwnn.WnnEngine#convert */
283    public int convert(ComposingText text) {
284        clearCandidates();
285        return 0;
286    }
287
288    /** @see jp.co.omronsoft.openwnn.WnnEngine#searchWords */
289    public int searchWords(String key) {
290        clearCandidates();
291        return 0;
292    }
293
294    /** @see jp.co.omronsoft.openwnn.WnnEngine#searchWords */
295    public int searchWords(WnnWord word) {
296        clearCandidates();
297        return 0;
298    }
299
300    /** @see jp.co.omronsoft.openwnn.WnnEngine#getNextCandidate */
301    public WnnWord getNextCandidate() {
302        if (mSearchKey == null) {
303            return null;
304        }
305        WnnWord word = getCandidate(mOutputNum);
306        if (word != null) {
307            mOutputNum++;
308        }
309        return word;
310    }
311
312    /** @see jp.co.omronsoft.openwnn.WnnEngine#learn */
313    public boolean learn(WnnWord word) {
314        return ( mDictionary.learnWord(word) == 0 );
315    }
316
317    /** @see jp.co.omronsoft.openwnn.WnnEngine#addWord */
318    public int addWord(WnnWord word) {
319        WnnDictionary dict = mDictionary;
320        dict.setInUseState( true );
321        dict.addWordToUserDictionary(word);
322        dict.setInUseState( false );
323        return 0;
324    }
325
326    /** @see jp.co.omronsoft.openwnn.WnnEngine#deleteWord */
327    public boolean deleteWord(WnnWord word) {
328        WnnDictionary dict = mDictionary;
329        dict.setInUseState( true );
330        dict.removeWordFromUserDictionary(word);
331        dict.setInUseState( false );
332        return false;
333    }
334
335    /** @see jp.co.omronsoft.openwnn.WnnEngine#setPreferences */
336    public void setPreferences(SharedPreferences pref) {}
337
338    /** @see jp.co.omronsoft.openwnn.WnnEngine#breakSequence */
339    public void breakSequence()  {}
340
341    /** @see jp.co.omronsoft.openwnn.WnnEngine#makeCandidateListOf */
342    public int makeCandidateListOf(int clausePosition)  {return 0;}
343
344    /** @see jp.co.omronsoft.openwnn.WnnEngine#initializeDictionary */
345    public boolean initializeDictionary(int dictionary)  {
346        WnnDictionary dict = mDictionary;
347
348        switch( dictionary ) {
349        case WnnEngine.DICTIONARY_TYPE_LEARN:
350            dict.setInUseState( true );
351            dict.clearLearnDictionary();
352            dict.setInUseState( false );
353            return true;
354
355        case WnnEngine.DICTIONARY_TYPE_USER:
356            dict.setInUseState( true );
357            dict.clearUserDictionary();
358            dict.setInUseState( false );
359            return true;
360        }
361        return false;
362    }
363
364    /** @see jp.co.omronsoft.openwnn.WnnEngine#initializeDictionary */
365    public boolean initializeDictionary(int dictionary, int type) {
366    	return initializeDictionary(dictionary);
367    }
368
369    /** @see jp.co.omronsoft.openwnn.WnnEngine#getUserDictionaryWords */
370    public WnnWord[] getUserDictionaryWords( ) {
371        WnnDictionary dict = mDictionary;
372        dict.setInUseState( true );
373        WnnWord[] result = dict.getUserDictionaryWords( );
374        dict.setInUseState( false );
375        return result;
376    }
377
378}
379