plugins/contacts/symbian/contactsmodel/cntplsql/src/cpplpredictivesearchtable.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
--- /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);
+			}
+		}
+	}