plugins/contacts/symbian/contactsmodel/cntplsql/src/cpplpredictivesearchtable.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     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 	}