Home | History | Annotate | Download | only in JAJP
      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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    338     public void clearPreviousWord() {
    339         mPreviousWord = null;
    340     }
    341 
    342     /**
    343      * Set keyboard type.
    344      *
    345      * @param keyboardType      Type of keyboard
    346      */
    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      */
    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 */
    365     public void init() {
    366         clearPreviousWord();
    367         mClauseConverter.setDictionary(mDictionaryJP);
    368         mKanaConverter.setDictionary(mDictionaryJP);
    369     }
    370 
    371     /** @see jp.co.omronsoft.openwnn.WnnEngine#close */
    372     public void close() {}
    373 
    374     /** @see jp.co.omronsoft.openwnn.WnnEngine#predict */
    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 */
    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 */
    466     public int searchWords(String key) {
    467         clearCandidates();
    468         return 0;
    469     }
    470 
    471     /** @see jp.co.omronsoft.openwnn.WnnEngine#searchWords */
    472     public int searchWords(WnnWord word) {
    473         clearCandidates();
    474         return 0;
    475     }
    476 
    477     /** @see jp.co.omronsoft.openwnn.WnnEngine#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 */
    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 */
    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 */
    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 */
    544     public void setPreferences(SharedPreferences pref) {}
    545 
    546     /** @see jp.co.omronsoft.openwnn.WnnEngine#breakSequence */
    547     public void breakSequence()  {
    548         clearPreviousWord();
    549     }
    550 
    551     /** @see jp.co.omronsoft.openwnn.WnnEngine#makeCandidateListOf */
    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 */
    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 */
    585     public boolean initializeDictionary(int dictionary, int type) {
    586         return initializeDictionary(dictionary);
    587     }
    588 
    589     /** @see jp.co.omronsoft.openwnn.WnnEngine#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 {
    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