phonebookengines/contactsmodel/cntplsql/src/cpplcommaddrtable.cpp
branchRCL_3
changeset 63 f4a778e096c2
child 64 c1e8ba0c2b16
equal deleted inserted replaced
62:5b6f26637ad3 63:f4a778e096c2
       
     1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "pltables.h"
       
    17 #include "dbsqlconstants.h"
       
    18 #include "plplugins.h"
       
    19 #include <cntdb.h>
       
    20 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
       
    21 #include <cntphonenumparser.h>
       
    22 #endif
       
    23 
       
    24 /// Unnamed namespace for local definitions
       
    25 namespace {
       
    26 
       
    27 TBool ComparePtr( const TPtrC* aLeft, const TPtrC& aRight )
       
    28     {
       
    29     return ( aLeft->Compare( aRight )== 0 );
       
    30     }
       
    31 
       
    32 } /// namespace
       
    33 
       
    34 /**
       
    35 @param aDatabase A handle to the database.
       
    36 @param aProperties A contact properties object.
       
    37 
       
    38 @return A pointer to a new CPplCommAddrTable object.
       
    39 */
       
    40 CPplCommAddrTable* CPplCommAddrTable::NewL(RSqlDatabase& aDatabase, CLplContactProperties& aProperties)
       
    41 	{
       
    42 	CPplCommAddrTable* self = CPplCommAddrTable::NewLC(aDatabase, aProperties);
       
    43 	CleanupStack::Pop(self);
       
    44 	return self;
       
    45 	}
       
    46 
       
    47 
       
    48 /**
       
    49 @param aDatabase A handle to the database.
       
    50 @param aProperties A contact properties object.
       
    51 
       
    52 @return A pointer to a new CPplCommAddrTable object.
       
    53 */
       
    54 CPplCommAddrTable* CPplCommAddrTable::NewLC(RSqlDatabase& aDatabase, CLplContactProperties& aProperties)
       
    55 	{
       
    56 	CPplCommAddrTable* self = new (ELeave) CPplCommAddrTable(aDatabase, aProperties);
       
    57 	CleanupStack::PushL(self);
       
    58 	self->ConstructL();
       
    59 	return self;
       
    60 	}
       
    61 
       
    62 /**
       
    63 Set up the CCntSqlStatement objects held by the class.
       
    64 */
       
    65 void CPplCommAddrTable::ConstructL()
       
    66 	{
       
    67 	// Statement types
       
    68 	TCntSqlStatementType insertType(EInsert, KSqlContactCommAddrTableName() );
       
    69 	TCntSqlStatementType selectType(ESelect, KSqlContactCommAddrTableName() );
       
    70 	TCntSqlStatementType updateType(EUpdate, KSqlContactCommAddrTableName() );
       
    71 	TCntSqlStatementType deleteType(EDelete, KSqlContactCommAddrTableName() );
       
    72 
       
    73 	// Where clauses
       
    74 
       
    75 	// sizes of the clauses
       
    76 	const TInt KWhereContactIdBufSize(KCommAddrContactId().Size() +
       
    77 		KWhereStringEqualsStringFormatText().Size() + KCommAddrContactIdParam().Size() );
       
    78 	const TInt KWhereCommAddrIdBufSize(KCommAddrId().Size() +
       
    79 		KWhereStringEqualsStringFormatText().Size() + KCommAddrIdParam().Size() );
       
    80 	const TInt KWhereAndClauseBufSize(KWhereCommAddrIdBufSize + KSqlAnd().Size() + KWhereCommAddrIdBufSize);
       
    81 
       
    82 	HBufC* whereContactIdClause = HBufC::NewLC(KWhereContactIdBufSize);
       
    83 	// for WHERE contact_id = [contact id value]
       
    84 	whereContactIdClause->Des().AppendFormat(KWhereStringEqualsStringFormatText,
       
    85 		&KCommAddrContactId, &KCommAddrContactIdParam );
       
    86 
       
    87 	// for WHERE comm_addr_id = [comm addr id value]
       
    88 	HBufC* whereCommAddrIdClause = HBufC::NewLC(KWhereCommAddrIdBufSize);
       
    89 	whereCommAddrIdClause->Des().AppendFormat(KWhereStringEqualsStringFormatText,
       
    90 		&KCommAddrId, &KCommAddrIdParam );
       
    91 
       
    92 	// for WHERE value = [value string] AND type = [type value]
       
    93 	HBufC* whereValueAndTypeClause = HBufC::NewLC(KWhereAndClauseBufSize);
       
    94 	TPtr whereValueAndTypeClausePtr = whereValueAndTypeClause->Des();
       
    95 	whereValueAndTypeClausePtr.AppendFormat(KWhereStringEqualsStringFormatText,
       
    96 		&KCommAddrValue, &KCommAddrValueParam );
       
    97 	whereValueAndTypeClausePtr.Append(KSqlAnd);
       
    98 	whereValueAndTypeClausePtr.AppendFormat(KWhereStringEqualsStringFormatText,
       
    99 		&KCommAddrType, &KCommAddrTypeParam );
       
   100 
       
   101 	// INSERT
       
   102 
       
   103 	// insert new comm addr record
       
   104 	// 	INSERT INTO comm_addr
       
   105 	//		(comm_addr_id, contact_id, type, value, extra_value)
       
   106 	//		VALUES (NULL, [contact id value], [type value],
       
   107 	//			[value string], [extra value string]);
       
   108 	//
       
   109 	iInsertStmnt = TSqlProvider::GetSqlStatementL(insertType);
       
   110 	iInsertStmnt->SetParamL(KCommAddrId(), KCommAddrIdParam() );
       
   111 	iInsertStmnt->SetParamL(KCommAddrContactId(), KCommAddrContactIdParam() );
       
   112 	iInsertStmnt->SetParamL(KCommAddrValue(), KCommAddrValueParam() );
       
   113 	iInsertStmnt->SetParamL(KCommAddrExtraValue(), KCommAddrExtraValueParam() );
       
   114 	iInsertStmnt->SetParamL(KCommAddrType(), KCommAddrTypeParam() );
       
   115 
       
   116 	// SELECT
       
   117 
       
   118 	// select all fields for all comm addrs for a particular item id
       
   119 	// For a statement in the following format:
       
   120 	// 	SELECT comm_addr_id, type, value, extra_value FROM comm_addr
       
   121 	//		WHERE contact_id = [contact id value];
       
   122 	//
       
   123 	iWholeSelectStmnt = TSqlProvider::GetSqlStatementL(selectType);
       
   124 	iWholeSelectStmnt->SetParamL(KCommAddrId(), KNullDesC() );
       
   125 	iWholeSelectStmnt->SetParamL(KCommAddrType(), KNullDesC() );
       
   126 	iWholeSelectStmnt->SetParamL(KCommAddrValue(), KNullDesC() );
       
   127 	iWholeSelectStmnt->SetParamL(KCommAddrExtraValue(), KNullDesC() );
       
   128 	iWholeSelectStmnt->SetConditionL(*whereContactIdClause);
       
   129 
       
   130 	// select fields for contacts that match phone, email or SIP lookup
       
   131 	// For a statement in the following format:
       
   132 	// 	SELECT contact_id, extra_value FROM comm_addr
       
   133 	//		WHERE value = [value string] AND type = [type value];
       
   134 	//
       
   135 	iMatchSelectStmnt = TSqlProvider::GetSqlStatementL(selectType);
       
   136 	iMatchSelectStmnt->SetParamL(KCommAddrContactId(), KNullDesC() );
       
   137 	iMatchSelectStmnt->SetParamL(KCommAddrExtraValue(), KNullDesC() );
       
   138 	iMatchSelectStmnt->SetConditionL(*whereValueAndTypeClause);
       
   139 
       
   140 	// UPDATE
       
   141 
       
   142 	// update comm addr record
       
   143 	// For a statement in the following format:
       
   144 	// 	UPDATE comm_addr SET
       
   145 	//		contact_id = [contact id value],
       
   146 	//		type = [type value],
       
   147 	//		value = [value string],
       
   148 	//		extra_value = [extra value string]
       
   149 	//		WHERE comm_addr_id = [comm addr id value];
       
   150 	//
       
   151 	iUpdateStmnt = TSqlProvider::GetSqlStatementL(updateType);
       
   152 	iUpdateStmnt->SetParamL(KCommAddrContactId(), KCommAddrContactIdParam() );
       
   153 	iUpdateStmnt->SetParamL(KCommAddrValue(), KCommAddrValueParam() );
       
   154 	iUpdateStmnt->SetParamL(KCommAddrExtraValue(), KCommAddrExtraValueParam() );
       
   155 	iUpdateStmnt->SetParamL(KCommAddrType(), KCommAddrTypeParam() );
       
   156 	iUpdateStmnt->SetConditionL(*whereCommAddrIdClause);
       
   157 
       
   158 	// DELETE
       
   159 
       
   160 	// delete single comm addr record
       
   161 	// For a statement in the following format:
       
   162 	// 	DELETE FROM comm_addr WHERE comm_addr_id = [comm addr id value];
       
   163 	//
       
   164 	iSingleDeleteStmnt = TSqlProvider::GetSqlStatementL(deleteType);
       
   165 	iSingleDeleteStmnt->SetConditionL(*whereCommAddrIdClause);
       
   166 
       
   167 	// delete all comm addrs for a particular contact item
       
   168 	// For a statement in the following format:
       
   169 	// 	DELETE FROM comm_addr WHERE contact_id = [contact id value];
       
   170 	//
       
   171 	iAllForItemDeleteStmnt = TSqlProvider::GetSqlStatementL(deleteType);
       
   172 	iAllForItemDeleteStmnt->SetConditionL(*whereContactIdClause);
       
   173 
       
   174 	CleanupStack::PopAndDestroy(3, whereContactIdClause); // and whereCommAddrIdClause, whereValueAndTypeClause
       
   175 	}
       
   176 
       
   177 
       
   178 /**
       
   179 Insert new communication addresses into the comm_addr table.
       
   180 @param aItem A contact item whose communication addresses are to be added to the contacts database.
       
   181 */
       
   182 void CPplCommAddrTable::CreateInDbL(CContactItem& aItem)
       
   183 	{
       
   184 	// Check that the contact item is a card, own card or ICC entry.
       
   185 	const TUid KType = aItem.Type();
       
   186 	if (KType != KUidContactCard && KType != KUidContactOwnCard && KType != KUidContactICCEntry)
       
   187 		{
       
   188 		return;
       
   189 		}
       
   190 
       
   191 	// create lists for comm_addrs to keep track of what we have already seen so as to avoid duplicates
       
   192 	RArray<TMatch> newPhones;
       
   193 	RArray<TPtrC>  newEmails;
       
   194 	RArray<TPtrC>  newSips;
       
   195 	CleanupClosePushL(newPhones);
       
   196 	CleanupClosePushL(newEmails);
       
   197 	CleanupClosePushL(newSips);
       
   198 
       
   199 	for (TInt fieldNum = aItem.CardFields().Count() - 1; fieldNum >= 0; --fieldNum)
       
   200 		{
       
   201 		CContactItemField& currField = aItem.CardFields()[fieldNum];
       
   202 		const CContentType& contType = currField.ContentType();
       
   203 		TBool isPhone(contType.ContainsFieldType(KUidContactFieldPhoneNumber) ||
       
   204 					  contType.ContainsFieldType(KUidContactFieldFax)		  ||
       
   205 					  contType.ContainsFieldType(KUidContactFieldSms)		  );
       
   206 		TBool isEmail(contType.ContainsFieldType(KUidContactFieldEMail) );
       
   207 		TBool isSip(contType.ContainsFieldType(KUidContactFieldSIPID) );
       
   208 
       
   209 		// check it's a field we want and that it's not empty
       
   210 		// insert a new address only if we haven't already seen it -- no point storing the same one twice.
       
   211 		if ((isPhone || isEmail || isSip) && currField.StorageType() == KStorageTypeText
       
   212 				&& currField.TextStorage()->IsFull() )
       
   213 			{
       
   214 			const TContactItemId KItemId(aItem.Id());
       
   215 
       
   216 			// get phone numbers
       
   217 			if (isPhone)
       
   218 				{
       
   219 				TMatch phoneNumber;
       
   220 				phoneNumber = CreatePaddedPhoneDigitsL(currField.TextStorage()->Text(), KLowerSevenDigits,
       
   221 													   KMaxPhoneMatchLength - KLowerSevenDigits);
       
   222 				if (newPhones.Find(phoneNumber, TIdentityRelation<TMatch>(&TMatch::Equals) ) == KErrNotFound)
       
   223 					{
       
   224 					DoPhoneNumWriteOpL(phoneNumber, EInsert, KItemId);
       
   225 					newPhones.AppendL(phoneNumber);
       
   226 					}
       
   227 				}
       
   228 			// get email addresses
       
   229 			else if (isEmail)
       
   230 				{
       
   231 				if (newEmails.Find( currField.TextStorage()->Text(), ComparePtr ) == KErrNotFound)
       
   232 					{
       
   233 					DoNonPhoneWriteOpL(currField.TextStorage()->Text(), EInsert, KItemId, EEmailAddress);
       
   234 					newEmails.AppendL(currField.TextStorage()->Text() );
       
   235 					}
       
   236 				}
       
   237 			// get SIP addresses
       
   238 			else if (newSips.Find(currField.TextStorage()->Text() ) == KErrNotFound)
       
   239 				{
       
   240 				DoNonPhoneWriteOpL(currField.TextStorage()->Text(), EInsert, KItemId, ESipAddress);
       
   241 				newSips.AppendL(currField.TextStorage()->Text() );
       
   242 				}
       
   243 			}
       
   244 		}
       
   245 
       
   246 	CleanupStack::PopAndDestroy(3, &newPhones); // and newSips, newEmails
       
   247 	}
       
   248 
       
   249 
       
   250 /**
       
   251 Updates communication addresses in the database.
       
   252 
       
   253 If there are the same number of items to be updated as are already in the database, the
       
   254 existing records are overwritten using the update statement. However, if there is a
       
   255 different number of addresses to be updated, records in the database (if there are any)
       
   256 are deleted (using DeleteL() ) and the new data is inserted (using CreateInDbL() ).
       
   257 
       
   258 @param aItem A contact item whose communication addresses are to be updated in the contacts database.
       
   259 */
       
   260 void CPplCommAddrTable::UpdateL(const CContactItem& aItem)
       
   261 	{
       
   262 	// Check that the contact item is a card, own card or ICC entry.
       
   263 	const TUid type(aItem.Type() );
       
   264 	if (type != KUidContactCard && type != KUidContactOwnCard && type != KUidContactICCEntry)
       
   265 		{
       
   266 		return;
       
   267 		}
       
   268 
       
   269 	const TContactItemId KItemId(aItem.Id() );
       
   270 
       
   271 	// create lists for comm_addrs and go through contact item to populate them with any new ones we find
       
   272 	RArray<TMatch> newPhones;
       
   273 	RArray<TPtrC>  newEmails;
       
   274 	RArray<TPtrC>  newSips;
       
   275 	CleanupClosePushL(newPhones);
       
   276 	CleanupClosePushL(newEmails);
       
   277 	CleanupClosePushL(newSips);
       
   278 
       
   279 	for (TInt fieldNum = aItem.CardFields().Count() - 1; fieldNum >= 0; --fieldNum)
       
   280 		{
       
   281 		CContactItemField& currField = aItem.CardFields()[fieldNum];
       
   282 		const CContentType& contType = currField.ContentType();
       
   283 		TBool isPhone(contType.ContainsFieldType(KUidContactFieldPhoneNumber) ||
       
   284 					  contType.ContainsFieldType(KUidContactFieldFax)		  ||
       
   285 					  contType.ContainsFieldType(KUidContactFieldSms)		  );
       
   286 		TBool isEmail(contType.ContainsFieldType(KUidContactFieldEMail) );
       
   287 		TBool isSip(contType.ContainsFieldType(KUidContactFieldSIPID) );
       
   288 
       
   289 		// check it's a field we want and that it's not empty
       
   290 		// store a new address if we haven't already seen it -- no point storing the same one twice.
       
   291 		if ((isPhone || isEmail || isSip) && currField.StorageType() == KStorageTypeText
       
   292 				&& currField.TextStorage()->IsFull() )
       
   293 			{
       
   294 			// get phone numbers
       
   295 			if (isPhone)
       
   296 				{
       
   297 				TMatch phoneNumber;
       
   298 				phoneNumber = CreatePaddedPhoneDigitsL(currField.TextStorage()->Text(), KLowerSevenDigits,
       
   299 													   KMaxPhoneMatchLength - KLowerSevenDigits);
       
   300 				if (newPhones.Find(phoneNumber, TIdentityRelation<TMatch>(&TMatch::Equals) ) == KErrNotFound)
       
   301 					{
       
   302 					newPhones.AppendL(phoneNumber);
       
   303 					}
       
   304 				}
       
   305 			// get email addresses
       
   306 		   else if (isEmail)
       
   307 			  {
       
   308 			  if (newEmails.Find(currField.TextStorage()->Text(), ComparePtr ) == KErrNotFound)
       
   309 				  {
       
   310 				  newEmails.AppendL(currField.TextStorage()->Text() );
       
   311 				  }
       
   312 			  }
       
   313 			// get SIP addresses
       
   314 			else if (newSips.Find(currField.TextStorage()->Text() ) == KErrNotFound)
       
   315 				{
       
   316 				newSips.AppendL(currField.TextStorage()->Text() );
       
   317 				}
       
   318 			}
       
   319 		}
       
   320 
       
   321 	// if there are no comm addresses in the contact item, delete any from the database
       
   322 	if (!(newPhones.Count() + newEmails.Count() + newSips.Count() ) )
       
   323 		{
       
   324 		TBool lowDiskErr(EFalse);
       
   325 		DeleteL(aItem, lowDiskErr);
       
   326 		CleanupStack::PopAndDestroy(3, &newPhones); // and newSips, newEmails
       
   327 		if (lowDiskErr)
       
   328 			{
       
   329 			User::Leave(KErrDiskFull);
       
   330 			}
       
   331 		return;
       
   332 		}
       
   333 
       
   334 	// create from the database a list of comm_addr_ids that can be recycled as their
       
   335 	// comm_addrs are in the database but not in the new version of the contact item
       
   336 	RArray<TInt> freeCommAddrIds;
       
   337 	CleanupClosePushL(freeCommAddrIds);
       
   338 
       
   339 	// weed out addresses from the list that are already in the db but haven't changed
       
   340 	// and populate the freeCommAddrIds list
       
   341 	RemoveNonUpdatedAddrsL(newPhones, newEmails, newSips, freeCommAddrIds, KItemId);
       
   342 
       
   343 	// do the actual updating on an address-by-address basis
       
   344 	DoUpdateCommAddrsL(newPhones, newEmails, newSips, freeCommAddrIds, KItemId);
       
   345 
       
   346 	CleanupStack::PopAndDestroy(4, &newPhones); // and freeCommAddrIds, newSips, newEmails
       
   347 	}
       
   348 
       
   349 
       
   350 /**
       
   351 Removes comm addresses from the 3 lists that are already in the database and have been updated.
       
   352 It takes the 3 lists in as parameters and modifies them accordingly. It also populates the list
       
   353 of comm address ids that are free to be recycled during updating.
       
   354 */
       
   355 void CPplCommAddrTable::RemoveNonUpdatedAddrsL(RArray<TMatch>& aNewPhones, RArray<TPtrC>& aNewEmails, RArray<TPtrC>& aNewSips,
       
   356 					  RArray<TInt>& aFreeCommAddrIds, const TInt aItemId)
       
   357 	{
       
   358 	// build the RSqlStatement
       
   359 	RSqlStatement stmnt;
       
   360 	CleanupClosePushL(stmnt);
       
   361 	stmnt.PrepareL(iDatabase, iWholeSelectStmnt->SqlStringL() );
       
   362 	const TInt KContactIdParamIndex(KFirstIndex); // first and only parameter in the query
       
   363 	User::LeaveIfError(stmnt.BindInt(KContactIdParamIndex, aItemId) ) ;
       
   364 
       
   365 	// fetch the results from the query and compare them with the new comm_addrs we have
       
   366 	TInt err(KErrNone);
       
   367 	while ((err = stmnt.Next() ) == KSqlAtRow)
       
   368 		{
       
   369 		const TInt KType(stmnt.ColumnInt(iWholeSelectStmnt->ParameterIndex(KCommAddrType() ) ) );
       
   370 		if (KType == EPhoneNumber)
       
   371 			{
       
   372 			TMatch phoneNumber;
       
   373 			TPtrC valString    = stmnt.ColumnTextL(iWholeSelectStmnt->ParameterIndex(KCommAddrValue() ) );
       
   374 			TPtrC extValString = stmnt.ColumnTextL(iWholeSelectStmnt->ParameterIndex(KCommAddrExtraValue() ) );
       
   375 			User::LeaveIfError(TLex(valString).Val(phoneNumber.iLowerSevenDigits) );
       
   376 			User::LeaveIfError(TLex(extValString).Val(phoneNumber.iUpperDigits) );
       
   377 
       
   378 			TInt matchIndex(aNewPhones.Find(phoneNumber, TIdentityRelation<TMatch>(&TMatch::Equals) ) );
       
   379 			// remove any phone numbers from the new list if we already
       
   380 			// have them in the db and they haven't changed...
       
   381 			if (matchIndex != KErrNotFound)
       
   382 				{
       
   383 				aNewPhones.Remove(matchIndex);
       
   384 				}
       
   385 			// ...and add any spare ids to the recycle list
       
   386 			else
       
   387 				{
       
   388 				aFreeCommAddrIds.AppendL(
       
   389 					stmnt.ColumnInt(iWholeSelectStmnt->ParameterIndex(KCommAddrId() ) ) );
       
   390 				}
       
   391 			}
       
   392 		else // is Email or SIP
       
   393 			{
       
   394 			TPtrC valString = stmnt.ColumnTextL(iWholeSelectStmnt->ParameterIndex(KCommAddrValue() ) );
       
   395 			TInt matchIndex(0);
       
   396 
       
   397 			// remove any email and sip addresses from the new list if
       
   398 			// we already have them in the db and they haven't changed...
       
   399 			if (KType == EEmailAddress)
       
   400 				{
       
   401 				matchIndex = aNewEmails.Find( valString, ComparePtr );
       
   402 				if (matchIndex != KErrNotFound)
       
   403 					{
       
   404 					aNewEmails.Remove(matchIndex);
       
   405 					}
       
   406 				}
       
   407 			else // SIP
       
   408 				{
       
   409 				matchIndex = aNewSips.Find(valString);
       
   410 				if (matchIndex != KErrNotFound)
       
   411 					{
       
   412 					aNewSips.Remove(matchIndex);
       
   413 					}
       
   414 				}
       
   415 
       
   416 			// ...and add any spare ids to the recycle list
       
   417 			if (matchIndex == KErrNotFound)
       
   418 				{
       
   419 				aFreeCommAddrIds.AppendL(
       
   420 					stmnt.ColumnInt(iWholeSelectStmnt->ParameterIndex(KCommAddrId() ) ) );
       
   421 				}
       
   422 			}
       
   423 		}
       
   424 	// leave if we didn't complete going through the results properly
       
   425 	if(err != KSqlAtEnd)
       
   426 		{
       
   427 		User::Leave(err);
       
   428 		}
       
   429 	CleanupStack::PopAndDestroy(&stmnt);
       
   430 	}
       
   431 
       
   432 
       
   433 /**
       
   434 Does comm_addr updating on an address-by-address basis.
       
   435 Takes 3 lists of new addresses and a list of free comm_addr_ids.
       
   436 */
       
   437 void CPplCommAddrTable::DoUpdateCommAddrsL(RArray<TMatch>& aNewPhones, RArray<TPtrC>& aNewEmails, RArray<TPtrC>& aNewSips,
       
   438 					  RArray<TInt>& aFreeCommAddrIds, const TInt aItemId)
       
   439 	{
       
   440 	// if we have free ids to recycle and new comm_addrs, insert them by UPDATE
       
   441 	const TInt KFirstElementId(0);
       
   442 	while (aFreeCommAddrIds.Count() && (aNewPhones.Count() || aNewEmails.Count() || aNewSips.Count() ) )
       
   443 		{
       
   444 		if(aNewPhones.Count() )
       
   445 			{
       
   446 			DoPhoneNumWriteOpL(aNewPhones[KFirstElementId], EUpdate, aItemId, aFreeCommAddrIds[KFirstElementId]);
       
   447 			aNewPhones.Remove(KFirstElementId);
       
   448 			aFreeCommAddrIds.Remove(KFirstElementId);
       
   449 			}
       
   450 		else if(aNewEmails.Count() )
       
   451 			{
       
   452 			DoNonPhoneWriteOpL(aNewEmails[KFirstElementId], EUpdate, aItemId, EEmailAddress,
       
   453 								aFreeCommAddrIds[KFirstElementId]);
       
   454 			aNewEmails.Remove(KFirstElementId);
       
   455 			aFreeCommAddrIds.Remove(KFirstElementId);
       
   456 			}
       
   457 		else if(aNewSips.Count() )
       
   458 			{
       
   459 			DoNonPhoneWriteOpL(aNewSips[KFirstElementId], EUpdate, aItemId, ESipAddress,
       
   460 								aFreeCommAddrIds[KFirstElementId]);
       
   461 			aNewSips.Remove(KFirstElementId);
       
   462 			aFreeCommAddrIds.Remove(KFirstElementId);
       
   463 			}
       
   464 		}
       
   465 
       
   466 	// if we still have free ids to recycle but no new comm_addrs,
       
   467 	// delete the existing comm_addrs for these ids
       
   468 	const TInt KNumFreeIds(aFreeCommAddrIds.Count() );
       
   469 	for (TInt i = 0; i < KNumFreeIds; ++i)
       
   470 		{
       
   471 		TBool lowDiskErr(EFalse);
       
   472 		DeleteSingleCommAddrL(aFreeCommAddrIds[i], lowDiskErr);
       
   473 		if (lowDiskErr)
       
   474 			{
       
   475 			User::Leave(KErrDiskFull);
       
   476 			}
       
   477 		}
       
   478 
       
   479 	// if we still have new comm_addrs but no free ids to recycle,
       
   480 	// put them in the database using INSERT
       
   481 	const TInt KNumNewPhones(aNewPhones.Count() );
       
   482 	const TInt KNumNewEmails(aNewEmails.Count() );
       
   483 	const TInt KNumNewSips(aNewSips.Count() );
       
   484 	for (TInt i = 0; i < KNumNewPhones; ++i)
       
   485 		{
       
   486 		DoPhoneNumWriteOpL(aNewPhones[i], EInsert, aItemId);
       
   487 		}
       
   488 	for (TInt i = 0; i < KNumNewEmails; ++i)
       
   489 		{
       
   490 		DoNonPhoneWriteOpL(aNewEmails[i], EInsert, aItemId, EEmailAddress);
       
   491 		}
       
   492 	for (TInt i = 0; i < KNumNewSips; ++i)
       
   493 		{
       
   494 		DoNonPhoneWriteOpL(aNewSips[i], EInsert, aItemId, ESipAddress);
       
   495 		}
       
   496 	}
       
   497 
       
   498 
       
   499 /**
       
   500 Deletes individual communication addresses from the database. In other words, deletes at
       
   501 the sub-contact item level rather than deleting everything with a specific contact item ID.
       
   502 */
       
   503 void CPplCommAddrTable::DeleteSingleCommAddrL(TInt aCommAddrId, TBool& aLowDiskErrorOccurred)
       
   504 	{
       
   505 	RSqlStatement stmnt;
       
   506 	CleanupClosePushL(stmnt);
       
   507 	stmnt.PrepareL(iDatabase, iSingleDeleteStmnt->SqlStringL() );
       
   508 	const TInt KCommAddrIdParamIndex(KFirstIndex); // first and only parameter in the query
       
   509 	User::LeaveIfError(stmnt.BindInt(KCommAddrIdParamIndex, aCommAddrId ) );
       
   510 	TInt err = stmnt.Exec();
       
   511 	CleanupStack::PopAndDestroy(&stmnt);
       
   512 
       
   513 	if (err == KErrDiskFull)
       
   514 		{
       
   515 		aLowDiskErrorOccurred = ETrue;
       
   516 		}
       
   517 	else
       
   518 		{
       
   519 		User::LeaveIfError(err);
       
   520 		}
       
   521 	}
       
   522 
       
   523 /**
       
   524 Performs write operations for individual communication addresses of type "phone number".
       
   525 This version of the method does not require a CommAddrId argument. Therefore, this can only be used
       
   526 for insertion and not updating.
       
   527 */
       
   528 void CPplCommAddrTable::DoPhoneNumWriteOpL(const CPplCommAddrTable::TMatch& aPhoneNum, TCntSqlStatement aType,
       
   529 											TInt aCntId)
       
   530 	{
       
   531 	// provide a commaddr of 0 as a default argument
       
   532 	DoPhoneNumWriteOpL(aPhoneNum, aType, aCntId, 0);
       
   533 	}
       
   534 
       
   535 
       
   536 /**
       
   537 Performs write (Insert/Update) operations for indiviual communication addresses of type "phone number".
       
   538 */
       
   539 void CPplCommAddrTable::DoPhoneNumWriteOpL(const CPplCommAddrTable::TMatch& aPhoneNum, TCntSqlStatement aType,
       
   540 											TInt aCntId, TInt aCommAddrId)
       
   541 	{
       
   542 	// leave if the statement type is not insert or update.
       
   543 	// also, we can't update if aCommAddrId is 0 as we don't know the record's id
       
   544 	if ((aType != EUpdate && aType != EInsert) || (aType == EUpdate && aCommAddrId == 0) )
       
   545 		{
       
   546 		User::Leave(KErrArgument);
       
   547 		}
       
   548 
       
   549 	RSqlStatement stmnt;
       
   550 	CleanupClosePushL(stmnt);
       
   551 
       
   552 	// temporary reference to the CCntSqlStatements member variables to take advantage
       
   553 	// of the commonality between update and insert operations.
       
   554 	CCntSqlStatement* tempCntStmnt = iUpdateStmnt;
       
   555 	if (aType == EInsert)
       
   556 		{
       
   557 		tempCntStmnt = iInsertStmnt;
       
   558 		}
       
   559 
       
   560 	User::LeaveIfError(stmnt.Prepare(iDatabase, tempCntStmnt->SqlStringL() ) );
       
   561 	User::LeaveIfError(stmnt.BindInt(tempCntStmnt->ParameterIndex(KCommAddrContactId() ), aCntId) );
       
   562 	User::LeaveIfError(stmnt.BindInt(tempCntStmnt->ParameterIndex(KCommAddrExtraValue() ), aPhoneNum.iUpperDigits) );
       
   563 	User::LeaveIfError(stmnt.BindInt(tempCntStmnt->ParameterIndex(KCommAddrValue() ), aPhoneNum.iLowerSevenDigits) );
       
   564 	User::LeaveIfError(stmnt.BindInt(tempCntStmnt->ParameterIndex(KCommAddrType() ), EPhoneNumber) );
       
   565 
       
   566 	if (aType == EInsert)
       
   567 		{
       
   568 		User::LeaveIfError(stmnt.BindNull(tempCntStmnt->ParameterIndex(KCommAddrId() ) ) );
       
   569 		}
       
   570 	else
       
   571 		{
       
   572 		// it's the fifth parameter in the query and is in the WHERE
       
   573 		// clause so we can't get its index from the CCntSqlStatement
       
   574 		const TInt KCommAddrIdParamIndex(KFirstIndex + 4);
       
   575 		User::LeaveIfError(stmnt.BindInt(KCommAddrIdParamIndex, aCommAddrId) );
       
   576 		}
       
   577 
       
   578 	User::LeaveIfError(stmnt.Exec() );
       
   579 	CleanupStack::PopAndDestroy(&stmnt);
       
   580 	}
       
   581 
       
   582 
       
   583 /**
       
   584 Performs write operations for individual communication addresses of type "email" or "SIP" address.
       
   585 This version of the method does not require a CommAddrId argument. Therefore, this can only be used
       
   586 for insertion and not updating.
       
   587 */
       
   588 void CPplCommAddrTable::DoNonPhoneWriteOpL(const TDesC& aAddress, const TCntSqlStatement aType,
       
   589 										 TInt aCntId, TCommAddrType aAddrType)
       
   590 	{
       
   591 	// provide a commaddr of 0 as a default argument
       
   592 	DoNonPhoneWriteOpL(aAddress, aType, aCntId, aAddrType, 0);
       
   593 	}
       
   594 
       
   595 
       
   596 /**
       
   597 Performs write (Insert/Update) operations for indiviual communication addresses of
       
   598 type "Email address" or "SIP Address".
       
   599 */
       
   600 void CPplCommAddrTable::DoNonPhoneWriteOpL(const TDesC& aAddress, const TCntSqlStatement aType,
       
   601 										 TInt aCntId, TCommAddrType aAddrType, TInt aCommAddrId)
       
   602 	{
       
   603 	// leave if the statement type is not insert or update.
       
   604 	// also, we can't update if aCommAddrId is 0 as we don't know the record's id
       
   605 	if ((aType != EUpdate && aType != EInsert) || (aType == EUpdate && aCommAddrId == 0) )
       
   606 		{
       
   607 		User::Leave(KErrNotFound);
       
   608 		}
       
   609 
       
   610 	RSqlStatement stmnt;
       
   611 	CleanupClosePushL(stmnt);
       
   612 
       
   613 	// temporary reference to the CCntSqlStatements member variables to take advantage
       
   614 	// of the commonality between update and insert operations.
       
   615 	CCntSqlStatement* tempCntStmnt = iUpdateStmnt;
       
   616 	if (aType == EInsert)
       
   617 		{
       
   618 		tempCntStmnt = iInsertStmnt;
       
   619 		}
       
   620 
       
   621 	User::LeaveIfError(stmnt.Prepare(iDatabase, tempCntStmnt->SqlStringL() ) );
       
   622 	User::LeaveIfError(stmnt.BindInt(tempCntStmnt->ParameterIndex(KCommAddrContactId() ), aCntId) );
       
   623 	User::LeaveIfError(stmnt.BindText(tempCntStmnt->ParameterIndex(KCommAddrValue() ), aAddress) );
       
   624 	User::LeaveIfError(stmnt.BindNull(tempCntStmnt->ParameterIndex(KCommAddrExtraValue() ) ) );
       
   625 	User::LeaveIfError(stmnt.BindInt(tempCntStmnt->ParameterIndex(KCommAddrType() ), aAddrType) );
       
   626 
       
   627 	if (aType == EInsert)
       
   628 		{
       
   629 		User::LeaveIfError(stmnt.BindNull(tempCntStmnt->ParameterIndex(KCommAddrId() ) ) );
       
   630 		}
       
   631 	else
       
   632 		{
       
   633 		// it's the fifth parameter in the query and is in the WHERE
       
   634 		// clause so we can't get its index from the CCntSqlStatement
       
   635 		const TInt KCommAddrIdParamIndex(KFirstIndex + 4);
       
   636 		User::LeaveIfError(stmnt.BindInt(KCommAddrIdParamIndex, aCommAddrId) );
       
   637 		}
       
   638 
       
   639 	User::LeaveIfError(stmnt.Exec() );
       
   640 	CleanupStack::PopAndDestroy(&stmnt);
       
   641 	}
       
   642 
       
   643 
       
   644 /**
       
   645 Deletes all the communication addresses for a particular contact item. Should be used when
       
   646 deleting a contact item from the database altogether.
       
   647 
       
   648 @param aItem The contact item whose communcation addresses are to be deleted.
       
   649 */
       
   650 void CPplCommAddrTable::DeleteL(const CContactItem& aItem, TBool& aLowDiskErrorOccurred)
       
   651 	{
       
   652 	const TUid KType = aItem.Type();
       
   653 	if (KType != KUidContactCard && KType != KUidContactOwnCard && KType != KUidContactICCEntry)
       
   654 		{
       
   655 		return;
       
   656 		}
       
   657 
       
   658 	RSqlStatement stmnt;
       
   659 	CleanupClosePushL(stmnt);
       
   660 	stmnt.PrepareL(iDatabase, iAllForItemDeleteStmnt->SqlStringL() );
       
   661 	const TInt KContactIdParamIndex(KFirstIndex); // first and only parameter in query
       
   662 	User::LeaveIfError(stmnt.BindInt(KContactIdParamIndex, aItem.Id() ) );
       
   663 	TInt err = stmnt.Exec();
       
   664 	CleanupStack::PopAndDestroy(&stmnt);
       
   665 
       
   666 	if (err == KErrDiskFull)
       
   667 		{
       
   668 		aLowDiskErrorOccurred = ETrue;
       
   669 		}
       
   670 	else
       
   671 		{
       
   672 		User::LeaveIfError(err);
       
   673 		}
       
   674 	}
       
   675 
       
   676 
       
   677 /**
       
   678 Creates the comm_addr table and its indexes in the database.
       
   679 */
       
   680 void CPplCommAddrTable::CreateTableL()
       
   681 	{
       
   682 	User::LeaveIfError(iDatabase.Exec(KCommAddrCreateStmnt) );
       
   683 	}
       
   684 
       
   685 
       
   686 /**
       
   687 Returns an array of contact item IDs for all the contact items which may contain
       
   688 the specified telephone number in a telephone, fax or SMS type field.
       
   689 
       
   690 The comparison method used is not exact.  The number is compared starting from
       
   691 the right side of the number and the method returns an array of candidate
       
   692 matches.  Punctuation (e.g. spaces) and other alphabetic characters are ignored
       
   693 when comparing.
       
   694 
       
   695 Additionally, if the Contacts Model Phone Parser (CNTPHONE.DLL) is available,
       
   696 then any DTMF digits are also excluded from the comparision.
       
   697 
       
   698 Note that due to the way numbers are stored in the database, it is recommended
       
   699 that at least 7 match digits are specified even when matching a number
       
   700 containing fewer digits.  Failure to follow this guideline may (depending on the
       
   701 database contents) mean that the function will not return the expected Contact
       
   702 IDs.
       
   703 
       
   704 @param aNumber Phone number string.
       
   705 @param aMatchLengthFromRight Number of digits from the right of the phone number
       
   706 to use.  Up to 15 digits can be specified, and it is recommended that at least 7
       
   707 match digits are specified.
       
   708 @param aDatabase The database.
       
   709 
       
   710 @return Array of contact IDs which are candidate matches.
       
   711 */
       
   712 CContactIdArray* CPplCommAddrTable::MatchPhoneNumberL(const TDesC& aNumber, const TInt aMatchLengthFromRight)
       
   713     {
       
   714     CContactIdArray* phoneMatchArray = CContactIdArray::NewLC();
       
   715 
       
   716     TInt numLowerDigits = aMatchLengthFromRight;
       
   717     TInt numUpperDigits = 0;
       
   718 
       
   719     if(numLowerDigits > KLowerSevenDigits)
       
   720         {
       
   721         // New style matching.
       
   722         numLowerDigits = KLowerSevenDigits;
       
   723         numUpperDigits = aMatchLengthFromRight - KLowerSevenDigits;
       
   724         }
       
   725 
       
   726     TMatch phoneDigits = CreatePaddedPhoneDigitsL(aNumber, numLowerDigits, numUpperDigits);
       
   727 
       
   728     if (phoneDigits.iNumLowerDigits + phoneDigits.iNumUpperDigits > 0)
       
   729         {
       
   730         // build statement
       
   731         RSqlStatement stmnt;
       
   732         CleanupClosePushL(stmnt);
       
   733         stmnt.PrepareL(iDatabase, iMatchSelectStmnt->SqlStringL() );
       
   734 
       
   735         const TInt KValueParamIndex(KFirstParam);           // first parameter in query...
       
   736         const TInt KTypeParamIndex(KValueParamIndex + 1);   // ...and the second.
       
   737 
       
   738         User::LeaveIfError(stmnt.BindInt(KValueParamIndex, phoneDigits.iLowerSevenDigits ));
       
   739         User::LeaveIfError(stmnt.BindInt(KTypeParamIndex, EPhoneNumber ));
       
   740 
       
   741         // fetch the list of any matching contact ids
       
   742         TInt err(KErrNone);
       
   743         const TInt KContactIdIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrContactId() ) );
       
   744         const TInt KExtraValueIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrExtraValue() ) );
       
   745         while ((err = stmnt.Next() ) == KSqlAtRow)
       
   746             {
       
   747             if (aMatchLengthFromRight <= KLowerSevenDigits)
       
   748                 {
       
   749                 // Matching 7 or less digits...we've already matched.
       
   750                 phoneMatchArray->AddL(stmnt.ColumnInt(KContactIdIdx) );
       
   751                 }
       
   752             else
       
   753                 {
       
   754                 // Check the upper digits...
       
   755                 TInt32 storedUpperDigits(0);
       
   756                 TPtrC extValString = stmnt.ColumnTextL(KExtraValueIdx);
       
   757                 User::LeaveIfError(TLex(extValString).Val(storedUpperDigits) );
       
   758 
       
   759                 const TInt KDigitsToRemove = KMaxPhoneMatchLength - KLowerSevenDigits - phoneDigits.iNumUpperDigits;
       
   760                 for(TInt i = 0; i < KDigitsToRemove; ++i)
       
   761                     {
       
   762                     // repeatedly divide by 10 to lop off the appropriate number of digits from the right
       
   763                     storedUpperDigits /= 10;
       
   764                     }
       
   765 
       
   766                 storedUpperDigits = TMatch::PadOutPhoneMatchNumber(storedUpperDigits, KDigitsToRemove);
       
   767 
       
   768                 if (phoneDigits.iUpperDigits == storedUpperDigits)
       
   769                     {
       
   770                     phoneMatchArray->AddL(stmnt.ColumnInt(KContactIdIdx) );
       
   771                     }
       
   772                 }
       
   773             }
       
   774 
       
   775         // leave if we didn't complete going through the results properly
       
   776         if(err != KSqlAtEnd)
       
   777             {
       
   778             User::Leave(err);
       
   779             }
       
   780         CleanupStack::PopAndDestroy(&stmnt);
       
   781         }
       
   782 
       
   783     CleanupStack::Pop(phoneMatchArray);
       
   784     return phoneMatchArray;
       
   785     }
       
   786 
       
   787 /**
       
   788 Returns an array of contact item IDs for all the contact items which may contain
       
   789 the specified telephone number in a telephone, fax or SMS type field.
       
   790 
       
   791 This is improved version of MatchPhoneNumberL method.
       
   792 The number is compared starting from the right side of the number and 
       
   793 the method returns an array of candidate matches.  
       
   794 Punctuation (e.g. spaces) and other alphabetic characters are ignored
       
   795 when comparing. Leading zeros are removed. Digits are compared up to 
       
   796 the lenght of shorter number.
       
   797 
       
   798 @param aNumber Phone number string.
       
   799 @return Array of contact IDs which are candidate matches.
       
   800 */
       
   801 CContactIdArray* CPplCommAddrTable::BestMatchingPhoneNumberL(const TDesC& aNumber)
       
   802     {
       
   803     const TInt KUpperMaxLength = KMaxPhoneMatchLength - KLowerSevenDigits;
       
   804 
       
   805     CContactIdArray* phoneMatchArray = CContactIdArray::NewLC();
       
   806 
       
   807     TMatch phoneDigits = CreatePaddedPhoneDigitsL(aNumber, KLowerSevenDigits, KUpperMaxLength);
       
   808 
       
   809     if (phoneDigits.iNumLowerDigits + phoneDigits.iNumUpperDigits > 0)
       
   810         {
       
   811         // build statement
       
   812         RSqlStatement stmnt;
       
   813         CleanupClosePushL(stmnt);
       
   814         stmnt.PrepareL(iDatabase, iMatchSelectStmnt->SqlStringL());
       
   815 
       
   816         const TInt KValueParamIndex(KFirstParam); // first parameter in query...
       
   817         const TInt KTypeParamIndex(KValueParamIndex + 1); // ...and the second.
       
   818 
       
   819         User::LeaveIfError(stmnt.BindInt(KValueParamIndex,
       
   820                 phoneDigits.iLowerSevenDigits));
       
   821         User::LeaveIfError(stmnt.BindInt(KTypeParamIndex, EPhoneNumber));
       
   822 
       
   823         // fetch the list of any matching contact ids
       
   824         TInt err(KErrNone);
       
   825         const TInt KContactIdIdx(iMatchSelectStmnt->ParameterIndex( KCommAddrContactId()));
       
   826         const TInt KExtraValueIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrExtraValue()));
       
   827         while ((err = stmnt.Next()) == KSqlAtRow)
       
   828             {
       
   829             // Check the upper digits...
       
   830             TInt32 number = phoneDigits.iUpperDigits;
       
   831             TPtrC extValString = stmnt.ColumnTextL(KExtraValueIdx);
       
   832             TInt32 storedUpperDigits;
       
   833             User::LeaveIfError(TLex(extValString).Val(storedUpperDigits));
       
   834             TInt32 stored = storedUpperDigits;
       
   835 
       
   836             TBool nonZeroInStoredFound = EFalse;
       
   837             TBool nonZeroInNumberFound = EFalse;
       
   838             while ((number != 0) && (stored != 0))
       
   839                 {
       
   840                 nonZeroInNumberFound |= (number % 10 != 0);
       
   841                 nonZeroInStoredFound |= (stored % 10 != 0);
       
   842                 if (nonZeroInStoredFound && nonZeroInNumberFound)
       
   843                     {
       
   844                     break;
       
   845                     }
       
   846                 number /= 10;
       
   847                 stored /= 10;
       
   848                 }
       
   849 
       
   850             if ((phoneDigits.iUpperDigits == 0) || (storedUpperDigits == 0) ||
       
   851                  (number == stored))
       
   852                 {
       
   853                 phoneMatchArray->AddL(stmnt.ColumnInt(KContactIdIdx));
       
   854                 }
       
   855             }
       
   856 
       
   857         // leave if we didn't complete going through the results properly
       
   858         if (err != KSqlAtEnd)
       
   859             {
       
   860             User::Leave(err);
       
   861             }
       
   862         CleanupStack::PopAndDestroy(&stmnt);
       
   863         }
       
   864 
       
   865     CleanupStack::Pop(phoneMatchArray);
       
   866     return phoneMatchArray;
       
   867     }
       
   868 
       
   869 
       
   870 /**
       
   871 Searches the contacts database to find any contact items with an exact match on the email address supplied.
       
   872 
       
   873 @param aEmailAddr A descriptor containing the email address to be found in the database.
       
   874 @return An array of contact IDs which match the supplied email address.
       
   875 */
       
   876 CContactIdArray* CPplCommAddrTable::MatchEmailAddressL(const TDesC& aEmailAddr)
       
   877 	{
       
   878 	return MatchNonPhoneAddrL(aEmailAddr, EEmailAddress);
       
   879 	}
       
   880 
       
   881 
       
   882 /**
       
   883 Searches the contacts database to find any contact items with an exact match on the SIP address supplied.
       
   884 
       
   885 @param aSipAddr A descriptor containing the SIP address to be found in the database.
       
   886 @return An array of contact IDs which match the supplied SIP address.
       
   887 */
       
   888 CContactIdArray* CPplCommAddrTable::MatchSipAddressL(const TDesC& aSipAddr)
       
   889 	{
       
   890 	return MatchNonPhoneAddrL(aSipAddr, ESipAddress);
       
   891 	}
       
   892 
       
   893 
       
   894 /**
       
   895 Searches the contacts database to find any contact items with an exact match on the address supplied.
       
   896 
       
   897 @param aCommAddr A descriptor containing the address to be found in the database.
       
   898 @param aAddrType The type of addresses that is being sought.
       
   899 @return An array of contact IDs which match the supplied address.
       
   900 */
       
   901 CContactIdArray* CPplCommAddrTable::MatchNonPhoneAddrL(const TDesC& aCommAddr, TCommAddrType aAddrType)
       
   902 	{
       
   903 
       
   904 	// build statement
       
   905 	RSqlStatement stmnt;
       
   906 	CleanupClosePushL(stmnt);
       
   907 	stmnt.PrepareL(iDatabase, iMatchSelectStmnt->SqlStringL() );
       
   908 
       
   909 	const TInt KValueParamIndex(KFirstParam);					// first parameter in query...
       
   910 	const TInt KTypeParamIndex(KValueParamIndex + 1);	// ...and the second.
       
   911 	User::LeaveIfError(stmnt.BindText(KValueParamIndex, aCommAddr) );
       
   912 	User::LeaveIfError(stmnt.BindInt(KTypeParamIndex, aAddrType) );
       
   913 
       
   914 	// fetch the list of any matching contact ids
       
   915 	CContactIdArray* idArray = CContactIdArray::NewLC();
       
   916 	TInt err(KErrNone);
       
   917 	const TInt KContactIdIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrContactId() ) );
       
   918 	while ((err = stmnt.Next() ) == KSqlAtRow)
       
   919 		{
       
   920 		idArray->AddL(stmnt.ColumnInt(KContactIdIdx) );
       
   921 		}
       
   922 
       
   923 	// leave if we didn't complete going through the results properly
       
   924 	if(err != KSqlAtEnd)
       
   925 		{
       
   926 		User::Leave(err);
       
   927 		}
       
   928 
       
   929 	CleanupStack::Pop(idArray);
       
   930 	CleanupStack::PopAndDestroy(&stmnt);
       
   931 
       
   932 	return idArray;
       
   933 	}
       
   934 
       
   935 
       
   936 /**
       
   937 CPplCommAddrTable constructor
       
   938 */
       
   939 CPplCommAddrTable::CPplCommAddrTable(RSqlDatabase& aDatabase, CLplContactProperties& aProperties)
       
   940 	: iProperties(aProperties),
       
   941 	iDatabase(aDatabase)
       
   942 	{
       
   943 	}
       
   944 
       
   945 
       
   946 /**
       
   947 Destructor
       
   948 
       
   949 Tidy up CCntSqlStatement objects
       
   950 */
       
   951 CPplCommAddrTable::~CPplCommAddrTable()
       
   952 	{
       
   953 	delete iInsertStmnt;
       
   954 	delete iWholeSelectStmnt;
       
   955 	delete iMatchSelectStmnt;
       
   956 	delete iUpdateStmnt;
       
   957 	delete iSingleDeleteStmnt;
       
   958 	delete iAllForItemDeleteStmnt;
       
   959 	}
       
   960 
       
   961 /**
       
   962 Convert the supplied string to a matchable phone number.
       
   963 
       
   964 @param aText Descriptor containing phone number.
       
   965 @param aLowerMatchlength Number of least significant phone digits to use.
       
   966 @param aUpperMatchLength Number of most significant phone digits to use.
       
   967 
       
   968 @return The hash code(s) to use when matching the supplied phone number.
       
   969 */
       
   970 CPplCommAddrTable::TMatch CPplCommAddrTable::CreatePaddedPhoneDigitsL(const TDesC& aNumber, const TInt aNumLowerDigits, const TInt aNumUpperDigits)
       
   971 	{
       
   972 	CPplCommAddrTable::TMatch phoneNumber = CreatePhoneMatchNumberL(aNumber, aNumLowerDigits, aNumUpperDigits);
       
   973 
       
   974 	if (phoneNumber.iNumLowerDigits + phoneNumber.iUpperDigits == 0)
       
   975 		{
       
   976 		// No digits, do nothing
       
   977 		}
       
   978 	else if(phoneNumber.iNumLowerDigits < KLowerSevenDigits)
       
   979 		{
       
   980 		// Only the lower-digits hash is used, pad out the number to
       
   981 		// KLowerSevenDigits.
       
   982 		TInt pad = KLowerSevenDigits - phoneNumber.iNumLowerDigits;
       
   983 		phoneNumber.iLowerSevenDigits = TMatch::PadOutPhoneMatchNumber(phoneNumber.iLowerSevenDigits,pad);
       
   984 		}
       
   985 	else if(phoneNumber.iNumUpperDigits < (KMaxPhoneMatchLength - KLowerSevenDigits) )
       
   986 		{
       
   987 		// The lower-digits hash is full, pad out the upper hash if less than 15
       
   988 		// digits total.
       
   989 		TInt pad = KMaxPhoneMatchLength - KLowerSevenDigits - phoneNumber.iNumUpperDigits;
       
   990 		phoneNumber.iUpperDigits = TMatch::PadOutPhoneMatchNumber(phoneNumber.iUpperDigits,pad);
       
   991 		}
       
   992 
       
   993 	return phoneNumber;
       
   994 	}
       
   995 
       
   996 /**
       
   997 CPplCommAddrTable::TMatch constructor.
       
   998 */
       
   999 CPplCommAddrTable::TMatch::TMatch()
       
  1000 	:
       
  1001 	iLowerSevenDigits(0),
       
  1002 	iUpperDigits(0),
       
  1003 	iNumLowerDigits(0),
       
  1004 	iNumUpperDigits(0)
       
  1005 	{
       
  1006 	}
       
  1007 
       
  1008 
       
  1009 TBool CPplCommAddrTable::TMatch::operator==(const TMatch& aSecondMatch)const
       
  1010 	{
       
  1011 	return (iLowerSevenDigits == aSecondMatch.iLowerSevenDigits)&& (iUpperDigits == aSecondMatch.iUpperDigits);
       
  1012 	}
       
  1013 
       
  1014 
       
  1015 TBool CPplCommAddrTable::TMatch::Equals(const TMatch& aRMatch, const TMatch& aLMatch)
       
  1016 	{
       
  1017 	return aRMatch == aLMatch;
       
  1018 	}
       
  1019 
       
  1020 /**
       
  1021 Pad out the supplied phone digits with zeroes.
       
  1022 
       
  1023 @param aPhoneNumber The number to pad.
       
  1024 @param aPadOutLength The number of digits to pad out.
       
  1025 
       
  1026 @return The padded number.
       
  1027 */
       
  1028 TInt32 CPplCommAddrTable::TMatch::PadOutPhoneMatchNumber(TInt32& aPhoneNumber, TInt aPadOutLength)
       
  1029 	{
       
  1030 	TInt32 result(aPhoneNumber);
       
  1031 	const TInt KMult(10);
       
  1032 	for(TInt i = 0; i < aPadOutLength; ++i)
       
  1033 		{
       
  1034 		result *= KMult;
       
  1035 		}
       
  1036 	aPhoneNumber = result;
       
  1037 	return result;
       
  1038 	}
       
  1039 
       
  1040 
       
  1041 /**
       
  1042 Returns the hash code(s) to use when matching the supplied phone number.  If the
       
  1043 number supplied has more actual phone digits (i.e. not including spaces) than
       
  1044 KLowerSevenDigits, a second hash is generated to hold the remaining most
       
  1045 significant phone digits.  Extracts DTMF digits from the phone number if a
       
  1046 parser is available, otherwise just removes the non-digit characters.
       
  1047 
       
  1048 @param aText Descriptor containing contacts phone number field.
       
  1049 @param aLowerMatchlength Number of least significant phone digits to use.
       
  1050 @param aUpperMatchLength Number of most significant phone digits to use.
       
  1051 
       
  1052 @return The hash code(s) to use when matching the supplied phone number.
       
  1053 */
       
  1054 CPplCommAddrTable::TMatch CPplCommAddrTable::CreatePhoneMatchNumberL(const TDesC& aText, TInt aLowerMatchLength, TInt aUpperMatchLength)
       
  1055 	{
       
  1056 	__ASSERT_DEBUG( ((aLowerMatchLength == KLowerSevenDigits) && (aUpperMatchLength > 0) )		// upper 8 digits
       
  1057 					|| ((aLowerMatchLength <= KLowerSevenDigits) && (aUpperMatchLength == 0) ),	// lower 7 digits
       
  1058 					User::Leave(KErrNotSupported) );
       
  1059 
       
  1060 	const TInt KBufLength = KCntMaxTextFieldLength+1;
       
  1061 	TBuf<KBufLength> buf;
       
  1062 
       
  1063 	CContactPhoneNumberParser* parser = iProperties.ContactPhoneParserL().Parser();
       
  1064 
       
  1065 	if (parser)
       
  1066 		{
       
  1067 		parser->ExtractRawNumber(aText.Left(KCntMaxTextFieldLength),buf);
       
  1068 		}
       
  1069 	else
       
  1070 		{
       
  1071 		if(aText.Length() <= KBufLength)
       
  1072 			{
       
  1073 			buf = aText;
       
  1074 			}
       
  1075 		else
       
  1076 			{
       
  1077 			buf = aText.Right(KBufLength);
       
  1078 			}
       
  1079 		TMatch::StripOutNonDigitChars(buf);
       
  1080 		}
       
  1081 
       
  1082 	TMatch phoneNumber;
       
  1083 
       
  1084 	if (buf.Length() == 0)
       
  1085 		{
       
  1086 		return phoneNumber;
       
  1087 		}
       
  1088 
       
  1089 	// Generate a hash for the upper digits only if the phone number string is
       
  1090 	// large enough and more than 7 digits are to be matched.
       
  1091 	TInt phoneNumberlength = buf.Length();
       
  1092 	if ( (phoneNumberlength > KLowerSevenDigits) && (aUpperMatchLength > 0) )
       
  1093 		{
       
  1094 		TPtrC upperPart = buf.Left(phoneNumberlength - KLowerSevenDigits);
       
  1095 		phoneNumber.iUpperDigits = TMatch::CreateHashL(upperPart,
       
  1096 			aUpperMatchLength, phoneNumber.iNumUpperDigits);
       
  1097 		}
       
  1098 
       
  1099 	// Generate a hash of the lower digits.
       
  1100 	phoneNumber.iLowerSevenDigits = TMatch::CreateHashL(buf, aLowerMatchLength, phoneNumber.iNumLowerDigits);
       
  1101 
       
  1102 	return phoneNumber;
       
  1103 	}
       
  1104 
       
  1105 
       
  1106 /**
       
  1107 Strip out any non-digit characters before we convert the phone number to an
       
  1108 integer.
       
  1109 
       
  1110 @param aText Phone number which on return will have any non-digit characters
       
  1111 removed.
       
  1112 */
       
  1113 void CPplCommAddrTable::TMatch::StripOutNonDigitChars(TDes& aText)
       
  1114 	{
       
  1115 	for(TInt chrPos = 0; chrPos < aText.Length(); ++chrPos)
       
  1116 		{
       
  1117 		TChar chr = aText[chrPos];
       
  1118 		if (!chr.IsDigit() )
       
  1119 			{
       
  1120 			aText.Delete(chrPos, 1);
       
  1121 			--chrPos;
       
  1122 			}
       
  1123 		}
       
  1124 	}
       
  1125 
       
  1126 
       
  1127 /**
       
  1128 Generates a hash value by reversing the aMatchLength least significant digits,
       
  1129 ignoring non-digits and zeroes at the end of the number.  Asserts if no phone
       
  1130 digits are supplied.
       
  1131 
       
  1132 @param aPhoneNumberString A descriptor containing a phone number.
       
  1133 @param aMatchLength The number of digits from the right of the phone number to use.
       
  1134 @param aNumPhoneDigits The number of digits found in the phone number string.
       
  1135 
       
  1136 @return An integer representation of the phone number string in reverse.
       
  1137 */
       
  1138 TInt32 CPplCommAddrTable::TMatch::CreateHashL(const TDesC& aPhoneNumberString, TInt aMatchLength, TInt& aNumPhoneDigits)
       
  1139 	{
       
  1140 	TInt phoneNumberLength = aPhoneNumberString.Length();
       
  1141 	__ASSERT_DEBUG(phoneNumberLength > 0, User::Leave(KErrNotSupported) );
       
  1142 
       
  1143 	TInt startIndex = 0;
       
  1144 
       
  1145 	if (phoneNumberLength > aMatchLength)
       
  1146 		{
       
  1147 		startIndex = phoneNumberLength - aMatchLength;
       
  1148 		}
       
  1149 
       
  1150 	aNumPhoneDigits = 0;
       
  1151 	TUint reversedDigits = 0;
       
  1152 	TInt mult = 1;
       
  1153 
       
  1154 	for (TInt chrIndex = startIndex; (aNumPhoneDigits < aMatchLength) && (chrIndex < phoneNumberLength); chrIndex++)
       
  1155 		{
       
  1156 		TChar chr = aPhoneNumberString[chrIndex];
       
  1157 		if (chr.IsDigit() )
       
  1158 			{
       
  1159 			reversedDigits += (chr.GetNumericValue() ) * mult;
       
  1160 			mult = mult * 10;
       
  1161 			++aNumPhoneDigits;
       
  1162 			}
       
  1163 		}
       
  1164 
       
  1165 	return reversedDigits ;
       
  1166 	}