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