|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include "pltables.h" |
|
19 #include "dbsqlconstants.h" |
|
20 #include "cpcskeymap.h" |
|
21 #include "cntitem.h" |
|
22 #include <QStringList> |
|
23 #include "predictivesearchlog.h" |
|
24 |
|
25 |
|
26 // How many characters from the beginning of the first name and last name are |
|
27 // stored. This only affects how precisely the results are put in alphabetic order. |
|
28 const TInt KCharactersFromName = 16; |
|
29 |
|
30 |
|
31 /** |
|
32 Destructor |
|
33 */ |
|
34 CPplPredictiveSearchTableBase::~CPplPredictiveSearchTableBase() |
|
35 { |
|
36 PRINT(_L("CPplPredictiveSearchTableBase dtor")); |
|
37 delete iInsertStmnt; |
|
38 delete iDeleteStmnt; |
|
39 delete iKeyMap; |
|
40 PRINT(_L("CPplPredictiveSearchTableBase dtor ends")); |
|
41 } |
|
42 |
|
43 |
|
44 /** |
|
45 @param aItem A contact item whose data are added to the table. |
|
46 */ |
|
47 void CPplPredictiveSearchTableBase::CreateInDbL(CContactItem& aItem) |
|
48 { |
|
49 PRINT(_L("CPplPredictiveSearchTableBase::CreateInDbL")); |
|
50 WriteToDbL(aItem); |
|
51 PRINT(_L("CPplPredictiveSearchTableBase::CreateInDbL ends")); |
|
52 } |
|
53 |
|
54 |
|
55 /** |
|
56 Update is done in two steps: delete contact from all predictive search tables, |
|
57 then insert it into relevant tables. |
|
58 |
|
59 @param aItem A contact item whose data is updated in the database. |
|
60 */ |
|
61 void CPplPredictiveSearchTableBase::UpdateL(const CContactItem& aItem) |
|
62 { |
|
63 PRINT(_L("CPplPredictiveSearchTableBase::UpdateL")); |
|
64 |
|
65 TBool lowDiskErrorOccurred(EFalse); |
|
66 DeleteFromAllTablesL(aItem.Id(), lowDiskErrorOccurred); |
|
67 if (lowDiskErrorOccurred) |
|
68 { |
|
69 User::Leave(KErrGeneral); |
|
70 } |
|
71 WriteToDbL(aItem); |
|
72 |
|
73 PRINT(_L("CPplPredictiveSearchTableBase::UpdateL ends")); |
|
74 } |
|
75 |
|
76 |
|
77 /** |
|
78 Deletes a contact item from predictive search tables. |
|
79 |
|
80 @param aItem The contact item to be deleted. It contains contact id, but not |
|
81 first name or last name fields. |
|
82 */ |
|
83 void CPplPredictiveSearchTableBase::DeleteL(const CContactItem& aItem, |
|
84 TBool& aLowDiskErrorOccurred) |
|
85 { |
|
86 PRINT(_L("CPplPredictiveSearchTableBase::DeleteL")); |
|
87 |
|
88 DeleteFromAllTablesL(aItem.Id(), aLowDiskErrorOccurred); |
|
89 |
|
90 PRINT(_L("CPplPredictiveSearchTableBase::DeleteL ends")); |
|
91 } |
|
92 |
|
93 |
|
94 /** |
|
95 Default implementation returns empty list. |
|
96 */ |
|
97 QStringList CPplPredictiveSearchTableBase::GetTableSpecificFields( |
|
98 const CContactItem& /*aItem*/, |
|
99 TBool& aMandatoryFieldsPresent) const |
|
100 { |
|
101 aMandatoryFieldsPresent = ETrue; |
|
102 QStringList emptyList; |
|
103 return emptyList; |
|
104 } |
|
105 |
|
106 |
|
107 HBufC* CPplPredictiveSearchTableBase::GetNextTableNameL(QList<QChar>& aTables) const |
|
108 { |
|
109 HBufC* tableName(NULL); |
|
110 if (aTables.count() > 0) |
|
111 { |
|
112 tableName = TableNameL(aTables[0]); |
|
113 aTables.removeFirst(); |
|
114 // PRINT1(_L("CPplPredictiveSearchTableBase::GetNextTableNameL '%S'"), tableName); |
|
115 } |
|
116 return tableName; |
|
117 } |
|
118 |
|
119 |
|
120 /** |
|
121 Set up the CCntSqlStatement objects held by the class. |
|
122 */ |
|
123 void CPplPredictiveSearchTableBase::ConstructL() |
|
124 { |
|
125 PRINT(_L("CPplPredictiveSearchTableBase::ConstructL")); |
|
126 |
|
127 // Using dummy table names here |
|
128 TCntSqlStatementType insertType(EInsert, KSqlContactPredSearchTable0); |
|
129 TCntSqlStatementType deleteType(EDelete, KSqlContactPredSearchTable0); |
|
130 iInsertStmnt = TSqlProvider::GetSqlStatementL(insertType); |
|
131 // Details of INSERT are done in subclass |
|
132 |
|
133 |
|
134 const TInt KWhereContactIdBufSize( |
|
135 KWhereStringEqualsStringFormatText().Size() + |
|
136 KPredSearchContactId().Size() + |
|
137 KPredSearchContactIdParam().Size() ); |
|
138 HBufC* whereContactIdClause = HBufC::NewLC(KWhereContactIdBufSize); |
|
139 // for WHERE contact_id = [contact id value] |
|
140 whereContactIdClause->Des().AppendFormat(KWhereStringEqualsStringFormatText, |
|
141 &KPredSearchContactId, &KPredSearchContactIdParam); |
|
142 |
|
143 // Delete information of a particular contact item |
|
144 // DELETE FROM predictivesearchX (X=0..11) |
|
145 // WHERE contact_id = [contact id value]; |
|
146 iDeleteStmnt = TSqlProvider::GetSqlStatementL(deleteType); |
|
147 iDeleteStmnt->SetConditionL(*whereContactIdClause); |
|
148 CleanupStack::PopAndDestroy(whereContactIdClause); |
|
149 |
|
150 PRINT(_L("CPplPredictiveSearchTableBase::ConstructL ends")); |
|
151 } |
|
152 |
|
153 |
|
154 /** |
|
155 Constructor |
|
156 */ |
|
157 CPplPredictiveSearchTableBase::CPplPredictiveSearchTableBase( |
|
158 RSqlDatabase& aDatabase, TInt aMaxTokens, TInt aMaxTokenLength) : |
|
159 iDatabase(aDatabase), |
|
160 iMaxTokens(aMaxTokens), |
|
161 iMaxTokenLength(aMaxTokenLength) |
|
162 { |
|
163 } |
|
164 |
|
165 |
|
166 QList<QChar> CPplPredictiveSearchTableBase::DetermineTables(QStringList aTokens) const |
|
167 { |
|
168 QList<QChar> tables; |
|
169 for (TInt i = aTokens.count() - 1; i >= 0; --i) |
|
170 { |
|
171 QChar ch = aTokens[i][0]; |
|
172 __ASSERT_ALWAYS(IsValidChar(ch), |
|
173 User::Panic(_L("DetermineTables"), KErrArgument)); |
|
174 if (!tables.contains(ch)) |
|
175 { |
|
176 tables.append(ch); |
|
177 } |
|
178 } |
|
179 return tables; |
|
180 } |
|
181 |
|
182 |
|
183 // Insert a contact to predictive search tables. |
|
184 // Write contact's all tokens to each associate pred.search table. |
|
185 // E.g. if FN="11 22" LN="2 333", write "11","22","2" and "333" to tables 1, 2 and 3. |
|
186 void CPplPredictiveSearchTableBase::WriteToDbL(const CContactItem& aItem) |
|
187 { |
|
188 PRINT(_L("CPplPredictiveSearchTableBase::WriteToDbL")); |
|
189 |
|
190 HBufC* firstNameAsNbr(NULL); // owned |
|
191 HBufC* lastNameAsNbr(NULL); // owned |
|
192 HBufC* firstName(NULL); // owned |
|
193 HBufC* lastName(NULL); // owned |
|
194 GetFieldsLC(aItem, &firstNameAsNbr, &lastNameAsNbr, &firstName, &lastName); |
|
195 |
|
196 QStringList tokens; |
|
197 QList<QChar> tables; |
|
198 QT_TRYCATCH_LEAVING({ |
|
199 TBool mandatoryFieldsPresent(EFalse); |
|
200 QStringList tableSpecificFields = |
|
201 GetTableSpecificFields(aItem, mandatoryFieldsPresent); |
|
202 if (mandatoryFieldsPresent) |
|
203 { |
|
204 tokens = GetTokens(tableSpecificFields, firstNameAsNbr, lastNameAsNbr); |
|
205 tables = DetermineTables(tokens); |
|
206 } |
|
207 }); |
|
208 |
|
209 HBufC* tableName(NULL); |
|
210 while ((tableName = GetNextTableNameL(tables)) != NULL) |
|
211 { |
|
212 // Takes ownership. Clears also earlier SQL statement. |
|
213 iInsertStmnt->SetTableName(tableName); |
|
214 RSqlStatement stmnt; |
|
215 CleanupClosePushL( stmnt ); |
|
216 PRINT1(_L("CPplPredictiveSearchTableBase::WriteToDbL SQL='%S'"), |
|
217 &iInsertStmnt->SqlStringL()); |
|
218 stmnt.PrepareL(iDatabase, iInsertStmnt->SqlStringL()); |
|
219 |
|
220 // TODO: while this works, it is inefficient, since the BIGINT values are |
|
221 // computed in every iteration of the while-loop, even though the data is |
|
222 // always the same. |
|
223 FillKeyboardSpecificFieldsL(stmnt, tokens); |
|
224 |
|
225 User::LeaveIfError(stmnt.BindInt( |
|
226 User::LeaveIfError(stmnt.ParameterIndex(KPredSearchContactIdParam)), |
|
227 aItem.Id())); |
|
228 |
|
229 if (firstName) |
|
230 { |
|
231 User::LeaveIfError(stmnt.BindText( |
|
232 User::LeaveIfError(stmnt.ParameterIndex(KPredSearchFirstNameParam)), |
|
233 *firstName)); |
|
234 } |
|
235 if (lastName) |
|
236 { |
|
237 User::LeaveIfError(stmnt.BindText( |
|
238 User::LeaveIfError(stmnt.ParameterIndex(KPredSearchLastNameParam)), |
|
239 *lastName)); |
|
240 } |
|
241 |
|
242 // PRINT(_L("CPplPredictiveSearchTableBase::WriteToDbL execute SQL statement")); |
|
243 // Execute the SQL statement |
|
244 User::LeaveIfError(stmnt.Exec()); |
|
245 CleanupStack::PopAndDestroy(&stmnt); |
|
246 } |
|
247 |
|
248 CleanupStack::PopAndDestroy(lastNameAsNbr); |
|
249 CleanupStack::PopAndDestroy(lastName); |
|
250 CleanupStack::PopAndDestroy(firstNameAsNbr); |
|
251 CleanupStack::PopAndDestroy(firstName); |
|
252 |
|
253 PRINT(_L("CPplPredictiveSearchTableBase::WriteToDbL ends")); |
|
254 } |
|
255 |
|
256 |
|
257 void CPplPredictiveSearchTableBase::GetFieldsLC(const CContactItem& aItem, |
|
258 HBufC** aFirstNameAsNbr, |
|
259 HBufC** aLastNameAsNbr, |
|
260 HBufC** aFirstName, |
|
261 HBufC** aLastName) const |
|
262 { |
|
263 PRINT(_L("CPplPredictiveSearchTableBase::GetFieldsLC")); |
|
264 __ASSERT_ALWAYS(aFirstNameAsNbr != NULL && *aFirstNameAsNbr == NULL, |
|
265 User::Leave(KErrArgument)); |
|
266 __ASSERT_ALWAYS(aLastNameAsNbr != NULL && *aLastNameAsNbr == NULL, |
|
267 User::Leave(KErrArgument)); |
|
268 __ASSERT_ALWAYS(aFirstName != NULL && *aFirstName == NULL, |
|
269 User::Leave(KErrArgument)); |
|
270 __ASSERT_ALWAYS(aLastName != NULL && *aLastName == NULL, |
|
271 User::Leave(KErrArgument)); |
|
272 |
|
273 CContactItemFieldSet& fieldset = aItem.CardFields(); |
|
274 TInt pos = fieldset.Find(KUidContactFieldGivenName); |
|
275 if (pos != KErrNotFound) |
|
276 { |
|
277 CContactTextField* textfield = fieldset[pos].TextStorage(); |
|
278 if (textfield) |
|
279 { |
|
280 TPtrC firstName = textfield->Text(); |
|
281 *aFirstName = firstName.Left(KCharactersFromName).AllocLC(); |
|
282 *aFirstNameAsNbr = iKeyMap->GetMappedStringL(firstName); |
|
283 } |
|
284 } |
|
285 // If aFirstName was not pushed to cleanupstack above, do it now |
|
286 if (*aFirstName == NULL) |
|
287 { |
|
288 CleanupStack::PushL(*aFirstName); |
|
289 } |
|
290 CleanupStack::PushL(*aFirstNameAsNbr); |
|
291 |
|
292 pos = fieldset.Find(KUidContactFieldFamilyName); |
|
293 if (pos != KErrNotFound) |
|
294 { |
|
295 CContactTextField* textfield = fieldset[pos].TextStorage(); |
|
296 if (textfield) |
|
297 { |
|
298 TPtrC lastName = textfield->Text(); |
|
299 *aLastName = lastName.Left(KCharactersFromName).AllocLC(); |
|
300 *aLastNameAsNbr = iKeyMap->GetMappedStringL(lastName); |
|
301 } |
|
302 } |
|
303 // If aLastName was not pushed to cleanupstack above, do it now |
|
304 if (*aLastName == NULL) |
|
305 { |
|
306 CleanupStack::PushL(*aLastName); |
|
307 } |
|
308 CleanupStack::PushL(*aLastNameAsNbr); |
|
309 |
|
310 PRINT5(_L("CPplPredictiveSearchTableBase::GetFieldsLC id=%d FNnbr='%S' LNnbr='%S' FN='%S' LN='%S'"), |
|
311 aItem.Id(), |
|
312 *aFirstNameAsNbr ? *aFirstNameAsNbr : &KNullDesC, |
|
313 *aLastNameAsNbr ? *aLastNameAsNbr : &KNullDesC, |
|
314 *aFirstName ? *aFirstName : &KNullDesC, |
|
315 *aLastName ? *aLastName: &KNullDesC); |
|
316 } |
|
317 |
|
318 |
|
319 // 1. get first token of LN |
|
320 // 2. get first token of FN |
|
321 // 3. get second token of LN |
|
322 // 4. get second token of FN |
|
323 // : |
|
324 // : |
|
325 // If LN or FN runs out of tokens before maximum amount of tokens have been found, |
|
326 // keep getting tokens from the other field. |
|
327 QStringList |
|
328 CPplPredictiveSearchTableBase::GetTokens(QStringList aNonTokenizedFields, |
|
329 HBufC* aFirstName, |
|
330 HBufC* aLastName) const |
|
331 { |
|
332 PRINT2(_L("CPplPredictiveSearchTableBase::GetTokens FN='%S',LN='%S'"), |
|
333 aFirstName ? aFirstName : &KNullDesC, |
|
334 aLastName ? aLastName : &KNullDesC); |
|
335 |
|
336 QStringList tokens; |
|
337 while (tokens.count() < iMaxTokens && !aNonTokenizedFields.isEmpty()) |
|
338 { |
|
339 GetNextToken(aNonTokenizedFields, tokens); |
|
340 } |
|
341 |
|
342 QStringList firstNameTokens; |
|
343 QStringList lastNameTokens; |
|
344 AddTokens(aFirstName, firstNameTokens); |
|
345 AddTokens(aLastName, lastNameTokens); |
|
346 |
|
347 while (tokens.count() < iMaxTokens && |
|
348 (!firstNameTokens.isEmpty() || !lastNameTokens.isEmpty())) |
|
349 { |
|
350 GetNextToken(lastNameTokens, tokens); |
|
351 GetNextToken(firstNameTokens, tokens); |
|
352 } |
|
353 PRINT1(_L("CPplPredictiveSearchTableBase::GetTokens found %d tokens"), |
|
354 tokens.count()); |
|
355 return tokens; |
|
356 } |
|
357 |
|
358 |
|
359 // Ignore tokens beginning with invalid (unknown) character. |
|
360 // Keep duplicate tokens to support e.g. search "202" when both FN and LN are "23". |
|
361 void |
|
362 CPplPredictiveSearchTableBase::AddTokens(HBufC* aString, QStringList& aTokens) const |
|
363 { |
|
364 if (aString) |
|
365 { |
|
366 QString s((QChar*)aString->Ptr(), aString->Length()); |
|
367 #if defined(USE_ORBIT_KEYMAP) |
|
368 QStringList tokens = s.split(iKeyMap->Separator(), QString::SkipEmptyParts); |
|
369 #else |
|
370 QStringList tokens = s.split(' ', QString::SkipEmptyParts); |
|
371 #endif |
|
372 |
|
373 // Select tokens in the same order they are in original aString |
|
374 for (TInt i = 0; i < tokens.count(); ++i) |
|
375 { |
|
376 if (IsValidChar(tokens[i][0])) |
|
377 { |
|
378 aTokens.append(tokens[i]); |
|
379 } |
|
380 } |
|
381 } |
|
382 } |
|
383 |
|
384 |
|
385 void CPplPredictiveSearchTableBase::GetNextToken(QStringList& aSource, |
|
386 QStringList& aDestination) const |
|
387 { |
|
388 if (!aSource.isEmpty() && aDestination.count() < iMaxTokens) |
|
389 { |
|
390 QString padded = aSource[0].left(iMaxTokenLength); |
|
391 aDestination.append(padded); |
|
392 aSource.removeFirst(); |
|
393 } |
|
394 } |
|
395 |
|
396 |
|
397 void |
|
398 CPplPredictiveSearchTableBase::DeleteFromAllTablesL(TContactItemId aContactId, |
|
399 TBool& aLowDiskErrorOccurred) const |
|
400 { |
|
401 QList<QChar> tables; |
|
402 QT_TRYCATCH_LEAVING(tables = FillAllTables()); |
|
403 |
|
404 HBufC* tableName(NULL); |
|
405 while ((tableName = GetNextTableNameL(tables)) != NULL) |
|
406 { |
|
407 iDeleteStmnt->SetTableName(tableName); // Clears also earlier SQL statement |
|
408 |
|
409 RSqlStatement stmnt; |
|
410 CleanupClosePushL(stmnt); |
|
411 |
|
412 PRINT1(_L("CPplPredictiveSearchTableBase::DeleteFromAllTablesL SQL='%S'"), |
|
413 &iDeleteStmnt->SqlStringL()); |
|
414 stmnt.PrepareL(iDatabase, iDeleteStmnt->SqlStringL()); |
|
415 |
|
416 // Contact id was not added with iDeleteStmnt->SetParamL() so it can not be |
|
417 // accessed with iDeleteStmnt->ParameterIndex(). |
|
418 // It is the first and only parameter in query |
|
419 const TInt KContactIdParamIndex(KFirstIndex); |
|
420 User::LeaveIfError(stmnt.BindInt(KContactIdParamIndex, aContactId)); |
|
421 |
|
422 // Returns the amount of affected rows. As contact is not present each |
|
423 // table, some operations return 0, it is not an error. |
|
424 TInt status = stmnt.Exec(); |
|
425 #if defined(WRITE_PRED_SEARCH_LOGS) |
|
426 if (status != 0) |
|
427 { |
|
428 PRINT1(_L(" rows deleted=%d"), status); |
|
429 } |
|
430 #endif |
|
431 CleanupStack::PopAndDestroy(&stmnt); |
|
432 |
|
433 if (status == KErrDiskFull) |
|
434 { |
|
435 PRINT(_L("CPplPredictiveSearchTableBase::DeleteFromAllTablesL disk full")); |
|
436 aLowDiskErrorOccurred = ETrue; |
|
437 } |
|
438 else |
|
439 { |
|
440 User::LeaveIfError(status); |
|
441 } |
|
442 } |
|
443 } |