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