diff -r 000000000000 -r 876b1a06bc25 plugins/contacts/symbian/contactsmodel/cntplsql/src/cpplpredictivesearchtable.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/contacts/symbian/contactsmodel/cntplsql/src/cpplpredictivesearchtable.cpp Wed Aug 25 15:49:42 2010 +0300 @@ -0,0 +1,443 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + +#include "pltables.h" +#include "dbsqlconstants.h" +#include "cpcskeymap.h" +#include "cntitem.h" +#include +#include "predictivesearchlog.h" + + +// How many characters from the beginning of the first name and last name are +// stored. This only affects how precisely the results are put in alphabetic order. +const TInt KCharactersFromName = 16; + + +/** +Destructor +*/ +CPplPredictiveSearchTableBase::~CPplPredictiveSearchTableBase() + { + PRINT(_L("CPplPredictiveSearchTableBase dtor")); + delete iInsertStmnt; + delete iDeleteStmnt; + delete iKeyMap; + PRINT(_L("CPplPredictiveSearchTableBase dtor ends")); + } + + +/** +@param aItem A contact item whose data are added to the table. +*/ +void CPplPredictiveSearchTableBase::CreateInDbL(CContactItem& aItem) + { + PRINT(_L("CPplPredictiveSearchTableBase::CreateInDbL")); + WriteToDbL(aItem); + PRINT(_L("CPplPredictiveSearchTableBase::CreateInDbL ends")); + } + + +/** +Update is done in two steps: delete contact from all predictive search tables, +then insert it into relevant tables. + +@param aItem A contact item whose data is updated in the database. +*/ +void CPplPredictiveSearchTableBase::UpdateL(const CContactItem& aItem) + { + PRINT(_L("CPplPredictiveSearchTableBase::UpdateL")); + + TBool lowDiskErrorOccurred(EFalse); + DeleteFromAllTablesL(aItem.Id(), lowDiskErrorOccurred); + if (lowDiskErrorOccurred) + { + User::Leave(KErrGeneral); + } + WriteToDbL(aItem); + + PRINT(_L("CPplPredictiveSearchTableBase::UpdateL ends")); + } + + +/** +Deletes a contact item from predictive search tables. + +@param aItem The contact item to be deleted. It contains contact id, but not + first name or last name fields. +*/ +void CPplPredictiveSearchTableBase::DeleteL(const CContactItem& aItem, + TBool& aLowDiskErrorOccurred) + { + PRINT(_L("CPplPredictiveSearchTableBase::DeleteL")); + + DeleteFromAllTablesL(aItem.Id(), aLowDiskErrorOccurred); + + PRINT(_L("CPplPredictiveSearchTableBase::DeleteL ends")); + } + + +/** +Default implementation returns empty list. +*/ +QStringList CPplPredictiveSearchTableBase::GetTableSpecificFields( + const CContactItem& /*aItem*/, + TBool& aMandatoryFieldsPresent) const + { + aMandatoryFieldsPresent = ETrue; + QStringList emptyList; + return emptyList; + } + + +HBufC* CPplPredictiveSearchTableBase::GetNextTableNameL(QList& aTables) const + { + HBufC* tableName(NULL); + if (aTables.count() > 0) + { + tableName = TableNameL(aTables[0]); + aTables.removeFirst(); +// PRINT1(_L("CPplPredictiveSearchTableBase::GetNextTableNameL '%S'"), tableName); + } + return tableName; + } + + +/** +Set up the CCntSqlStatement objects held by the class. +*/ +void CPplPredictiveSearchTableBase::ConstructL() + { + PRINT(_L("CPplPredictiveSearchTableBase::ConstructL")); + + // Using dummy table names here + TCntSqlStatementType insertType(EInsert, KSqlContactPredSearchTable0); + TCntSqlStatementType deleteType(EDelete, KSqlContactPredSearchTable0); + iInsertStmnt = TSqlProvider::GetSqlStatementL(insertType); + // Details of INSERT are done in subclass + + + const TInt KWhereContactIdBufSize( + KWhereStringEqualsStringFormatText().Size() + + KPredSearchContactId().Size() + + KPredSearchContactIdParam().Size() ); + HBufC* whereContactIdClause = HBufC::NewLC(KWhereContactIdBufSize); + // for WHERE contact_id = [contact id value] + whereContactIdClause->Des().AppendFormat(KWhereStringEqualsStringFormatText, + &KPredSearchContactId, &KPredSearchContactIdParam); + + // Delete information of a particular contact item + // DELETE FROM predictivesearchX (X=0..11) + // WHERE contact_id = [contact id value]; + iDeleteStmnt = TSqlProvider::GetSqlStatementL(deleteType); + iDeleteStmnt->SetConditionL(*whereContactIdClause); + CleanupStack::PopAndDestroy(whereContactIdClause); + + PRINT(_L("CPplPredictiveSearchTableBase::ConstructL ends")); + } + + +/** +Constructor +*/ +CPplPredictiveSearchTableBase::CPplPredictiveSearchTableBase( + RSqlDatabase& aDatabase, TInt aMaxTokens, TInt aMaxTokenLength) : + iDatabase(aDatabase), + iMaxTokens(aMaxTokens), + iMaxTokenLength(aMaxTokenLength) + { + } + + +QList CPplPredictiveSearchTableBase::DetermineTables(QStringList aTokens) const + { + QList tables; + for (TInt i = aTokens.count() - 1; i >= 0; --i) + { + QChar ch = aTokens[i][0]; + __ASSERT_ALWAYS(IsValidChar(ch), + User::Panic(_L("DetermineTables"), KErrArgument)); + if (!tables.contains(ch)) + { + tables.append(ch); + } + } + return tables; + } + + +// Insert a contact to predictive search tables. +// Write contact's all tokens to each associate pred.search table. +// E.g. if FN="11 22" LN="2 333", write "11","22","2" and "333" to tables 1, 2 and 3. +void CPplPredictiveSearchTableBase::WriteToDbL(const CContactItem& aItem) + { + PRINT(_L("CPplPredictiveSearchTableBase::WriteToDbL")); + + HBufC* firstNameAsNbr(NULL); // owned + HBufC* lastNameAsNbr(NULL); // owned + HBufC* firstName(NULL); // owned + HBufC* lastName(NULL); // owned + GetFieldsLC(aItem, &firstNameAsNbr, &lastNameAsNbr, &firstName, &lastName); + + QStringList tokens; + QList tables; + QT_TRYCATCH_LEAVING({ + TBool mandatoryFieldsPresent(EFalse); + QStringList tableSpecificFields = + GetTableSpecificFields(aItem, mandatoryFieldsPresent); + if (mandatoryFieldsPresent) + { + tokens = GetTokens(tableSpecificFields, firstNameAsNbr, lastNameAsNbr); + tables = DetermineTables(tokens); + } + }); + + HBufC* tableName(NULL); + while ((tableName = GetNextTableNameL(tables)) != NULL) + { + // Takes ownership. Clears also earlier SQL statement. + iInsertStmnt->SetTableName(tableName); + RSqlStatement stmnt; + CleanupClosePushL( stmnt ); + PRINT1(_L("CPplPredictiveSearchTableBase::WriteToDbL SQL='%S'"), + &iInsertStmnt->SqlStringL()); + stmnt.PrepareL(iDatabase, iInsertStmnt->SqlStringL()); + +// TODO: while this works, it is inefficient, since the BIGINT values are +// computed in every iteration of the while-loop, even though the data is +// always the same. + FillKeyboardSpecificFieldsL(stmnt, tokens); + + User::LeaveIfError(stmnt.BindInt( + User::LeaveIfError(stmnt.ParameterIndex(KPredSearchContactIdParam)), + aItem.Id())); + + if (firstName) + { + User::LeaveIfError(stmnt.BindText( + User::LeaveIfError(stmnt.ParameterIndex(KPredSearchFirstNameParam)), + *firstName)); + } + if (lastName) + { + User::LeaveIfError(stmnt.BindText( + User::LeaveIfError(stmnt.ParameterIndex(KPredSearchLastNameParam)), + *lastName)); + } + +// PRINT(_L("CPplPredictiveSearchTableBase::WriteToDbL execute SQL statement")); + // Execute the SQL statement + User::LeaveIfError(stmnt.Exec()); + CleanupStack::PopAndDestroy(&stmnt); + } + + CleanupStack::PopAndDestroy(lastNameAsNbr); + CleanupStack::PopAndDestroy(lastName); + CleanupStack::PopAndDestroy(firstNameAsNbr); + CleanupStack::PopAndDestroy(firstName); + + PRINT(_L("CPplPredictiveSearchTableBase::WriteToDbL ends")); + } + + +void CPplPredictiveSearchTableBase::GetFieldsLC(const CContactItem& aItem, + HBufC** aFirstNameAsNbr, + HBufC** aLastNameAsNbr, + HBufC** aFirstName, + HBufC** aLastName) const + { + PRINT(_L("CPplPredictiveSearchTableBase::GetFieldsLC")); + __ASSERT_ALWAYS(aFirstNameAsNbr != NULL && *aFirstNameAsNbr == NULL, + User::Leave(KErrArgument)); + __ASSERT_ALWAYS(aLastNameAsNbr != NULL && *aLastNameAsNbr == NULL, + User::Leave(KErrArgument)); + __ASSERT_ALWAYS(aFirstName != NULL && *aFirstName == NULL, + User::Leave(KErrArgument)); + __ASSERT_ALWAYS(aLastName != NULL && *aLastName == NULL, + User::Leave(KErrArgument)); + + CContactItemFieldSet& fieldset = aItem.CardFields(); + TInt pos = fieldset.Find(KUidContactFieldGivenName); + if (pos != KErrNotFound) + { + CContactTextField* textfield = fieldset[pos].TextStorage(); + if (textfield) + { + TPtrC firstName = textfield->Text(); + *aFirstName = firstName.Left(KCharactersFromName).AllocLC(); + *aFirstNameAsNbr = iKeyMap->GetMappedStringL(firstName); + } + } + // If aFirstName was not pushed to cleanupstack above, do it now + if (*aFirstName == NULL) + { + CleanupStack::PushL(*aFirstName); + } + CleanupStack::PushL(*aFirstNameAsNbr); + + pos = fieldset.Find(KUidContactFieldFamilyName); + if (pos != KErrNotFound) + { + CContactTextField* textfield = fieldset[pos].TextStorage(); + if (textfield) + { + TPtrC lastName = textfield->Text(); + *aLastName = lastName.Left(KCharactersFromName).AllocLC(); + *aLastNameAsNbr = iKeyMap->GetMappedStringL(lastName); + } + } + // If aLastName was not pushed to cleanupstack above, do it now + if (*aLastName == NULL) + { + CleanupStack::PushL(*aLastName); + } + CleanupStack::PushL(*aLastNameAsNbr); + + PRINT5(_L("CPplPredictiveSearchTableBase::GetFieldsLC id=%d FNnbr='%S' LNnbr='%S' FN='%S' LN='%S'"), + aItem.Id(), + *aFirstNameAsNbr ? *aFirstNameAsNbr : &KNullDesC, + *aLastNameAsNbr ? *aLastNameAsNbr : &KNullDesC, + *aFirstName ? *aFirstName : &KNullDesC, + *aLastName ? *aLastName: &KNullDesC); + } + + +// 1. get first token of LN +// 2. get first token of FN +// 3. get second token of LN +// 4. get second token of FN +// : +// : +// If LN or FN runs out of tokens before maximum amount of tokens have been found, +// keep getting tokens from the other field. +QStringList +CPplPredictiveSearchTableBase::GetTokens(QStringList aNonTokenizedFields, + HBufC* aFirstName, + HBufC* aLastName) const + { + PRINT2(_L("CPplPredictiveSearchTableBase::GetTokens FN='%S',LN='%S'"), + aFirstName ? aFirstName : &KNullDesC, + aLastName ? aLastName : &KNullDesC); + + QStringList tokens; + while (tokens.count() < iMaxTokens && !aNonTokenizedFields.isEmpty()) + { + GetNextToken(aNonTokenizedFields, tokens); + } + + QStringList firstNameTokens; + QStringList lastNameTokens; + AddTokens(aFirstName, firstNameTokens); + AddTokens(aLastName, lastNameTokens); + + while (tokens.count() < iMaxTokens && + (!firstNameTokens.isEmpty() || !lastNameTokens.isEmpty())) + { + GetNextToken(lastNameTokens, tokens); + GetNextToken(firstNameTokens, tokens); + } + PRINT1(_L("CPplPredictiveSearchTableBase::GetTokens found %d tokens"), + tokens.count()); + return tokens; + } + + +// Ignore tokens beginning with invalid (unknown) character. +// Keep duplicate tokens to support e.g. search "202" when both FN and LN are "23". +void +CPplPredictiveSearchTableBase::AddTokens(HBufC* aString, QStringList& aTokens) const + { + if (aString) + { + QString s((QChar*)aString->Ptr(), aString->Length()); +#if defined(USE_ORBIT_KEYMAP) + QStringList tokens = s.split(iKeyMap->Separator(), QString::SkipEmptyParts); +#else + QStringList tokens = s.split(' ', QString::SkipEmptyParts); +#endif + + // Select tokens in the same order they are in original aString + for (TInt i = 0; i < tokens.count(); ++i) + { + if (IsValidChar(tokens[i][0])) + { + aTokens.append(tokens[i]); + } + } + } + } + + +void CPplPredictiveSearchTableBase::GetNextToken(QStringList& aSource, + QStringList& aDestination) const + { + if (!aSource.isEmpty() && aDestination.count() < iMaxTokens) + { + QString padded = aSource[0].left(iMaxTokenLength); + aDestination.append(padded); + aSource.removeFirst(); + } + } + + +void +CPplPredictiveSearchTableBase::DeleteFromAllTablesL(TContactItemId aContactId, + TBool& aLowDiskErrorOccurred) const + { + QList tables; + QT_TRYCATCH_LEAVING(tables = FillAllTables()); + + HBufC* tableName(NULL); + while ((tableName = GetNextTableNameL(tables)) != NULL) + { + iDeleteStmnt->SetTableName(tableName); // Clears also earlier SQL statement + + RSqlStatement stmnt; + CleanupClosePushL(stmnt); + + PRINT1(_L("CPplPredictiveSearchTableBase::DeleteFromAllTablesL SQL='%S'"), + &iDeleteStmnt->SqlStringL()); + stmnt.PrepareL(iDatabase, iDeleteStmnt->SqlStringL()); + + // Contact id was not added with iDeleteStmnt->SetParamL() so it can not be + // accessed with iDeleteStmnt->ParameterIndex(). + // It is the first and only parameter in query + const TInt KContactIdParamIndex(KFirstIndex); + User::LeaveIfError(stmnt.BindInt(KContactIdParamIndex, aContactId)); + + // Returns the amount of affected rows. As contact is not present each + // table, some operations return 0, it is not an error. + TInt status = stmnt.Exec(); +#if defined(WRITE_PRED_SEARCH_LOGS) + if (status != 0) + { + PRINT1(_L(" rows deleted=%d"), status); + } +#endif + CleanupStack::PopAndDestroy(&stmnt); + + if (status == KErrDiskFull) + { + PRINT(_L("CPplPredictiveSearchTableBase::DeleteFromAllTablesL disk full")); + aLowDiskErrorOccurred = ETrue; + } + else + { + User::LeaveIfError(status); + } + } + }