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