• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
OpenWnnDictionaryImpl( String dicLibPath )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      */
OpenWnnDictionaryImpl( String dicLibPath, String dicFilePath )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      */
finalize( )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      */
createDictionaryTable( String tableName )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      */
freeDatabase( )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      */
freeCursor( )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      */
isActive()344     public boolean isActive() {
345         return (this.mWnnWork != 0);
346     }
347 
348     /**
349      * @see jp.co.omronsoft.openwnn.WnnDictionary#setInUseState
350      */
setInUseState( boolean flag )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      */
clearDictionary( )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      */
setDictionary(int index, int base, int high )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      */
createQuery( String keyString, WnnWord wnnWord, int operation, int order)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      */
searchWord( int operation, int order, String keyString )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      */
searchWord( int operation, int order, String keyString, WnnWord wnnWord )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      */
getNextWord( )621     public WnnWord getNextWord( ) {
622         return getNextWord( 0 );
623     }
624 
625     /**
626      * @see jp.co.omronsoft.openwnn.WnnDictionary#getNextWord
627      */
getNextWord( int length )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      */
getUserDictionaryWords( )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      */
clearApproxPattern( )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      */
setApproxPattern( String src, String dst )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      */
setApproxPattern( int approxPattern )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      */
getConnectMatrix( )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      */
getPOS( int type )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      */
clearUserDictionary()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      */
clearLearnDictionary()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      */
addWordToUserDictionary( WnnWord[] word )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      */
addWordToUserDictionary( WnnWord word )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      */
removeWordFromUserDictionary( WnnWord[] word )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      */
removeWordFromUserDictionary( WnnWord word )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      */
learnWord( WnnWord word )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      */
learnWord( WnnWord word, WnnWord previousWord )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