Home | History | Annotate | Download | only in openwnn
      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;
     18 
     19 import android.content.ContentValues;
     20 import android.database.DatabaseUtils;
     21 import android.database.SQLException;
     22 import android.database.sqlite.SQLiteCursor;
     23 import android.database.sqlite.SQLiteDatabase;
     24 
     25 import android.util.Log;
     26 
     27 /**
     28  * The implementation class of WnnDictionary interface (JNI wrapper class).
     29  *
     30  * @author Copyright (C) 2008, 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
     31  */
     32 public class OpenWnnDictionaryImpl implements WnnDictionary {
     33     /*
     34      * DEFINITION FOR JNI
     35      */
     36     static {
     37         /* Load the dictionary search library */
     38         System.loadLibrary( "wnndict" );
     39     }
     40 
     41     /*
     42      * DEFINITION OF CONSTANTS
     43      */
     44     /** The maximum length of stroke */
     45     public static final int MAX_STROKE_LENGTH       = 50;
     46     /** The maximum length of candidate */
     47     public static final int MAX_CANDIDATE_LENGTH    = 50;
     48     /** The table name of writable dictionary on the database */
     49     protected static final String TABLE_NAME_DIC    = "dic";
     50     /** The type name of user word */
     51     protected static final int TYPE_NAME_USER   = 0;
     52     /** The type name of learn word */
     53     protected static final int TYPE_NAME_LEARN  = 1;
     54 
     55     /** The column name of database */
     56     protected static final String COLUMN_NAME_ID                 = "rowid";
     57     /** The column name of database  */
     58     protected static final String COLUMN_NAME_TYPE               = "type";
     59     /** The column name of database  */
     60     protected static final String COLUMN_NAME_STROKE             = "stroke";
     61     /** The column name of database  */
     62     protected static final String COLUMN_NAME_CANDIDATE          = "candidate";
     63     /** The column name of database  */
     64     protected static final String COLUMN_NAME_POS_LEFT           = "posLeft";
     65     /** The column name of database  */
     66     protected static final String COLUMN_NAME_POS_RIGHT          = "posRight";
     67     /** The column name of database  */
     68     protected static final String COLUMN_NAME_PREVIOUS_STROKE    = "prevStroke";
     69     /** The column name of database  */
     70     protected static final String COLUMN_NAME_PREVIOUS_CANDIDATE = "prevCandidate";
     71     /** The column name of database  */
     72     protected static final String COLUMN_NAME_PREVIOUS_POS_LEFT  = "prevPosLeft";
     73     /** The column name of database  */
     74     protected static final String COLUMN_NAME_PREVIOUS_POS_RIGHT = "prevPosRight";
     75 
     76     /** Query for normal search */
     77     protected static final String NORMAL_QUERY =
     78         "select distinct " + COLUMN_NAME_STROKE + "," +
     79                              COLUMN_NAME_CANDIDATE + "," +
     80                              COLUMN_NAME_POS_LEFT + "," +
     81                              COLUMN_NAME_POS_RIGHT + "," +
     82                              COLUMN_NAME_TYPE +
     83                   " from " + TABLE_NAME_DIC + " where %s order by " +
     84                              COLUMN_NAME_TYPE + " DESC, %s";
     85 
     86     /** Query for link search */
     87     protected static final String LINK_QUERY =
     88         "select distinct " + COLUMN_NAME_STROKE + "," +
     89                              COLUMN_NAME_CANDIDATE + "," +
     90                              COLUMN_NAME_POS_LEFT + "," +
     91                              COLUMN_NAME_POS_RIGHT + "," +
     92                              COLUMN_NAME_TYPE +
     93                   " from " + TABLE_NAME_DIC + " where %s = ? and %s = ? and %s order by " +
     94                              COLUMN_NAME_TYPE + " DESC, %s";
     95 
     96     /** The max words of user dictionary */
     97     protected static final int MAX_WORDS_IN_USER_DICTIONARY     = 100;
     98     /** The max words of learning dictionary */
     99     protected static final int MAX_WORDS_IN_LEARN_DICTIONARY    = 2000;
    100 
    101     /** The base frequency of user dictionary */
    102     protected static final int OFFSET_FREQUENCY_OF_USER_DICTIONARY  = 1000;
    103     /** The base frequency of learning dictionary */
    104     protected static final int OFFSET_FREQUENCY_OF_LEARN_DICTIONARY = 2000;
    105 
    106     /*
    107      * Constants to define the upper limit of query.
    108      *
    109      * That is used to fix the size of query expression.
    110      * If the number of approximate patterns for a character is exceeded MAX_PATTERN_OF_APPROX,
    111      * increase that constant to the maximum number of patterns.
    112      */
    113     /** Constants to define the upper limit of approximate patterns */
    114     protected final static int MAX_PATTERN_OF_APPROX    = 6;
    115     /** Constants to define the upper limit of length of a query */
    116     protected final static int MAX_LENGTH_OF_QUERY      = 50;
    117     /**
    118      * Constants to define the turn around time of query.
    119      * <br>
    120      * It can be set between 1 to {@code MAX_LENGTH_OF_QUERY}. If the length of query
    121      * string is shorter than {@code FAST_QUERY_LENGTH}, the simple search logic is applied.
    122      * Therefore, the turn around time for short query string is fast so that it is short.
    123      * However, the difference of turn around time at the border length grows big.
    124      * the value should be fixed carefully.
    125      */
    126     protected final static int FAST_QUERY_LENGTH        = 20;
    127 
    128     /*
    129      * DEFINITION OF PRIVATE FIELD
    130      */
    131     /** Internal work area for the dictionary search library */
    132     protected long mWnnWork = 0;
    133 
    134     /** The file path of the writable dictionary */
    135     protected String mDicFilePath = "";
    136     /** The writable dictionary object */
    137     protected SQLiteDatabase mDbDic = null;
    138     /** The search cursor of the writable dictionary */
    139     protected SQLiteCursor mDbCursor = null;
    140     /** The writable dictionary object Access helper */
    141     protected OpenWnnSQLiteOpenHelper mDbOpenHelper = null;
    142     /** The number of queried items */
    143     protected int mCountCursor = 0;
    144     /** The type of the search cursor object */
    145     protected int mTypeOfQuery = -1;
    146 
    147     /** The query base strings for query operation */
    148     protected String mExactQuerySqlOrderByFreq;
    149     /** The query base strings for query operation */
    150     protected String mExactQuerySqlOrderByKey;
    151 
    152     /** The query base strings for query operation */
    153     protected String mFullPrefixQuerySqlOrderByFreq;
    154     /** The query base strings for query operation */
    155     protected String mFastPrefixQuerySqlOrderByFreq;
    156     /** The query base strings for query operation */
    157     protected String mFullPrefixQuerySqlOrderByKey;
    158     /** The query base strings for query operation */
    159     protected String mFastPrefixQuerySqlOrderByKey;
    160 
    161     /** The query base strings for query operation */
    162     protected String mFullLinkQuerySqlOrderByFreq;
    163     /** The query base strings for query operation */
    164     protected String mFastLinkQuerySqlOrderByFreq;
    165     /** The query base strings for query operation */
    166     protected String mFullLinkQuerySqlOrderByKey;
    167     /** The query base strings for query operation */
    168     protected String mFastLinkQuerySqlOrderByKey;
    169 
    170     /** The string array used by query operation (for "selection") */
    171     protected String mExactQueryArgs[] = new String[ 1 ];
    172     /** The string array used by query operation (for "selection") */
    173     protected String mFullQueryArgs[] = new String[ MAX_LENGTH_OF_QUERY * (MAX_PATTERN_OF_APPROX+1) ];
    174     /** The string array used by query operation (for "selection") */
    175     protected String mFastQueryArgs[] = new String[ FAST_QUERY_LENGTH * (MAX_PATTERN_OF_APPROX+1) ];
    176 
    177     /** The Frequency offset of user dictionary */
    178     protected int mFrequencyOffsetOfUserDictionary = -1;
    179     /** The Frequency offset of learn dictionary */
    180     protected int mFrequencyOffsetOfLearnDictionary = -1;
    181 
    182     /*
    183      * DEFINITION OF METHODS
    184      */
    185     /**
    186      * The constructor of this class without writable dictionary.
    187      *
    188      * Create a internal work area for the search engine. It is allocated for each object.
    189      *
    190      * @param dicLibPath    The dictionary library file path
    191      */
    192     public OpenWnnDictionaryImpl( String dicLibPath ) {
    193         this( dicLibPath, null );
    194     }
    195 
    196     /**
    197      * The constructor of this class with writable dictionary.
    198      *
    199      * Create a internal work area and the writable dictionary for the search engine. It is allocated for each object.
    200      *
    201      * @param dicLibPath    The dictionary library file path
    202      * @param dicFilePath   The path name of writable dictionary
    203      */
    204     public OpenWnnDictionaryImpl( String dicLibPath, String dicFilePath ) {
    205         /* Create the internal work area */
    206         this.mWnnWork = OpenWnnDictionaryImplJni.createWnnWork( dicLibPath );
    207 
    208         if( this.mWnnWork != 0 && dicFilePath != null ) {
    209             /* Create query base strings */
    210             String queryFullBaseString =
    211                 OpenWnnDictionaryImplJni.createQueryStringBase(
    212                     this.mWnnWork,
    213                     MAX_LENGTH_OF_QUERY,
    214                     MAX_PATTERN_OF_APPROX,
    215                     COLUMN_NAME_STROKE );
    216 
    217             String queryFastBaseString =
    218                 OpenWnnDictionaryImplJni.createQueryStringBase(
    219                     this.mWnnWork,
    220                     FAST_QUERY_LENGTH,
    221                     MAX_PATTERN_OF_APPROX,
    222                     COLUMN_NAME_STROKE );
    223 
    224 
    225             mExactQuerySqlOrderByFreq = String.format(
    226                 NORMAL_QUERY,
    227                 String.format( "%s=?", COLUMN_NAME_STROKE ), String.format( "%s DESC", COLUMN_NAME_ID ) );
    228 
    229             mExactQuerySqlOrderByKey = String.format(
    230                 NORMAL_QUERY,
    231                 String.format( "%s=?", COLUMN_NAME_STROKE ), COLUMN_NAME_STROKE );
    232 
    233 
    234             mFullPrefixQuerySqlOrderByFreq = String.format(
    235                 NORMAL_QUERY,
    236                 queryFullBaseString, String.format( "%s DESC", COLUMN_NAME_ID ) );
    237 
    238             mFastPrefixQuerySqlOrderByFreq = String.format(
    239                 NORMAL_QUERY,
    240                 queryFastBaseString, String.format( "%s DESC", COLUMN_NAME_ID ) );
    241 
    242             mFullPrefixQuerySqlOrderByKey = String.format(
    243                 NORMAL_QUERY,
    244                 queryFullBaseString, COLUMN_NAME_STROKE );
    245 
    246             mFastPrefixQuerySqlOrderByKey = String.format(
    247                 NORMAL_QUERY,
    248                 queryFastBaseString, COLUMN_NAME_STROKE );
    249 
    250 
    251             mFullLinkQuerySqlOrderByFreq = String.format(
    252                 LINK_QUERY, COLUMN_NAME_PREVIOUS_STROKE, COLUMN_NAME_PREVIOUS_CANDIDATE,
    253                 queryFullBaseString, String.format( "%s DESC", COLUMN_NAME_ID ) );
    254 
    255             mFastLinkQuerySqlOrderByFreq = String.format(
    256                 LINK_QUERY, COLUMN_NAME_PREVIOUS_STROKE, COLUMN_NAME_PREVIOUS_CANDIDATE,
    257                 queryFastBaseString, String.format( "%s DESC", COLUMN_NAME_ID ) );
    258 
    259             mFullLinkQuerySqlOrderByKey = String.format(
    260                 LINK_QUERY, COLUMN_NAME_PREVIOUS_STROKE, COLUMN_NAME_PREVIOUS_CANDIDATE,
    261                 queryFullBaseString, COLUMN_NAME_STROKE );
    262 
    263             mFastLinkQuerySqlOrderByKey = String.format(
    264                 LINK_QUERY, COLUMN_NAME_PREVIOUS_STROKE, COLUMN_NAME_PREVIOUS_CANDIDATE,
    265                 queryFastBaseString, COLUMN_NAME_STROKE );
    266 
    267 
    268             try {
    269                 /* Create the database object */
    270                 mDicFilePath = dicFilePath;
    271                 setInUseState( true );
    272 
    273                 /* Create the table if not exist */
    274                 createDictionaryTable( TABLE_NAME_DIC );
    275             } catch( SQLException e ) {
    276             }
    277         }
    278     }
    279 
    280     /**
    281      * The finalizer of this class.
    282      * Destroy the internal work area for the search engine.
    283      */
    284     protected void finalize( ) {
    285         /* Free the internal work area */
    286         if( this.mWnnWork != 0 ) {
    287             OpenWnnDictionaryImplJni.freeWnnWork( this.mWnnWork );
    288             this.mWnnWork = 0;
    289 
    290             freeDatabase();
    291         }
    292     }
    293 
    294     /**
    295      * Create the table of writable dictionary.
    296      *
    297      * @param tableName     The name of table
    298      */
    299     protected void createDictionaryTable( String tableName ) {
    300         String sqlStr = "create table if not exists " + tableName +
    301             " (" + COLUMN_NAME_ID                 + " integer primary key autoincrement, " +
    302                    COLUMN_NAME_TYPE               + " integer, " +
    303                    COLUMN_NAME_STROKE             + " text, " +
    304                    COLUMN_NAME_CANDIDATE          + " text, " +
    305                    COLUMN_NAME_POS_LEFT           + " integer, " +
    306                    COLUMN_NAME_POS_RIGHT          + " integer, " +
    307                    COLUMN_NAME_PREVIOUS_STROKE    + " text, " +
    308                    COLUMN_NAME_PREVIOUS_CANDIDATE + " text, " +
    309                    COLUMN_NAME_PREVIOUS_POS_LEFT  + " integer, " +
    310                    COLUMN_NAME_PREVIOUS_POS_RIGHT + " integer)";
    311 
    312         if( mDbDic != null ) {
    313             mDbDic.execSQL( sqlStr );
    314         }
    315     }
    316 
    317     /**
    318      * Free the {@link SQLiteDatabase} of writable dictionary.
    319      */
    320     protected void freeDatabase( ) {
    321         freeCursor();
    322 
    323         if( mDbDic != null ) {
    324             /* The SQLiteDataBase object must close() before releasing. */
    325             mDbDic.close();
    326             mDbDic = null;
    327             mDbOpenHelper = null;
    328         }
    329     }
    330     /**
    331      * Free the {@link SQLiteCursor} of writable dictionary.
    332      */
    333     protected void freeCursor( ) {
    334         if( mDbCursor != null) {
    335             /* The SQLiteCursor object must close() before releasing. */
    336             mDbCursor.close();
    337             mDbCursor = null;
    338 
    339             mTypeOfQuery = -1;
    340         }
    341     }
    342 
    343 
    344     /**
    345      * @see jp.co.omronsoft.openwnn.WnnDictionary#setInUseState
    346      */
    347     public boolean isActive() {
    348         return (this.mWnnWork != 0);
    349     }
    350 
    351     /**
    352      * @see jp.co.omronsoft.openwnn.WnnDictionary#setInUseState
    353      */
    354     public void setInUseState( boolean flag ) {
    355         if( flag ) {
    356             if( mDbDic == null ) {
    357                 mDbOpenHelper = new OpenWnnSQLiteOpenHelper(OpenWnn.getCurrentIme(), mDicFilePath);
    358                 mDbDic = mDbOpenHelper.getWritableDatabase();
    359             }
    360         } else {
    361             freeDatabase();
    362         }
    363     }
    364 
    365     /**
    366      * @see jp.co.omronsoft.openwnn.WnnDictionary#clearDictionary
    367      */
    368     public int clearDictionary( ) {
    369         if( this.mWnnWork != 0 ) {
    370             mFrequencyOffsetOfUserDictionary  = -1;
    371             mFrequencyOffsetOfLearnDictionary = -1;
    372 
    373             return OpenWnnDictionaryImplJni.clearDictionaryParameters( this.mWnnWork );
    374         } else {
    375             return -1;
    376         }
    377     }
    378 
    379     /**
    380      * @see jp.co.omronsoft.openwnn.WnnDictionary#setDictionary
    381      */
    382     public int setDictionary(int index, int base, int high ) {
    383         if( this.mWnnWork != 0 ) {
    384             switch( index ) {
    385             case WnnDictionary.INDEX_USER_DICTIONARY:
    386                 if( base < 0 || high < 0 || base > high
    387                     /* || base < OFFSET_FREQUENCY_OF_USER_DICTIONARY || high >= OFFSET_FREQUENCY_OF_LEARN_DICTIONARY */ ) {
    388                     mFrequencyOffsetOfUserDictionary = -1;
    389                 } else {
    390                     mFrequencyOffsetOfUserDictionary = high;
    391                 }
    392                 return 0;
    393             case WnnDictionary.INDEX_LEARN_DICTIONARY:
    394                 if( base < 0 || high < 0 || base > high
    395                     /* || base < OFFSET_FREQUENCY_OF_LEARN_DICTIONARY */ ) {
    396                     mFrequencyOffsetOfLearnDictionary = -1;
    397                 } else {
    398                     mFrequencyOffsetOfLearnDictionary = high;
    399                 }
    400                 return 0;
    401             default:
    402                 return OpenWnnDictionaryImplJni.setDictionaryParameter( this.mWnnWork, index, base, high );
    403             }
    404         } else {
    405             return -1;
    406         }
    407     }
    408 
    409     /**
    410      * Query to the database
    411      *
    412      * @param keyString     The key string
    413      * @param wnnWord      The previous word for link search
    414      * @param operation    The search operation
    415      * @param order         The type of sort order
    416      */
    417     protected void createQuery( String keyString, WnnWord wnnWord, int operation, int order) {
    418         int newTypeOfQuery, maxBindsOfQuery;
    419         String querySqlOrderByFreq, querySqlOrderByKey;
    420         String queryArgs[];
    421 
    422         if( operation != WnnDictionary.SEARCH_LINK ) {
    423             wnnWord = null;
    424         }
    425 
    426         switch( operation ) {
    427         case WnnDictionary.SEARCH_EXACT:
    428             querySqlOrderByFreq = mExactQuerySqlOrderByFreq;
    429             querySqlOrderByKey  = mExactQuerySqlOrderByKey;
    430             newTypeOfQuery      = 0;
    431             queryArgs           = mExactQueryArgs;
    432 
    433             queryArgs[ 0 ]      = keyString;
    434             break;
    435 
    436         case WnnDictionary.SEARCH_PREFIX:
    437         case WnnDictionary.SEARCH_LINK:
    438             /* Select the suitable parameters for the query */
    439             if( keyString.length() <= FAST_QUERY_LENGTH ) {
    440                 if( wnnWord != null ) {
    441                     querySqlOrderByFreq = mFastLinkQuerySqlOrderByFreq;
    442                     querySqlOrderByKey  = mFastLinkQuerySqlOrderByKey;
    443                     newTypeOfQuery      = 1;
    444                 } else {
    445                     querySqlOrderByFreq = mFastPrefixQuerySqlOrderByFreq;
    446                     querySqlOrderByKey  = mFastPrefixQuerySqlOrderByKey;
    447                     newTypeOfQuery      = 2;
    448                 }
    449                 maxBindsOfQuery     = FAST_QUERY_LENGTH;
    450                 queryArgs           = mFastQueryArgs;
    451             } else {
    452                 if( wnnWord != null ) {
    453                     querySqlOrderByFreq = mFullLinkQuerySqlOrderByFreq;
    454                     querySqlOrderByKey  = mFullLinkQuerySqlOrderByKey;
    455                     newTypeOfQuery      = 3;
    456                 } else {
    457                     querySqlOrderByFreq = mFullPrefixQuerySqlOrderByFreq;
    458                     querySqlOrderByKey  = mFullPrefixQuerySqlOrderByKey;
    459                     newTypeOfQuery      = 4;
    460                 }
    461                 maxBindsOfQuery     = MAX_LENGTH_OF_QUERY;
    462                 queryArgs           = mFullQueryArgs;
    463             }
    464 
    465             if( wnnWord != null ) {
    466                 /* If link search is enabled, insert information of the previous word */
    467                 String[] queryArgsTemp = OpenWnnDictionaryImplJni.createBindArray( this.mWnnWork, keyString, maxBindsOfQuery, MAX_PATTERN_OF_APPROX );
    468 
    469                 queryArgs = new String[ queryArgsTemp.length + 2 ];
    470                 for( int i = 0 ; i < queryArgsTemp.length ; i++ ) {
    471                     queryArgs[ i + 2 ] = queryArgsTemp[ i ];
    472                 }
    473 
    474                 queryArgs[ 0 ] = wnnWord.stroke;
    475                 queryArgs[ 1 ] = wnnWord.candidate;
    476             } else {
    477                 queryArgs = OpenWnnDictionaryImplJni.createBindArray( this.mWnnWork, keyString, maxBindsOfQuery, MAX_PATTERN_OF_APPROX );
    478             }
    479             break;
    480 
    481         default:
    482             mCountCursor = 0;
    483             freeCursor( );
    484             return;
    485         }
    486 
    487         /* Create the cursor and set arguments */
    488         mCountCursor = 0;
    489 
    490         if( mDbCursor == null || mTypeOfQuery != newTypeOfQuery ) {
    491             /* If the cursor is not exist or the type of query is changed, compile the query string and query words */
    492             freeCursor( );
    493 
    494             try {
    495                 switch( order ) {
    496                 case WnnDictionary.ORDER_BY_FREQUENCY:
    497                     mDbCursor = ( SQLiteCursor )mDbDic.rawQuery( querySqlOrderByFreq, queryArgs );
    498                     break;
    499                 case WnnDictionary.ORDER_BY_KEY:
    500                     mDbCursor = ( SQLiteCursor )mDbDic.rawQuery( querySqlOrderByKey, queryArgs );
    501                     break;
    502                 default:
    503                     return;
    504                 }
    505             } catch( SQLException e ) {
    506                 return;
    507             }
    508 
    509             mTypeOfQuery = newTypeOfQuery;
    510         } else {
    511             /* If the cursor is exist, bind new arguments and re-query words (DO NOT recompile the query string) */
    512             try {
    513                 mDbCursor.setSelectionArguments( queryArgs );
    514                 mDbCursor.requery( );
    515             } catch( SQLException e ) {
    516                 return;
    517             }
    518         }
    519 
    520         if( mDbCursor != null ) {
    521             /* If querying is succeed, count the number of words */
    522             mCountCursor = mDbCursor.getCount();
    523             if( mCountCursor == 0 ) {
    524                 /* If no word is retrieved, deactivate the cursor for reduce the resource */
    525                 mDbCursor.deactivate( );
    526             }
    527         }
    528 
    529         return;
    530     }
    531 
    532     /**
    533      * @see jp.co.omronsoft.openwnn.WnnDictionary#searchWord
    534      */
    535     public int searchWord( int operation, int order, String keyString ) {
    536         /* Unset the previous word information */
    537         OpenWnnDictionaryImplJni.clearResult( this.mWnnWork );
    538 
    539         /* Search to user/learn dictionary */
    540         if( mDbDic != null && ( mFrequencyOffsetOfUserDictionary  >= 0 ||
    541                                 mFrequencyOffsetOfLearnDictionary >= 0 ) ) {
    542             try {
    543                 if( keyString.length() > 0 ) {
    544                     createQuery( keyString, null, operation, order );
    545                     if( mDbCursor != null ) {
    546                         mDbCursor.moveToFirst();
    547                     }
    548                 } else {
    549                     /* If the key string is "", no word is retrieved */
    550                     if( mDbCursor != null ) {
    551                         mDbCursor.deactivate();
    552                     }
    553                     mCountCursor = 0;
    554                 }
    555             } catch( SQLException e ) {
    556                 if( mDbCursor != null ) {
    557                     mDbCursor.deactivate();
    558                 }
    559                 mCountCursor = 0;
    560             }
    561         } else {
    562             mCountCursor = 0;
    563         }
    564 
    565         /* Search to fixed dictionary */
    566         if( this.mWnnWork != 0 ) {
    567             int ret = OpenWnnDictionaryImplJni.searchWord( this.mWnnWork, operation, order, keyString );
    568             if (mCountCursor > 0) {
    569                 ret = 1;
    570             }
    571             return ret;
    572         } else {
    573             return -1;
    574         }
    575     }
    576 
    577     /**
    578      * @see jp.co.omronsoft.openwnn.WnnDictionary#searchWord
    579      */
    580     public int searchWord( int operation, int order, String keyString, WnnWord wnnWord ) {
    581         if( wnnWord == null || wnnWord.partOfSpeech == null ) {
    582             return -1;
    583         }
    584 
    585         /* Search to user/learn dictionary with link information */
    586         if( mDbDic != null && ( mFrequencyOffsetOfUserDictionary  >= 0 ||
    587                                 mFrequencyOffsetOfLearnDictionary >= 0 ) ) {
    588             try {
    589                 createQuery( keyString, wnnWord, operation, order );
    590                 if( mDbCursor != null ) {
    591                     mDbCursor.moveToFirst();
    592                 }
    593             } catch( SQLException e ) {
    594                 if( mDbCursor != null ) {
    595                     mDbCursor.deactivate();
    596                 }
    597                 mCountCursor = 0;
    598             }
    599         } else {
    600             mCountCursor = 0;
    601         }
    602 
    603         /* Search to fixed dictionary with link information */
    604         OpenWnnDictionaryImplJni.clearResult( this.mWnnWork );
    605         OpenWnnDictionaryImplJni.setStroke( this.mWnnWork, wnnWord.stroke );
    606         OpenWnnDictionaryImplJni.setCandidate( this.mWnnWork, wnnWord.candidate );
    607         OpenWnnDictionaryImplJni.setLeftPartOfSpeech( this.mWnnWork, wnnWord.partOfSpeech.left );
    608         OpenWnnDictionaryImplJni.setRightPartOfSpeech( this.mWnnWork, wnnWord.partOfSpeech.right );
    609         OpenWnnDictionaryImplJni.selectWord( this.mWnnWork );
    610 
    611         if( this.mWnnWork != 0 ) {
    612             int ret = OpenWnnDictionaryImplJni.searchWord( this.mWnnWork, operation, order, keyString );
    613             if (mCountCursor > 0) {
    614                 ret = 1;
    615             }
    616             return ret;
    617         } else {
    618             return -1;
    619         }
    620     }
    621 
    622     /**
    623      * @see jp.co.omronsoft.openwnn.WnnDictionary#getNextWord
    624      */
    625     public WnnWord getNextWord( ) {
    626         return getNextWord( 0 );
    627     }
    628 
    629     /**
    630      * @see jp.co.omronsoft.openwnn.WnnDictionary#getNextWord
    631      */
    632     public WnnWord getNextWord( int length ) {
    633         if( this.mWnnWork != 0 ) {
    634             if( mDbDic != null && mDbCursor != null && mCountCursor > 0 ) {
    635                 /* If the user/learn dictionary is queried, get the result from the user/learn dictionary */
    636                 WnnWord result = new WnnWord( );
    637                 try {
    638                     /* Skip results if that is not contained the type of search or length of stroke is not equal specified length */
    639                     while( mCountCursor > 0 &&
    640                            ( ( mFrequencyOffsetOfUserDictionary < 0  && mDbCursor.getInt( 4 ) == TYPE_NAME_USER      ) ||
    641                              ( mFrequencyOffsetOfLearnDictionary < 0 && mDbCursor.getInt( 4 ) == TYPE_NAME_LEARN     ) ||
    642                              ( length > 0                            && mDbCursor.getString( 0 ).length( ) != length ) ) ) {
    643                         mDbCursor.moveToNext();
    644                         mCountCursor--;
    645                     }
    646 
    647                     if( mCountCursor > 0 ) {
    648                         /* Get the information of word */
    649                         result.stroke               = mDbCursor.getString( 0 );
    650                         result.candidate            = mDbCursor.getString( 1 );
    651                         result.partOfSpeech.left    = mDbCursor.getInt( 2 );
    652                         result.partOfSpeech.right   = mDbCursor.getInt( 3 );
    653 
    654                         if( mDbCursor.getInt( 4 ) == TYPE_NAME_USER ) {
    655                             result.frequency        = mFrequencyOffsetOfUserDictionary;
    656                         } else {
    657                             result.frequency        = mFrequencyOffsetOfLearnDictionary;
    658                         }
    659 
    660                         /* Move cursor to next result. If the next result is not exist, deactivate the cursor */
    661                         mDbCursor.moveToNext();
    662                         if( --mCountCursor <= 0 ) {
    663                             mDbCursor.deactivate();
    664                         }
    665 
    666                         return result;
    667                     } else {
    668                         /* if no result is found, terminate the searching of user/learn dictionary */
    669                         mDbCursor.deactivate();
    670                         result = null;
    671                     }
    672                 } catch( SQLException e ) {
    673                     mDbCursor.deactivate();
    674                     mCountCursor = 0;
    675                     result = null;
    676                 }
    677             }
    678 
    679             /* Get the result from fixed dictionary */
    680             int res = OpenWnnDictionaryImplJni.getNextWord( this.mWnnWork, length );
    681             if( res > 0 ) {
    682                 WnnWord result = new WnnWord( );
    683                 if( result != null ) {
    684                     result.stroke               = OpenWnnDictionaryImplJni.getStroke( this.mWnnWork );
    685                     result.candidate            = OpenWnnDictionaryImplJni.getCandidate( this.mWnnWork );
    686                     result.frequency            = OpenWnnDictionaryImplJni.getFrequency( this.mWnnWork );
    687                     result.partOfSpeech.left    = OpenWnnDictionaryImplJni.getLeftPartOfSpeech( this.mWnnWork );
    688                     result.partOfSpeech.right   = OpenWnnDictionaryImplJni.getRightPartOfSpeech( this.mWnnWork );
    689                 }
    690                 return result;
    691             } else if ( res == 0 ) {
    692                 /* No result is found. */
    693                 return null;
    694             } else {
    695                 /* An error occur (It is regarded as "No result is found".) */
    696                 return null;
    697             }
    698         } else {
    699             return null;
    700         }
    701     }
    702 
    703     /**
    704      * @see jp.co.omronsoft.openwnn.WnnDictionary#getUserDictionaryWords
    705      */
    706     public WnnWord[] getUserDictionaryWords( ) {
    707         if( this.mWnnWork != 0 && mDbDic != null ) {
    708             int numOfWords, i;
    709             SQLiteCursor cursor = null;
    710 
    711             try {
    712                 /* Count all words in the user dictionary */
    713                 cursor = ( SQLiteCursor )mDbDic.query(
    714                     TABLE_NAME_DIC,
    715                     new String[] { COLUMN_NAME_STROKE, COLUMN_NAME_CANDIDATE },
    716                     String.format( "%s=%d", COLUMN_NAME_TYPE, TYPE_NAME_USER ),
    717                     null, null, null, null);
    718                 numOfWords = cursor.getCount();
    719 
    720                 if( numOfWords > 0 ) {
    721                     /* Retrieve all words in the user dictionary */
    722                     WnnWord[] words = new WnnWord[ numOfWords ];
    723 
    724                     cursor.moveToFirst();
    725                     for( i = 0 ; i < numOfWords ; i++ ) {
    726                         words[ i ] = new WnnWord();
    727                         words[ i ].stroke       = cursor.getString( 0 );
    728                         words[ i ].candidate    = cursor.getString( 1 );
    729                         cursor.moveToNext();
    730                     }
    731 
    732                     return words;
    733                 }
    734             } catch( SQLException e ) {
    735                 /* An error occurs */
    736                 return null;
    737             } finally {
    738                 if( cursor != null ) {
    739                     cursor.close( );
    740                 }
    741             }
    742         }
    743         return null;
    744     }
    745 
    746     /**
    747      * @see jp.co.omronsoft.openwnn.WnnDictionary#clearApproxPattern
    748      */
    749     public void clearApproxPattern( ) {
    750         if( this.mWnnWork != 0 ) {
    751             OpenWnnDictionaryImplJni.clearApproxPatterns( this.mWnnWork );
    752         }
    753     }
    754 
    755     /**
    756      * @see jp.co.omronsoft.openwnn.WnnDictionary#setApproxPattern
    757      */
    758     public int setApproxPattern( String src, String dst ) {
    759         if( this.mWnnWork != 0 ) {
    760             return OpenWnnDictionaryImplJni.setApproxPattern( this.mWnnWork, src, dst );
    761         } else {
    762             return -1;
    763         }
    764     }
    765 
    766     /**
    767      * @see jp.co.omronsoft.openwnn.WnnDictionary#setApproxPattern
    768      */
    769     public int setApproxPattern( int approxPattern ) {
    770         if( this.mWnnWork != 0 ) {
    771             return OpenWnnDictionaryImplJni.setApproxPattern( this.mWnnWork, approxPattern );
    772         } else {
    773             return -1;
    774         }
    775     }
    776 
    777     /**
    778      * @see jp.co.omronsoft.openwnn.WnnDictionary#getConnectMatrix
    779      */
    780     public byte[][] getConnectMatrix( ) {
    781         byte[][]    result;
    782         int         lcount, i;
    783 
    784         if (this.mWnnWork != 0) {
    785             /* 1-origin */
    786             lcount = OpenWnnDictionaryImplJni.getNumberOfLeftPOS( this.mWnnWork );
    787             result = new byte[ lcount + 1 ][ ];
    788 
    789             if( result != null ) {
    790                 for( i = 0 ; i < lcount + 1 ; i++ ) {
    791                     result[ i ] = OpenWnnDictionaryImplJni.getConnectArray( this.mWnnWork, i );
    792 
    793                     if( result[ i ] == null ) {
    794                         return null;
    795                     }
    796                 }
    797             }
    798         } else {
    799             result = new byte[1][1];
    800         }
    801         return result;
    802     }
    803 
    804     /**
    805      * @see jp.co.omronsoft.openwnn.WnnDictionary#getPOS
    806      */
    807     public WnnPOS getPOS( int type ) {
    808         WnnPOS result = new WnnPOS( );
    809 
    810         if( this.mWnnWork != 0 && result != null ) {
    811             result.left  = OpenWnnDictionaryImplJni.getLeftPartOfSpeechSpecifiedType( this.mWnnWork, type );
    812             result.right = OpenWnnDictionaryImplJni.getRightPartOfSpeechSpecifiedType( this.mWnnWork, type );
    813 
    814             if( result.left < 0 || result.right < 0 ) {
    815                 return null;
    816             }
    817         }
    818         return result;
    819     }
    820 
    821     /**
    822      * @see jp.co.omronsoft.openwnn.WnnDictionary#clearUserDictionary
    823      */
    824     public int clearUserDictionary() {
    825         if( mDbDic != null ) {
    826             mDbDic.execSQL( String.format( "delete from %s where %s=%d", TABLE_NAME_DIC, COLUMN_NAME_TYPE, TYPE_NAME_USER ) );
    827         }
    828 
    829         /* If no writable dictionary exists, no error occurs. */
    830         return 0;
    831     }
    832 
    833     /**
    834      * @see jp.co.omronsoft.openwnn.WnnDictionary#clearLearnDictionary
    835      */
    836     public int clearLearnDictionary() {
    837         if( mDbDic != null ) {
    838             mDbDic.execSQL( String.format( "delete from %s where %s=%d", TABLE_NAME_DIC, COLUMN_NAME_TYPE, TYPE_NAME_LEARN ) );
    839         }
    840 
    841         /* If no writable dictionary exists, no error occurs. */
    842         return 0;
    843     }
    844 
    845     /**
    846      * @see jp.co.omronsoft.openwnn.WnnDictionary#addWordToUserDictionary
    847      */
    848     public int addWordToUserDictionary( WnnWord[] word ) {
    849         int result = 0;
    850 
    851         if( mDbDic != null ) {
    852             SQLiteCursor cursor;
    853 
    854             /* Count all words in the user dictionary */
    855             cursor = ( SQLiteCursor )mDbDic.query(
    856                 TABLE_NAME_DIC,
    857                 new String[] { COLUMN_NAME_ID },
    858                 String.format( "%s=%d", COLUMN_NAME_TYPE, TYPE_NAME_USER ),
    859                 null, null, null, null);
    860 
    861             int count = cursor.getCount();
    862             cursor.close();
    863 
    864             if( count + word.length > MAX_WORDS_IN_USER_DICTIONARY ) {
    865                 /* If user dictionary is full, an error occurs. */
    866                 return -1;
    867             } else {
    868                 mDbDic.beginTransaction();
    869                 try {
    870                     StringBuilder strokeSQL    = new StringBuilder();
    871                     StringBuilder candidateSQL = new StringBuilder();
    872 
    873                     for( int index = 0 ; index < word.length ; index++ ) {
    874                         if( word[index].stroke.length()    > 0 && word[index].stroke.length()    <= MAX_STROKE_LENGTH &&
    875                             word[index].candidate.length() > 0 && word[index].candidate.length() <= MAX_CANDIDATE_LENGTH ) {
    876                             strokeSQL.setLength( 0 );
    877                             candidateSQL.setLength( 0 );
    878                             DatabaseUtils.appendEscapedSQLString( strokeSQL, word[index].stroke );
    879                             DatabaseUtils.appendEscapedSQLString( candidateSQL, word[index].candidate );
    880 
    881                             cursor = ( SQLiteCursor )mDbDic.query(
    882                                 TABLE_NAME_DIC,
    883                                 new String[] { COLUMN_NAME_ID },
    884                                 String.format( "%s=%d and %s=%s and %s=%s",
    885                                                COLUMN_NAME_TYPE, TYPE_NAME_USER,
    886                                                COLUMN_NAME_STROKE, strokeSQL.toString(),
    887                                                COLUMN_NAME_CANDIDATE, candidateSQL.toString() ),
    888                                 null, null, null, null );
    889 
    890                             if( cursor.getCount() > 0 ) {
    891                                 /* if the specified word is exist, an error reported and skipped that word. */
    892                                 result = -2;
    893                             } else {
    894                                 ContentValues content = new ContentValues();
    895 
    896                                 content.clear();
    897                                 content.put( COLUMN_NAME_TYPE,      TYPE_NAME_USER );
    898                                 content.put( COLUMN_NAME_STROKE,    word[index].stroke );
    899                                 content.put( COLUMN_NAME_CANDIDATE, word[index].candidate );
    900                                 content.put( COLUMN_NAME_POS_LEFT,  word[index].partOfSpeech.left );
    901                                 content.put( COLUMN_NAME_POS_RIGHT, word[index].partOfSpeech.right );
    902 
    903                                 mDbDic.insert( TABLE_NAME_DIC, null, content );
    904                             }
    905 
    906                             cursor.close( );
    907                             cursor = null;
    908                         }
    909                     }
    910                     mDbDic.setTransactionSuccessful();
    911                 } catch( SQLException e ) {
    912                     /* An error occurs */
    913                     return -1;
    914                 } finally {
    915                     mDbDic.endTransaction();
    916                     if( cursor != null ) {
    917                         cursor.close( );
    918                     }
    919                 }
    920             }
    921         }
    922 
    923         /* If no writable dictionary exists, no error occurs. */
    924         return result;
    925     }
    926 
    927     /**
    928      * @see jp.co.omronsoft.openwnn.WnnDictionary#addWordToUserDictionary
    929      */
    930     public int addWordToUserDictionary( WnnWord word ) {
    931         WnnWord[] words = new WnnWord[1];
    932         words[0] = word;
    933 
    934         return addWordToUserDictionary( words );
    935     }
    936 
    937     /**
    938      * @see jp.co.omronsoft.openwnn.WnnDictionary#removeWordFromUserDictionary
    939      */
    940     public int removeWordFromUserDictionary( WnnWord[] word ) {
    941         if( mDbDic != null ) {
    942             /* Remove the specified word */
    943             mDbDic.beginTransaction();
    944             try {
    945                 StringBuilder strokeSQL    = new StringBuilder();
    946                 StringBuilder candidateSQL = new StringBuilder();
    947 
    948                 for( int index = 0 ; index < word.length ; index++ ) {
    949                     if( word[index].stroke.length()    > 0 && word[index].stroke.length()    <= MAX_STROKE_LENGTH &&
    950                         word[index].candidate.length() > 0 && word[index].candidate.length() <= MAX_CANDIDATE_LENGTH ) {
    951                         strokeSQL.setLength( 0 );
    952                         candidateSQL.setLength( 0 );
    953                         DatabaseUtils.appendEscapedSQLString( strokeSQL, word[index].stroke );
    954                         DatabaseUtils.appendEscapedSQLString( candidateSQL, word[index].candidate );
    955 
    956                         mDbDic.delete( TABLE_NAME_DIC,
    957                             String.format( "%s=%d and %s=%s and %s=%s",
    958                                            COLUMN_NAME_TYPE, TYPE_NAME_USER,
    959                                            COLUMN_NAME_STROKE, strokeSQL,
    960                                            COLUMN_NAME_CANDIDATE, candidateSQL ),
    961                             null );
    962                     }
    963                 }
    964                 mDbDic.setTransactionSuccessful();
    965             } catch( SQLException e ) {
    966                 /* An error occurs */
    967                 return -1;
    968             } finally {
    969                 mDbDic.endTransaction();
    970             }
    971         }
    972 
    973         /* If no writable dictionary exists, no error occurs. */
    974         return 0;
    975     }
    976 
    977     /**
    978      * @see jp.co.omronsoft.openwnn.WnnDictionary#removeWordFromUserDictionary
    979      */
    980     public int removeWordFromUserDictionary( WnnWord word ) {
    981         WnnWord[] words = new WnnWord[1];
    982         words[0] = word;
    983 
    984         return removeWordFromUserDictionary( words );
    985     }
    986 
    987     /**
    988      * @see jp.co.omronsoft.openwnn.WnnDictionary#learnWord
    989      */
    990     public int learnWord( WnnWord word ) {
    991         return learnWord( word, null );
    992     }
    993 
    994     /**
    995      * Learn the word with connection.
    996      *
    997      * @param word              The word to learn
    998      * @param previousWord      The word which is selected previously.
    999      * @return                  0 if success; minus value if fail.
   1000      */
   1001     public int learnWord( WnnWord word, WnnWord previousWord ) {
   1002         if( mDbDic != null ) {
   1003             StringBuilder previousStrokeSQL    = new StringBuilder();
   1004             StringBuilder previousCandidateSQL = new StringBuilder();
   1005 
   1006             if( previousWord != null &&
   1007                 previousWord.stroke.length()    > 0 && previousWord.stroke.length()    <= MAX_STROKE_LENGTH &&
   1008                 previousWord.candidate.length() > 0 && previousWord.candidate.length() <= MAX_CANDIDATE_LENGTH ) {
   1009                 DatabaseUtils.appendEscapedSQLString( previousStrokeSQL, previousWord.stroke );
   1010                 DatabaseUtils.appendEscapedSQLString( previousCandidateSQL, previousWord.candidate );
   1011                 /* If the information of previous word is set, perform the link learning */
   1012             }
   1013 
   1014             if( word.stroke.length()    > 0 && word.stroke.length()    <= MAX_STROKE_LENGTH &&
   1015                 word.candidate.length() > 0 && word.candidate.length() <= MAX_CANDIDATE_LENGTH ) {
   1016                 StringBuilder strokeSQL    = new StringBuilder();
   1017                 StringBuilder candidateSQL = new StringBuilder();
   1018                 DatabaseUtils.appendEscapedSQLString( strokeSQL, word.stroke );
   1019                 DatabaseUtils.appendEscapedSQLString( candidateSQL, word.candidate );
   1020 
   1021                 SQLiteCursor cursor;
   1022 
   1023                 /* Count the number of registered words and retrieve that words ascending by the ID */
   1024                 cursor = ( SQLiteCursor )mDbDic.query(
   1025                     TABLE_NAME_DIC,
   1026                     new String[] { COLUMN_NAME_STROKE, COLUMN_NAME_CANDIDATE },
   1027                     String.format( "%s=%d", COLUMN_NAME_TYPE, TYPE_NAME_LEARN ),
   1028                     null, null, null,
   1029                     String.format( "%s ASC", COLUMN_NAME_ID ) );
   1030 
   1031                 if( cursor.getCount( ) >= MAX_WORDS_IN_LEARN_DICTIONARY ) {
   1032                     /* If a registering space is short, delete the words that contain same stroke and candidate to the oldest word */
   1033                     mDbDic.beginTransaction();
   1034                     try {
   1035                         cursor.moveToFirst( );
   1036 
   1037                         StringBuilder oldestStrokeSQL    = new StringBuilder();
   1038                         StringBuilder oldestCandidateSQL = new StringBuilder();
   1039                         DatabaseUtils.appendEscapedSQLString( oldestStrokeSQL, cursor.getString( 0 ) );
   1040                         DatabaseUtils.appendEscapedSQLString( oldestCandidateSQL, cursor.getString( 1 ) );
   1041 
   1042                         mDbDic.delete( TABLE_NAME_DIC,
   1043                             String.format( "%s=%d and %s=%s and %s=%s",
   1044                                            COLUMN_NAME_TYPE, TYPE_NAME_LEARN,
   1045                                            COLUMN_NAME_STROKE, oldestStrokeSQL.toString( ),
   1046                                            COLUMN_NAME_CANDIDATE, oldestCandidateSQL.toString( ) ),
   1047                             null );
   1048 
   1049                         mDbDic.setTransactionSuccessful();
   1050                     } catch( SQLException e ) {
   1051                         return -1;
   1052                     } finally {
   1053                         mDbDic.endTransaction();
   1054                         cursor.close();
   1055                     }
   1056                 } else {
   1057                     cursor.close();
   1058                 }
   1059 
   1060                 /* learning the word */
   1061                 ContentValues content = new ContentValues();
   1062 
   1063                 content.clear();
   1064                 content.put( COLUMN_NAME_TYPE,                   TYPE_NAME_LEARN );
   1065                 content.put( COLUMN_NAME_STROKE,                 word.stroke );
   1066                 content.put( COLUMN_NAME_CANDIDATE,              word.candidate );
   1067                 content.put( COLUMN_NAME_POS_LEFT,               word.partOfSpeech.left );
   1068                 content.put( COLUMN_NAME_POS_RIGHT,              word.partOfSpeech.right );
   1069                 if( previousWord != null ) {
   1070                     content.put( COLUMN_NAME_PREVIOUS_STROKE,    previousWord.stroke );
   1071                     content.put( COLUMN_NAME_PREVIOUS_CANDIDATE, previousWord.candidate );
   1072                     content.put( COLUMN_NAME_PREVIOUS_POS_LEFT,  previousWord.partOfSpeech.left );
   1073                     content.put( COLUMN_NAME_PREVIOUS_POS_RIGHT, previousWord.partOfSpeech.right );
   1074                 }
   1075 
   1076                 mDbDic.beginTransaction();
   1077                 try {
   1078                     mDbDic.insert( TABLE_NAME_DIC, null, content );
   1079                     mDbDic.setTransactionSuccessful();
   1080                 } catch( SQLException e ) {
   1081                     mDbDic.endTransaction();
   1082                     return -1;
   1083                 } finally {
   1084                     mDbDic.endTransaction();
   1085                 }
   1086             }
   1087         }
   1088 
   1089         /* If no writable dictionary exists, no error occurs. */
   1090         return 0;
   1091     }
   1092 }
   1093