--- /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 <QStringList>
+#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<QChar>& 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<QChar> CPplPredictiveSearchTableBase::DetermineTables(QStringList aTokens) const
+ {
+ QList<QChar> 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<QChar> 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<QChar> 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);
+ }
+ }
+ }