1 #define OV_DEBUG
2 
3 #include <vector>
4 #include <algorithm>
5 
6 #include "DictionarySingleton.h"
7 
8 #ifndef WIN32
9 	#include <OpenVanilla/OVUtility.h>
10 #else
11 	#include "OVUtility.h"
12 #endif
13 
14 using namespace std;
15 
16 DictionarySingleton* DictionarySingleton::itsInstance = NULL;
17 SQLite3* DictionarySingleton::dictionaryDB = NULL;
18 
DictionarySingleton(const char * dbFilePath)19 DictionarySingleton::DictionarySingleton(
20     const char* dbFilePath)
21 {
22     murmur("new DictionarySingleton");
23 
24     DictionarySingleton::dictionaryDB = new SQLite3;
25     if (int err = DictionarySingleton::dictionaryDB->open(dbFilePath)) {
26         murmur("SQLite3 error! code=%d", err);
27     }
28 }
29 
~DictionarySingleton()30 DictionarySingleton::~DictionarySingleton()
31 {
32     delete DictionarySingleton::dictionaryDB;
33 }
34 
lostInstance()35 void DictionarySingleton::lostInstance()
36 {
37 	if(DictionarySingleton::itsInstance)
38 	{
39 		delete DictionarySingleton::itsInstance;
40 	}
41 }
42 
isVocabulary(string characters)43 bool DictionarySingleton::isVocabulary(string characters)
44 {
45     string strTableName = DictionarySingleton::inputMethodId;
46     strTableName += "_char2word_table";
47     string strColumnWordID = strTableName + ".wordID";
48     string strColumnCharacters = strTableName + ".characters";
49 
50     string selectString("SELECT count(" + strColumnWordID + ")");
51     string fromString(" FROM ");
52     fromString += strTableName;
53     string whereString(" WHERE ");
54     whereString += strColumnCharacters + " = '" + characters + "'";
55     string commandString = selectString + fromString + whereString;
56 
57     SQLite3Statement *sth =
58         DictionarySingleton::dictionaryDB->prepare(commandString.c_str());
59     if (!sth) {
60         murmur("illegal SQL statement[%s]?", commandString.c_str());
61         return false;
62     }
63 
64     if (sth->step() == SQLITE_ROW) {
65         int count = sth->column_int(0);
66         murmur("found[%d]", count);
67         delete sth;
68 
69         if (count > 0)  return true;
70         else            return false;
71     }
72     else
73     {
74         delete sth;
75         murmur("found count but encountered error?");
76         return false;
77     }
78 }
79 
getVocabulariesByKeystrokes(string keystrokes,vector<Vocabulary> & vocabulariesRef)80 bool DictionarySingleton::getVocabulariesByKeystrokes(
81 	string keystrokes, vector<Vocabulary>& vocabulariesRef)
82 {
83     /// key2word table schema:  |key|wordID|
84     /// word table schema:      |wordID|word|
85     /// frequency table schema: |wordID|freq|
86     ///
87     /// The reasons why to separate into 3 tables are:
88     /// 1. Characters can be different by different input methods.
89     /// 2. Different frequency table represents different "context" or
90     ///    "user profile," for example, "freq_zh_Hant_contextBar."
91     /// 3. Support words in zh_Hans and zh_Holo, etc.
92     ///
93     /// A SQL statement for example:
94     /// SELECT word_zh_Hant.word, freq_zh_Hant_generic.freq, key2word_bpmf.ord
95     /// FROM key2word_bpmf, word_zh_Hant, freq_zh_Hant_generic
96     /// WHERE key2word_bpmf.key = 'foo'
97     ///     AND word_zh_Hant.wordID = key2word_bpmf.wordID
98     ///     AND word_zh_Hant.wordID = freq_zh_Hant_generic.wordID
99     /// ORDER BY freq_zh_Hant_generic.freq DESC"
100     ///
101     /// Since there're two inner joins,
102     /// the order of tables and columns are very very important.
103 
104     /// bind_foo seems not work on table/column name (sure it can't!),
105     /// so use stupid concat...
106     string tableIM = "key2word_" + DictionarySingleton::inputMethodId;
107     string lang = "zh_Hant";
108     string tableWord = "word_" + lang;
109     string tableFreq = "freq_" + lang;
110     string selectString("SELECT ");
111     selectString +=
112     	tableWord + ".word, " + tableFreq + ".freq, " + tableIM + ".ord";
113     string fromString(" FROM ");
114     fromString += tableIM + ", " + tableWord + ", " + tableFreq;
115     string whereString(" WHERE ");
116     whereString += tableIM + ".key = '" + keystrokes + "'";
117     whereString += " AND " + tableWord + ".wordID = " + tableIM + ".wordID" +
118         " AND " + tableWord + ".wordID = " + tableFreq + ".wordID";
119     whereString += " ORDER BY " + tableFreq + ".freq DESC";
120     string commandString = selectString + fromString + whereString;
121 
122     SQLite3Statement *sth =
123         DictionarySingleton::dictionaryDB->prepare(commandString.c_str());
124     if (!sth) {
125         murmur("illegal SQL statement[%s]?", commandString.c_str());
126         return false;
127     }
128 
129     int rows = 0;
130     while (sth->step() == SQLITE_ROW) rows++;
131 
132     murmur("query string=%s, number of candidates=%d",
133         keystrokes.c_str(), rows);
134     if (!rows) {
135         delete sth;
136         return false;
137     }
138 
139     sth->reset();
140 
141     // count token numbers by '\t'.
142     int length = 1;
143     string::size_type index = 0;
144     while(index != string::npos) {
145 	    index = keystrokes.find('\t', index);
146 	    if(index != string::npos)
147 	    	length++;
148 	}
149 
150     while (sth->step() == SQLITE_ROW) {
151         const char* word = sth->column_text(0);
152         murmur("found[%s]", word);
153         int freq = sth->column_int(1);
154         int order = sth->column_int(2);
155         Vocabulary currentVocabulary;
156         currentVocabulary.word = string(word);
157         currentVocabulary.freq = freq;
158         currentVocabulary.order = order;
159         currentVocabulary.length = length;
160 
161         vocabulariesRef.push_back(currentVocabulary);
162     }
163     delete sth;
164     return true;
165 }
166