Home | History | Annotate | Download | only in JAJP
      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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    341     public void clearPreviousWord() {
    342         mPreviousWord = null;
    343     }
    344 
    345     /**
    346      * Set keyboard type.
    347      *
    348      * @param keyboardType      Type of keyboard
    349      */
    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      */
    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 */
    368     public void init() {
    369         clearPreviousWord();
    370         mClauseConverter.setDictionary(mDictionaryJP);
    371         mKanaConverter.setDictionary(mDictionaryJP);
    372     }
    373 
    374     /** @see jp.co.omronsoft.openwnn.WnnEngine#close */
    375     public void close() {}
    376 
    377     /** @see jp.co.omronsoft.openwnn.WnnEngine#predict */
    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 */
    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 */
    469     public int searchWords(String key) {
    470         clearCandidates();
    471         return 0;
    472     }
    473 
    474     /** @see jp.co.omronsoft.openwnn.WnnEngine#searchWords */
    475     public int searchWords(WnnWord word) {
    476         clearCandidates();
    477         return 0;
    478     }
    479 
    480     /** @see jp.co.omronsoft.openwnn.WnnEngine#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 */
    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 */
    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 */
    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 */
    547     public void setPreferences(SharedPreferences pref) {}
    548 
    549     /** @see jp.co.omronsoft.openwnn.WnnEngine#breakSequence */
    550     public void breakSequence()  {
    551         clearPreviousWord();
    552     }
    553 
    554     /** @see jp.co.omronsoft.openwnn.WnnEngine#makeCandidateListOf */
    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 */
    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 */
    588     public boolean initializeDictionary(int dictionary, int type) {
    589         return initializeDictionary(dictionary);
    590     }
    591 
    592     /** @see jp.co.omronsoft.openwnn.WnnEngine#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 {
    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