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