phonebookengines/contactsmodel/cntplsql/src/cviewcontactmanager.cpp
branchRCL_3
changeset 20 f4a778e096c2
equal deleted inserted replaced
19:5b6f26637ad3 20:f4a778e096c2
       
     1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 /**
       
    17  @file
       
    18  @internalComponent
       
    19  @released
       
    20 */
       
    21 
       
    22 #include <cntviewsortplugin.h>
       
    23 #include "cntviewprivate.h"
       
    24 #include "cviewcontactmanager.h"
       
    25 
       
    26 #include <cntviewbase.h>
       
    27 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
       
    28 #include <cntviewsortpluginbase.h>
       
    29 #endif
       
    30 
       
    31 const TInt KContactsArrayGranularity = 100;
       
    32 const TInt KUnsortedArrayGranularity = 16;
       
    33 
       
    34 /**
       
    35 @SYMPatchable
       
    36 @publishedPartner
       
    37 @released
       
    38  
       
    39 Patchable constant for the number of CViewContacts to read into memory 
       
    40 before merging them into a larger sorted list (via a heap sort).
       
    41 When the view contacts are merged, their text fields are released from memory. 
       
    42 The larger the constant the faster a View containing large numbers of items is created. 
       
    43 However, a larger constant will use more RAM for large Views.
       
    44  
       
    45 The constant can be changed at ROM build time using patchdata OBY keyword.
       
    46 */
       
    47 IMPORT_C extern const TInt KNumberOfContactsToReadPerMerge;
       
    48 
       
    49 /**
       
    50 @internalComponent
       
    51 @released
       
    52 
       
    53 Number of Contacts to process per invocation of the Sorter
       
    54 Local Views can't do much whilst sorting, but we want to allow other
       
    55 Active Objects in the thread to run. Use to be 50 for the DBMS version, 75 here to match
       
    56 performance (i.e. should occupy the server for the same amount of time).
       
    57 */    
       
    58 const TInt KNumberOfContactsToReadPerChunk = 75;
       
    59 
       
    60 
       
    61 /** 
       
    62 Private constructor
       
    63 */
       
    64 CViewContactManager::CViewContactManager(CContactLocalView& aLocalView, MLplViewIteratorManager& aLplViewMgr, TContactViewPreferences aViewPreferences, CViewContactSortPlugin* aSortPlugin)
       
    65 : iLocalView(aLocalView),
       
    66   iViewSessionId(KPLViewSessionIdNull),   
       
    67   iViewContacts(NULL), 
       
    68   iUnsortedViewContacts(NULL), 
       
    69   iViewContactsBuffer(NULL), 
       
    70   iSortPluginImpl(aSortPlugin), 
       
    71   iViewPreferences(aViewPreferences),
       
    72   iLplViewItemMgr(aLplViewMgr) 
       
    73     {
       
    74     }
       
    75 
       
    76     
       
    77 /** 
       
    78 Destructor
       
    79 */
       
    80 CViewContactManager::~CViewContactManager()
       
    81     {
       
    82     if(iViewSessionId != KPLViewSessionIdNull)
       
    83         {
       
    84     	iLplViewItemMgr.CloseView(iViewSessionId);
       
    85         }
       
    86 	
       
    87     delete iIdleSorter;
       
    88     
       
    89     if(iViewContacts)
       
    90     	{
       
    91     	iViewContacts->ResetAndDestroy();
       
    92 		delete iViewContacts;	
       
    93     	}	
       
    94 	if (iUnsortedViewContacts)
       
    95 		{
       
    96 		iUnsortedViewContacts->ResetAndDestroy();
       
    97 		delete iUnsortedViewContacts;	
       
    98 		}	
       
    99 	
       
   100 	if(iViewContactsBuffer)
       
   101 		{
       
   102 		iViewContactsBuffer->ResetAndDestroy();
       
   103 		delete iViewContactsBuffer;
       
   104 		}
       
   105 		
       
   106 	//The class doesn't own iSortPluginImpl,so don't release it here.
       
   107     }
       
   108 
       
   109     
       
   110 /** 
       
   111 Static factory constructor. Uses two phase construction and 
       
   112 leaves nothing on the CleanupStack.
       
   113 
       
   114 @return A pointer to the newly created CViewContactManager object.
       
   115 */
       
   116 CViewContactManager* CViewContactManager::NewL(CContactLocalView& aLocalView, MLplPersistenceLayerFactory& aFactory, const CContactTextDef& aTextDef, TContactViewPreferences aViewPreferences, CViewContactSortPlugin* aSortPlugin)
       
   117     {
       
   118     MLplViewIteratorManager& lplViewIteratorManager = aFactory.GetViewIteratorManagerL();
       
   119 	CViewContactManager* self = new(ELeave) CViewContactManager(aLocalView, lplViewIteratorManager, aViewPreferences, aSortPlugin);
       
   120 	CleanupStack::PushL(self);
       
   121 	self->ConstructL(aFactory, aTextDef);
       
   122 	CleanupStack::Pop(self);
       
   123 	return self;
       
   124     }
       
   125 
       
   126     
       
   127 /** 
       
   128 Constructor
       
   129 */
       
   130 void CViewContactManager::ConstructL(MLplPersistenceLayerFactory& aFactory, const CContactTextDef& aTextDef)
       
   131 	{
       
   132 	iViewContacts = new(ELeave) RPointerArray<CViewContact>(KContactsArrayGranularity);
       
   133 	iUnsortedViewContacts = new(ELeave) RPointerArray<CViewContact>(KUnsortedArrayGranularity);
       
   134 	
       
   135 	// Collation method by index 0 stays the same (only changes at a reboot)
       
   136  	iCollationMethod = *Mem::CollationMethodByIndex(0);
       
   137  	iCollationMethod.iFlags |= TCollationMethod::EIgnoreNone;
       
   138 	
       
   139 	iViewSessionId = iLplViewItemMgr.OpenViewL(aTextDef, iViewPreferences);
       
   140 
       
   141     //Create idle thread to prepare sort asynchronisely		
       
   142 	iIdleSorter = CIdleContactSorter::NewL(*this, aFactory, iViewPreferences);	
       
   143 	}
       
   144 
       
   145 
       
   146 /** 
       
   147 Start sorting with stored sort order.
       
   148 */
       
   149 void CViewContactManager::SortL()
       
   150 	{
       
   151 	iIdleSorter->Stop();	
       
   152 	
       
   153 	iLplViewItemMgr.BeginIterateL(iViewSessionId);
       
   154 	ResetSort();
       
   155 	
       
   156 	iIdleSorter->Start();	
       
   157 	}
       
   158 
       
   159 /** 
       
   160 Start sorting with given sort order/text definition.
       
   161 
       
   162 @param aTextDef new text definition would be used for sorting.
       
   163 */
       
   164 void CViewContactManager::SortL(const CContactTextDef& aTextDef)
       
   165 	{
       
   166 	//Stop current sorting.
       
   167 	iIdleSorter->Stop();	
       
   168 	
       
   169 	iLplViewItemMgr.EndIterateL(iViewSessionId);
       
   170 	iLplViewItemMgr.ChangeSortOrderL(iViewSessionId, aTextDef);
       
   171 	
       
   172 	//Restart iterating with changed sort order 
       
   173 	iLplViewItemMgr.BeginIterateL(iViewSessionId);
       
   174 	
       
   175 	ResetSort();
       
   176 	iIdleSorter->Start();	
       
   177 	}
       
   178 
       
   179 	
       
   180 /** 
       
   181 Start current sorting.
       
   182 */
       
   183 void CViewContactManager::StopSortL()
       
   184 	{
       
   185 	//Stop current sorting.
       
   186 	iIdleSorter->Stop();	
       
   187 	
       
   188 	//Call the lpl layer to stop iterating view contacts.
       
   189 	iLplViewItemMgr.EndIterateL(iViewSessionId);
       
   190 	}
       
   191 
       
   192 	
       
   193 /** 
       
   194 Reset contact ids arrays for resort or initial sort, this is called 
       
   195 when database complete restoring data or view is creating(first sort).
       
   196 */
       
   197 void CViewContactManager::ResetSort()
       
   198     {
       
   199 	ASSERT(iViewContacts);
       
   200     iViewContacts->ResetAndDestroy();
       
   201 	
       
   202 	ASSERT(iUnsortedViewContacts);
       
   203     iUnsortedViewContacts->ResetAndDestroy();
       
   204 	
       
   205 	if(iViewContactsBuffer)
       
   206 		{
       
   207 		iViewContactsBuffer->ResetAndDestroy();
       
   208 		delete iViewContactsBuffer;
       
   209 		iViewContactsBuffer = NULL;
       
   210 		}
       
   211     }
       
   212 
       
   213 
       
   214 /** 
       
   215 Implementation of MContactViewSortObserver interface, this is called with:
       
   216 1. TIccViewNotify_IccOnlyLocked: The view is ICCEntryOnly view and ICC store is locked.
       
   217 2. TIccViewNotify_IccUnlocked: The view include ICCEntry and ICC store is unlocked 
       
   218                                (resort is needed)
       
   219 
       
   220 @param  aIccViewNotify flag to show if ICC store is locked or not.
       
   221 @return ETrue if there is still view contact in database and needs idle
       
   222         sorting asynchroniser call back again.
       
   223 
       
   224 @leave KErrNoMemory Out of memory.
       
   225 @leave KErrNotFound The view session cannot be found.
       
   226 */
       
   227 void  CViewContactManager::IccViewNotifyL(TInt aIccViewNotify)
       
   228     {
       
   229     switch(aIccViewNotify)
       
   230         {
       
   231         case TIccViewNotify_IccOnlyLocked:
       
   232             iLocalView.SetState(CContactLocalView::EReady);
       
   233             break;
       
   234         case TIccViewNotify_IccUnlocked:
       
   235             iLocalView.SetState(CContactLocalView::ENotReady);
       
   236         	iLplViewItemMgr.EndIterateL(iViewSessionId);
       
   237             break;
       
   238         }
       
   239     }
       
   240 
       
   241 
       
   242 /** 
       
   243 Implementation of MContactViewSortObserver interface, this is called by
       
   244 the idle sorting asynchroniser repeatly. 
       
   245 
       
   246 @param  aSortErr if there is error occurs in idle sorting asynchroniser.
       
   247 @return ETrue if there is still view contact in database and needs idle
       
   248         sorting asynchroniser call back again.
       
   249 */
       
   250 TBool CViewContactManager::IdleSorterNotifyL(TInt aSortErr)
       
   251 	{
       
   252 	if(aSortErr != KErrNone)
       
   253 		{
       
   254 		TInt err = KErrNone;
       
   255 		TRAP(err, iLplViewItemMgr.EndIterateL(iViewSessionId));
       
   256 		iLocalView.SortComplete(aSortErr);
       
   257 		return EFalse;
       
   258 		}
       
   259 		
       
   260 	if(!iViewContactsBuffer)
       
   261 		{
       
   262 		//Create a temporary buffer for reading view contact objects.
       
   263 		iViewContactsBuffer = new(ELeave) RPointerArray<CViewContact>(KContactsArrayGranularity);
       
   264 		}
       
   265 		
       
   266 	CViewContactManager::TReadState readState = ReadInViewContactsL(*iViewContactsBuffer);
       
   267     
       
   268     if(readState == EReadFullChunk)
       
   269 		{
       
   270 		// Full chunk of view contact items have been read, so request another read.
       
   271 		return ETrue;
       
   272 		}
       
   273 		
       
   274 	//Reach here only when readState is EReadFullForMerge or EReadCompleted
       
   275 	if (iSortPluginImpl)
       
   276 		{
       
   277 		// prepare View Sort plug-in
       
   278 		User::LeaveIfError(iSortPluginImpl->SortStart(CViewContactSortPlugin::ESortStartFull, iViewContactsBuffer->Count()));
       
   279 		}
       
   280 	
       
   281 	// sort the view contacts in buffer
       
   282 	HeapSortL(*iViewContactsBuffer);
       
   283 
       
   284 	if(iViewContacts->Count() > 0)		
       
   285 		{
       
   286 		//There are sorted view objects stored in iViewContacts, merging is needed.
       
   287 		RPointerArray<CViewContact>* mergedArray = MergeL(*iViewContacts, *iViewContactsBuffer);	
       
   288 		
       
   289 		// all view contact objects have been merged into iViewContacts, 
       
   290 		// clean up the buffer array. 
       
   291 		iViewContactsBuffer->Reset();
       
   292 		
       
   293 		iViewContacts->Close();
       
   294 		delete iViewContacts;
       
   295 		iViewContacts = mergedArray;
       
   296 		}
       
   297 	else
       
   298 		{
       
   299 		//It's first time we get a list of sorted view contat objects
       
   300 		iViewContacts->Close();
       
   301 		delete iViewContacts;
       
   302 		iViewContacts = iViewContactsBuffer;	
       
   303 		iViewContactsBuffer = NULL;
       
   304 		}
       
   305 		
       
   306 	if (iSortPluginImpl)
       
   307 		{
       
   308 		iSortPluginImpl->SortCompleted();
       
   309 		}
       
   310 		
       
   311 	if(readState == EReadCompleted)
       
   312 		{
       
   313 		//Sorting is complete.
       
   314 		iLplViewItemMgr.EndIterateL(iViewSessionId);
       
   315 		iLocalView.SortComplete(KErrNone);
       
   316 		return EFalse;
       
   317 		}
       
   318     
       
   319 	return ETrue;
       
   320 	}
       
   321 
       
   322 
       
   323 /** 
       
   324 Fill in the given view contacts buffer by asking underlying persistence layer,
       
   325 and add all unsorted view contacts into unsorted array directly.
       
   326 
       
   327 @param  aViewContacts CViewContact objects array to fill in and sort.
       
   328 @return One of enum in TReadState, see the definition in TReadState for detail
       
   329 */
       
   330 CViewContactManager::TReadState CViewContactManager::ReadInViewContactsL(RPointerArray<CViewContact>& aViewContacts)
       
   331     {
       
   332 	TInt total = aViewContacts.Count();
       
   333 	
       
   334 	// process a chunk of contacts
       
   335 	CViewContact* contact = NULL;
       
   336 	
       
   337 	//Here to use current sorting view preference in iIdleSorter who knows
       
   338 	//if we can access ICCEntry store now, therefore decides if we should read in
       
   339 	//ICCEntry contacts from the database.
       
   340 	TContactViewPreferences sortViewPref = iIdleSorter->SortViewPreferences();
       
   341 	
       
   342 	for(TInt i = 0; i<KNumberOfContactsToReadPerChunk; ++i)
       
   343 		{
       
   344 		contact = iLplViewItemMgr.NextItemL(iViewSessionId, sortViewPref);
       
   345 		if(contact == NULL)
       
   346 			{
       
   347     		return EReadCompleted;
       
   348 			}
       
   349 			
       
   350 		CleanupStack::PushL(contact);
       
   351 		if(IsContactSortable(*contact,iViewPreferences))
       
   352 			{
       
   353 			aViewContacts.AppendL(contact);
       
   354 			CleanupStack::Pop(contact);
       
   355 		    if(++total >= KNumberOfContactsToReadPerMerge)
       
   356 		        {
       
   357 		        return EReadFullForMerge;
       
   358 		        }
       
   359 			}
       
   360 		else if(iViewPreferences & (EUnSortedAtBeginning | EUnSortedAtEnd))
       
   361 			{
       
   362 			// It's an unsortable contact,
       
   363 			// just append it to unsortable view contacts list.
       
   364 			iUnsortedViewContacts->AppendL(contact);
       
   365 			CleanupStack::Pop(contact);
       
   366 			}
       
   367 		else
       
   368 			{
       
   369 			CleanupStack::PopAndDestroy(contact);
       
   370 			}
       
   371 		}
       
   372 		
       
   373 	return EReadFullChunk;
       
   374     }
       
   375 
       
   376 
       
   377 /** 
       
   378 Check if the contact type uid matchs the view preferences.
       
   379 
       
   380 @param  aContactTypeUid contact type uid to check.
       
   381 @param  aTypeToInclude  view preferences have flags to define which contact the view should have.
       
   382 @return ETrue if the uid match or reverse.
       
   383 */
       
   384 TBool CViewContactManager::ContactCorrectType(TUid aContactTypeUid, TContactViewPreferences aTypeToInclude)
       
   385     {
       
   386     TBool correctType = EFalse;
       
   387 
       
   388     if (aContactTypeUid == KUidContactCard)
       
   389         {
       
   390         if (!(aTypeToInclude & (EGroupsOnly | EICCEntriesOnly)))
       
   391             {
       
   392             correctType = ETrue;
       
   393             }
       
   394         }
       
   395     else if (aContactTypeUid == KUidContactOwnCard)
       
   396         {
       
   397         if (!(aTypeToInclude & (EGroupsOnly | EICCEntriesOnly | EContactCardsOnly)))
       
   398             {
       
   399             correctType = ETrue;
       
   400             }
       
   401         }
       
   402     else if (aContactTypeUid == KUidContactGroup)
       
   403         {
       
   404         if (aTypeToInclude & (EGroupsOnly | EContactAndGroups))
       
   405             {
       
   406             correctType = ETrue;
       
   407             }
       
   408         }
       
   409     else if (aContactTypeUid == KUidContactICCEntry)
       
   410         {
       
   411         if (aTypeToInclude & (EICCEntriesOnly | EICCEntriesAndContacts))
       
   412             {
       
   413             correctType = ETrue;
       
   414             }
       
   415         }
       
   416 
       
   417     return correctType;
       
   418     }
       
   419 
       
   420 
       
   421 /** 
       
   422 Heap sort the give view contacts array.
       
   423 
       
   424 @param  aContacts the array of view contacts to be sorted.
       
   425 @leave  leave errors from CContactViewBase::CompareContactsAndIdsL
       
   426 */
       
   427 void CViewContactManager::HeapSortL(RPointerArray<CViewContact>& aContacts)
       
   428 	{
       
   429 	// HeapSort (stolen from RPointerArrayBase)
       
   430 	TInt ss = aContacts.Count();
       
   431 	if ( ss>1 )
       
   432 		{
       
   433 		TInt sh = ss>>1;
       
   434 		FOREVER
       
   435 			{
       
   436 			CViewContact* si;
       
   437 			if (sh != 0)
       
   438 				{
       
   439 				// make heap
       
   440 				--sh;
       
   441 				si = aContacts[sh];
       
   442 				}
       
   443 			else
       
   444 				{
       
   445 				// sort heap
       
   446 				--ss;
       
   447 				si = aContacts[ss];
       
   448 				aContacts[ss] = aContacts[0];
       
   449 				if (ss == 1)
       
   450 					{
       
   451 					aContacts[0] = si;
       
   452 					break;
       
   453 					}
       
   454 				}
       
   455 
       
   456 			// sift down
       
   457 			TInt ii = sh;
       
   458 			TInt jj = sh;
       
   459 			FOREVER
       
   460 				{
       
   461 				jj = (jj+1)<<1;
       
   462 				if ((jj >= ss) || (iLocalView.CompareContactsAndIdsL(*aContacts[jj-1],*aContacts[jj]) > 0))
       
   463 					{
       
   464 					--jj;
       
   465 					}
       
   466 					
       
   467 				if ((jj >= ss) || (iLocalView.CompareContactsAndIdsL(*aContacts[jj],*si) <= 0))
       
   468 					{
       
   469 					break;
       
   470 					}
       
   471 					
       
   472 				aContacts[ii] = aContacts[jj];
       
   473 				ii = jj;
       
   474 				} //FOREVER
       
   475 				
       
   476 			aContacts[ii] = si;
       
   477 			} //FOREVER
       
   478 		} //if (ss > 1)
       
   479 	}
       
   480 
       
   481 
       
   482 /** 
       
   483 Merge two view contacts array into a result array, here we always assume the items in 
       
   484 aRightContacts are fulfilled objects.
       
   485 
       
   486 @param  aLeftContacts  the left array of view contacts to be merged.
       
   487 @param  aRightContacts the right array of view contacts to be merged.
       
   488 @return the final merged array.
       
   489 */
       
   490 RPointerArray<CViewContact>* CViewContactManager::MergeL(RPointerArray<CViewContact>& aLeftContacts, RPointerArray<CViewContact>& aRightContacts)
       
   491     {
       
   492 	TInt indexLeft(0);
       
   493 	TInt indexRight(0);
       
   494 	const TInt KCountLeft = aLeftContacts.Count();
       
   495 	
       
   496 	RPointerArray<CViewContact>* resultContacts = new(ELeave) RPointerArray<CViewContact>(KContactsArrayGranularity);	
       
   497 	CleanupStack::PushL(TCleanupItem(ResetAndDestroyRPointerArrayPointer, resultContacts));
       
   498 	
       
   499     while(indexLeft < KCountLeft && indexRight < aRightContacts.Count())
       
   500 		{
       
   501 		CViewContact* firstLeftContact = static_cast<CViewContact*>(aLeftContacts[indexLeft]);
       
   502 		CViewContact* firstRightContact = static_cast<CViewContact*>(aRightContacts[indexRight]);
       
   503 		
       
   504 		if(firstLeftContact->IsLightweightObject())
       
   505 			{
       
   506 			//The current contact in iViewContact is lightweight object, so we need to load the
       
   507 			//fulfilled view contact to compare.
       
   508 			CViewContact* contact = iLplViewItemMgr.ItemAtL(firstLeftContact->Id(), iViewSessionId);				
       
   509 			if(contact == NULL)
       
   510 				{
       
   511 				//The already sorted contact is not in database!!!
       
   512 				User::Leave(KErrNotFound);
       
   513 				}
       
   514 				
       
   515 			CleanupStack::PushL(contact);    
       
   516 			firstLeftContact->CopyL(*contact);
       
   517 			CleanupStack::PopAndDestroy(contact);
       
   518 			}
       
   519 
       
   520         TInt diff = CompareContactsAndIdsL(*firstLeftContact, *firstRightContact);
       
   521         
       
   522 		if(diff > 0)
       
   523 			{
       
   524 			resultContacts->AppendL(firstRightContact);
       
   525 			++indexRight;
       
   526 			}
       
   527 		else
       
   528 			{		
       
   529 			resultContacts->AppendL(firstLeftContact);
       
   530 			++indexLeft;
       
   531 			if(diff == 0)
       
   532 			    {
       
   533 			    aRightContacts.Remove(indexRight);
       
   534 			    delete firstRightContact;
       
   535 			    }
       
   536 			}
       
   537 		}
       
   538 		
       
   539 	while(indexLeft < KCountLeft)
       
   540 		{
       
   541 		CViewContact* firstLeftContact = static_cast<CViewContact*>(aLeftContacts[indexLeft]);
       
   542 		resultContacts->AppendL(firstLeftContact);
       
   543 		++indexLeft;
       
   544 		}
       
   545 	
       
   546 	while(indexRight < aRightContacts.Count())
       
   547 		{
       
   548 		CViewContact* firstRightContact = static_cast<CViewContact*>(aRightContacts[indexRight]);
       
   549 		resultContacts->AppendL(firstRightContact);
       
   550 		++indexRight;
       
   551 		}
       
   552 		
       
   553     CleanupStack::Pop(resultContacts);
       
   554 	
       
   555 	return resultContacts;
       
   556     }
       
   557 
       
   558 
       
   559 /** 
       
   560 Insert a view contact object into existing sorted view contacts array(iViewContacts).
       
   561 
       
   562 @param  aNewContact   the new view contact object to be inserted.
       
   563 @param  aSortByIdOnly if the sorting method is sorting by id only
       
   564 @param  aStart        start searching position in existing view contacts array.
       
   565 @param  aIndex        the index of the view contact after insertion.
       
   566 @leave  KErrNoMemory or leave error from CContactViewBase::CompareContactsAndIdsL
       
   567 */
       
   568 TInt CViewContactManager::InsertViewContactL(const CViewContact* aNewContact, TBool aSortByIdOnly, TInt aStart)
       
   569 	{
       
   570 	TInt maxPos = iViewContacts->Count() - 1;
       
   571 	TInt minPos = aStart;
       
   572 	TInt position = 0;
       
   573     TInt error(KErrNone);
       
   574     
       
   575     //Using binary search to find the sorting position for the new contact.    
       
   576 	if (maxPos >= 0)
       
   577 		{
       
   578     	if (iSortPluginImpl)
       
   579     		{
       
   580     		// prepare View Sort plug-in
       
   581     		User::LeaveIfError(iSortPluginImpl->SortStart(CViewContactSortPlugin::ESortStartInsertOne, 1));
       
   582     		}
       
   583 
       
   584 		position = (minPos + maxPos + 1) / 2;
       
   585 
       
   586 		while (maxPos >= minPos)
       
   587 			{
       
   588 			TInt diff = 0;
       
   589             CViewContact* currContact = static_cast<CViewContact*>((*iViewContacts)[position]);
       
   590             
       
   591 			if (aSortByIdOnly)
       
   592 				{
       
   593 				diff = aNewContact->Id() - currContact->Id();
       
   594 				}
       
   595 			else
       
   596 				{
       
   597 				//The view contacts stored in sortable contact list(iViewContacts) have been changed to
       
   598 				//lightweight objects after sorting has done for memory saving purpose. 
       
   599 				if(currContact->IsLightweightObject())
       
   600 				    {
       
   601 				    //The current contact in iViewContact is lightweight object, so we need to load the
       
   602 				    //full content into the view contact before compare.
       
   603                     CViewContact* contact = iLplViewItemMgr.ItemAtL(currContact->Id(), iViewSessionId);				
       
   604 					if(contact == NULL)
       
   605 						{
       
   606 						//the sorted contact is not in the database!!!
       
   607 						User::Leave(KErrNotFound);
       
   608 						}
       
   609                     
       
   610                     CleanupStack::PushL(contact);    
       
   611     				diff = CompareContactsAndIdsL(*aNewContact, *contact);
       
   612     				CleanupStack::PopAndDestroy(contact);
       
   613 				    }
       
   614 				else
       
   615 				    {
       
   616 				    //It's fulfilled object already, use it to compare directly
       
   617     				diff = CompareContactsAndIdsL(*aNewContact, *currContact);
       
   618 				    }    
       
   619 				}
       
   620 
       
   621 
       
   622 			if (diff == 0)
       
   623 				{
       
   624 				// duplicate Id
       
   625 				error = KErrAlreadyExists;
       
   626 				break;
       
   627 				}
       
   628 				
       
   629 			if (diff < 0)
       
   630 				{
       
   631 				maxPos = position - 1;
       
   632 				}
       
   633 			else // diff > 0
       
   634 				{
       
   635 				minPos = position + 1;
       
   636 				}
       
   637 
       
   638 			position = (minPos + maxPos + 1) / 2;
       
   639 			} //while (maxPos >= minPos)
       
   640 			
       
   641     	if (iSortPluginImpl)
       
   642     		{
       
   643     		iSortPluginImpl->SortCompleted();
       
   644     		}
       
   645 		}
       
   646 
       
   647     User::LeaveIfError(error);
       
   648     
       
   649 	iViewContacts->InsertL(aNewContact, position);
       
   650 
       
   651 	return position;
       
   652 	}
       
   653 
       
   654 /** 
       
   655 Get a reference of a view contact by given index to existing view contacts array,
       
   656 if the the requested view object is in lightweight(only stored contact item id and
       
   657 contact type) the function will fill the whole content to the object.
       
   658 
       
   659 @param  aIndex  the index to the contact ids array.
       
   660 @leave  KErrNoMemory or KErrNotFound
       
   661 @return the reference to the requested contact view object(always be fulfiled), 
       
   662         the constrains of const is to keep to the same definition of the function 
       
   663 		in local view which calls down to this function.
       
   664 */
       
   665 const CViewContact& CViewContactManager::ContactAtL(TInt aIndex) const
       
   666     {
       
   667 	CViewContact& viewContact = ViewContactAtL(aIndex);
       
   668 	
       
   669 	if(viewContact.IsLightweightObject())
       
   670 		{
       
   671 		CViewContact* contact = iLplViewItemMgr.ItemAtL(viewContact.Id(), iViewSessionId);
       
   672 		if(contact == NULL)
       
   673 			{
       
   674 			User::Leave(KErrNotFound);
       
   675 			}
       
   676 		
       
   677 		CleanupStack::PushL(contact);    
       
   678 		viewContact.CopyL(*contact);
       
   679 		CleanupStack::PopAndDestroy(contact);
       
   680 		}
       
   681 	
       
   682     return viewContact;    	
       
   683     }
       
   684 
       
   685 
       
   686 /** 
       
   687 Get the view contact object by given index to existing view contacts array.
       
   688 
       
   689 @param  aIndex  the index to the contact ids array.
       
   690 @param  aLoadFullContent  if the view contact need to load into full content.
       
   691 @leave  KErrNotFound
       
   692 @return the pointer to the requested contact view object but caller doesn't own the object
       
   693         aLoadFullContent is ETrue if the object was a lightweight object but 
       
   694 		has been loaded into full content in the function.
       
   695 */
       
   696 CViewContact& CViewContactManager::ViewContactAtL(TInt aIndex) const
       
   697     {
       
   698 	const TInt unsortedCount = iUnsortedViewContacts->Count();
       
   699 	const TInt sortedCount = iViewContacts->Count();
       
   700 	
       
   701 	if(aIndex >= (unsortedCount + sortedCount))
       
   702 		{
       
   703 		//Out of Bounds.
       
   704 		User::Leave(KErrNotFound);
       
   705 		}
       
   706 
       
   707 	CViewContact* viewContact = NULL;
       
   708     
       
   709 	if(iViewPreferences & EUnSortedAtBeginning)
       
   710 		{
       
   711 		if(aIndex < unsortedCount)
       
   712 			{
       
   713 			//contact in unsorted array
       
   714 			viewContact = (*iUnsortedViewContacts)[aIndex];
       
   715 			}
       
   716 		else
       
   717 			{
       
   718 			//contact in sorted array
       
   719 			viewContact = (*iViewContacts)[aIndex - unsortedCount];
       
   720 			}
       
   721 		}
       
   722 	else if ((iViewPreferences & EUnSortedAtEnd) && (aIndex>=sortedCount))
       
   723 		{
       
   724 		viewContact = (*iUnsortedViewContacts)[aIndex - sortedCount];
       
   725 		}
       
   726 	else
       
   727 	    {
       
   728 	    viewContact = (*iViewContacts)[aIndex];
       
   729 	    }    
       
   730 	
       
   731 	ASSERT(viewContact != NULL);	
       
   732 	return *viewContact;
       
   733     }
       
   734 
       
   735 
       
   736 /** 
       
   737 Get the contact item id by given index.
       
   738 
       
   739 @param  aIndex  the index to the contact ids array.
       
   740 @leave  KErrNotFound
       
   741 @return the contact item id.
       
   742 */
       
   743 TContactItemId CViewContactManager::AtL(TInt aIndex) const
       
   744     {
       
   745     return ViewContactAtL(aIndex).Id();
       
   746     }
       
   747 
       
   748 
       
   749 /** 
       
   750 Get the number of contacts in the veiw.
       
   751 
       
   752 @return the number of contacts in the veiw.
       
   753 */
       
   754 TInt CViewContactManager::Count() const
       
   755     {
       
   756     ASSERT(iViewContacts);
       
   757     ASSERT(iUnsortedViewContacts);
       
   758     
       
   759 	return iViewContacts->Count() + iUnsortedViewContacts->Count();
       
   760     }
       
   761 
       
   762 
       
   763 /** 
       
   764 Find if the given contact item id is in the view.
       
   765 
       
   766 @param  aContactId The contact item id to be retrieved from database.
       
   767 @return Index of the requested view contact or KErrNotFound.
       
   768 */
       
   769 TInt CViewContactManager::FindL(TContactItemId aContactId) const
       
   770     {
       
   771 	TInt index = KErrNotFound;
       
   772 	CViewContact* contact = CViewContact::NewLC(aContactId);
       
   773 	const TInt unSortedCount = iUnsortedViewContacts->Count();
       
   774 	
       
   775 	// first look in unsorted contacts
       
   776 	if(unSortedCount > 0)
       
   777 		{
       
   778 		// contact may be in the unsorted array
       
   779 		index = iUnsortedViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
       
   780 
       
   781 		if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtEnd))
       
   782 			{
       
   783 			// account for sorted array size
       
   784 			index = index + iViewContacts->Count();
       
   785 			}
       
   786 		}
       
   787 
       
   788 	// if not found try sorted contacts
       
   789 	if (index == KErrNotFound)
       
   790 		{
       
   791 		//contact may be in the sorted array
       
   792 		index = iViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
       
   793 
       
   794 		if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtBeginning))
       
   795 			{
       
   796 			// account for unsorted array size
       
   797 			index = index + unSortedCount;
       
   798 			}
       
   799 		}
       
   800 
       
   801 	CleanupStack::PopAndDestroy(contact);
       
   802 	return index;
       
   803     }
       
   804     
       
   805 
       
   806 /** 
       
   807 Get all fields content of a view contact and separate them with charaters in aSeparators.
       
   808 
       
   809 @param  aIndex     the index of view contact.
       
   810 @param  aSeparator the charactors to split the fields content.
       
   811 @return Buffer of the formated fields content.
       
   812 */
       
   813 HBufC* CViewContactManager::AllFieldsLC(TInt aIndex, const TDesC& aSeparator) const
       
   814     {
       
   815 	CViewContact& viewContact = ViewContactAtL(aIndex); 
       
   816 	if(viewContact.IsLightweightObject())
       
   817 		{
       
   818 		CViewContact* contact = iLplViewItemMgr.ItemAtL(viewContact.Id(), iViewSessionId);
       
   819 		if(contact == NULL)
       
   820 			{
       
   821 			User::Leave(KErrNotFound);
       
   822 			}
       
   823 		
       
   824 		CleanupStack::PushL(contact);    
       
   825 		viewContact.CopyL(*contact);
       
   826 		CleanupStack::PopAndDestroy(contact);
       
   827 		}
       
   828 
       
   829 	HBufC* buf = FieldsWithSeparatorLC(viewContact,aSeparator);
       
   830 
       
   831     return buf;	
       
   832     }
       
   833 
       
   834 
       
   835 /** 
       
   836 Insert a contact into the view.
       
   837 
       
   838 @param  aContactId       the contact item id of the contact to be inserted.
       
   839 @param  aViewPreferences view preference which defines the view.
       
   840 @return Index of inserted contact or KErrNotFound.
       
   841 */
       
   842 TInt CViewContactManager::InsertL(const TContactItemId aContactId, TContactViewPreferences& aViewPreferences)
       
   843     {
       
   844 	if(!iIdleSorter->InsertViewPreferences(aViewPreferences))
       
   845 		{
       
   846 		return KErrNotFound;
       
   847 		}
       
   848 
       
   849 	TInt index = KErrNotFound;
       
   850 	
       
   851 	CViewContact* contact = iLplViewItemMgr.ItemAtL(aContactId, iViewSessionId);
       
   852 	if(contact == NULL)
       
   853 		{
       
   854 		//there is not a contact in database owning the contact id.
       
   855 		return KErrNotFound;
       
   856 		}
       
   857 		
       
   858 	CleanupStack::PushL(contact);
       
   859 	
       
   860 	if(ContactCorrectType(contact->ContactTypeUid(),aViewPreferences))
       
   861 		{
       
   862 		if(IsContactSortable(*contact, iViewPreferences))
       
   863 			{
       
   864 			//Contact has normal fields and can be added to the standard sorted array				
       
   865 			//Insert using Sort Plugin compare method, and get new index
       
   866 			index = InsertViewContactL(contact, EFalse, 0);
       
   867 			if (iViewPreferences & EUnSortedAtBeginning)
       
   868 				{
       
   869 				index += iUnsortedViewContacts->Count();
       
   870 				}
       
   871 			}
       
   872 		else if (iViewPreferences & (EUnSortedAtBeginning | EUnSortedAtEnd))
       
   873 			{
       
   874 			// unsortable contacts go at the end or beginning
       
   875 			// we want this to be stable (e.g. when ICC becomes unlocked)
       
   876 			iUnsortedViewContacts->AppendL(contact);
       
   877             index = iUnsortedViewContacts->Count() - 1; 
       
   878             			
       
   879 			// calc new index
       
   880 			if (iViewPreferences & EUnSortedAtEnd)
       
   881 				{
       
   882 				index += iViewContacts->Count();
       
   883 				}
       
   884 			}
       
   885 		}
       
   886 		
       
   887     CleanupStack::Pop(contact);
       
   888     
       
   889     if(index == KErrNotFound)
       
   890         {
       
   891         //The contact is not inserted, so delete it.
       
   892         delete contact;
       
   893         }
       
   894         
       
   895 	return index;
       
   896     }
       
   897 
       
   898     
       
   899 /** 
       
   900 Remove a contact from the view.
       
   901 
       
   902 @param  aContactId the contact item id of the contact to be removed.
       
   903 @return Index of removed contact or KErrNotFound.
       
   904 */
       
   905 TInt CViewContactManager::RemoveL(const TContactItemId aContactId)
       
   906     {
       
   907 	TInt index=KErrNotFound;
       
   908 	const TInt unSortedCount=iUnsortedViewContacts->Count();
       
   909 	
       
   910 	CViewContact* contact = CViewContact::NewLC(aContactId);
       
   911 	
       
   912 	// first look in unsorted contacts
       
   913 	if(unSortedCount > 0)
       
   914 		{
       
   915 		// contact may be in the unsorted array
       
   916 		index = iUnsortedViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
       
   917 
       
   918 		if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtEnd))
       
   919 			{
       
   920 			CViewContact* viewContact = (*iUnsortedViewContacts)[index];
       
   921 			iUnsortedViewContacts->Remove(index);
       
   922 			delete viewContact;
       
   923 			
       
   924 			// account for sorted array size
       
   925 			index = index + iViewContacts->Count();
       
   926 			}
       
   927 		}
       
   928 
       
   929 	// if not found try sorted contacts
       
   930 	if (index == KErrNotFound)
       
   931 		{
       
   932 		//contact may be in the sorted array
       
   933 		index = iViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
       
   934 
       
   935 		if (index != KErrNotFound)
       
   936 		    {
       
   937 			CViewContact* viewContact = (*iViewContacts)[index];
       
   938 			iViewContacts->Remove(index);
       
   939 			delete viewContact;
       
   940 			
       
   941 		    if (iViewPreferences & EUnSortedAtBeginning)
       
   942     			{
       
   943     			// account for unsorted array size
       
   944     			index = index + unSortedCount;
       
   945     			}
       
   946 		    }
       
   947 		}
       
   948     
       
   949     CleanupStack::PopAndDestroy(contact);    		
       
   950 	return index;
       
   951     }
       
   952 
       
   953 	
       
   954 /**
       
   955 This a delegating method to call IsICCSynchronised in iIdleSorter which is
       
   956 also observing ICC synchronization
       
   957 
       
   958 @return EFalse when iIdleSorter is waiting for ICC cards to be synchronized.
       
   959 */
       
   960 TBool CViewContactManager::IsICCSynchronised() const
       
   961 	{
       
   962 	return iIdleSorter->IsICCSynchronised();
       
   963 	}
       
   964 	
       
   965 	
       
   966 /**
       
   967 Cleanup stack call up to handle RPointerArray.
       
   968 
       
   969 @param aArray Pointer to an RPointerArray<CContactItemField> instance.
       
   970 
       
   971 @return None.
       
   972 */
       
   973 void CViewContactManager::ResetAndDestroyRPointerArrayPointer(TAny *aArray)
       
   974 	{
       
   975 	RPointerArray<CViewContact> *parray = static_cast< RPointerArray<CViewContact> * >(aArray);
       
   976 	parray->Close();			
       
   977 	delete parray;
       
   978 	}
       
   979 	
       
   980 /** 
       
   981 This function determines whether a contact should be added to the normal
       
   982 sorted array (iContacts) or put into the alternative iUnSortedContacts array. 
       
   983 Depending on the view preferences, these "unsorted" contacts are either 
       
   984 ignored (deleted), or added to the beginning or end of the iContacts 
       
   985 sorted list by At(), Find() etc... methods.
       
   986 
       
   987 @param aContact the view contact object to be checked
       
   988 @param aViewPreferences view preferences that determine if aContact is sortable.
       
   989 @return ETrue if the view contact object is sortable.
       
   990 */
       
   991 TBool CViewContactManager::IsContactSortable(const CViewContact& aContact, TContactViewPreferences& aViewPreferences) const
       
   992 	{
       
   993 	TBool sortableContact=EFalse;
       
   994 
       
   995 	// only test contact if it matters
       
   996 	if(aViewPreferences & (EIgnoreUnSorted | EUnSortedAtBeginning | EUnSortedAtEnd)) //Distinguish Unsorted
       
   997 		{
       
   998 		if (iSortPluginImpl)
       
   999 			{
       
  1000 			// use plugin
       
  1001 			sortableContact = iSortPluginImpl->ViewContactIsSortable(aContact);
       
  1002 			}
       
  1003 		else
       
  1004 			{
       
  1005 			// ask contact itself
       
  1006 			sortableContact = aContact.IsSortable();
       
  1007 			}
       
  1008 		}
       
  1009 	else
       
  1010 		{
       
  1011 		// Sortable
       
  1012 		sortableContact = ETrue;
       
  1013 		}
       
  1014 
       
  1015 	return sortableContact;
       
  1016 	}
       
  1017 
       
  1018 
       
  1019 /** 
       
  1020 Used for view sorting and insersion. In order to give a stable result 
       
  1021 if contact details match, it falls back to comparing contact IDs.
       
  1022 If a contact view sort plugin is loaded it uses its SortCompareViewContactsL() method.
       
  1023 
       
  1024 @param aFirst the first view contact to be compared.
       
  1025 @param aSecond the second view contact to be compared.
       
  1026 @return 0 if two view contacts' id are equal.
       
  1027 */
       
  1028 TInt CViewContactManager::CompareContactsAndIdsL(const CViewContact& aFirst, const CViewContact& aSecond) const
       
  1029 	{
       
  1030 	TInt result(0);
       
  1031 
       
  1032 	if (iSortPluginImpl)
       
  1033 		{
       
  1034 		// use View Sort plugin
       
  1035 		result = iSortPluginImpl->SortCompareViewContactsL(aFirst,aSecond);
       
  1036 		}
       
  1037 	else
       
  1038 		{
       
  1039 		// no View sort plugin loaded
       
  1040 		result = TextCompareFieldsL(aFirst, aSecond);
       
  1041 		}
       
  1042 
       
  1043 	if (result == 0)
       
  1044 		{
       
  1045 		result = aFirst.Id() - aSecond.Id();
       
  1046 		}
       
  1047 
       
  1048 	return result;
       
  1049 	}
       
  1050 
       
  1051 	
       
  1052 /** Collates two contact items' field contents.
       
  1053 
       
  1054 This is done by comparing each contact item on a field by field basis starting 
       
  1055 with the loosest collation level initially, and then progressing to tighter 
       
  1056 collation levels only if the items are considered the same at the looser level. 
       
  1057 This is required so that items differing by case only are sorted correctly. 
       
  1058 If a field isn't present, then the comparison is done using the first field 
       
  1059 that is present.
       
  1060 
       
  1061 Faster than static CompareFieldsL() method as it uses prefetched collation method.
       
  1062 
       
  1063 @param aFirst The first contact item.
       
  1064 @param aSecond The second contact item.
       
  1065 @return A positive value indicates that aFirst's field contents are greater 
       
  1066 than aSecond's (so that aFirst would occur after aSecond in a sort order). 
       
  1067 A negative value indicates that aFirst's field contents are less than aSecond's 
       
  1068 (so that aFirst would occur before aSecond in a sort order). Zero indicates 
       
  1069 that aFirst and aSecond have identical field contents. */
       
  1070 TInt CViewContactManager::TextCompareFieldsL(const CViewContact& aFirst,const CViewContact& aSecond) const
       
  1071 	{
       
  1072 	const TInt KDefaultCollationLevel=3;
       
  1073 	TInt comparison = 0; // result of comparison, Zero = fields are identical
       
  1074 	TInt collationLevel = 0;
       
  1075 
       
  1076 	do
       
  1077 		{
       
  1078 		comparison = CViewContactManager::CompareFieldsWithCollationLevel(aFirst, aSecond, collationLevel, &iCollationMethod);
       
  1079 		++collationLevel;
       
  1080 		}
       
  1081 		while (comparison == 0 && collationLevel <= KDefaultCollationLevel);
       
  1082 
       
  1083 	return comparison;
       
  1084 	}
       
  1085 	
       
  1086 
       
  1087 TInt CViewContactManager::CompareFieldsWithCollationLevel(const CViewContact& aFirst, const CViewContact& aSecond, TInt aCollationLevel, const TCollationMethod* aCollateMethod)
       
  1088 	{
       
  1089 	const TInt KLastField = aFirst.FieldCount() - 1;
       
  1090 
       
  1091 	TInt retval = 0; // result of comparison, Zero = fields are identical
       
  1092 	TInt firstField(-1);
       
  1093 	TInt secondField(-1);
       
  1094 	TPtrC first;
       
  1095 	TPtrC second;
       
  1096 
       
  1097 	for (TInt counter=0; !retval && (counter <= KLastField); ++counter)
       
  1098 		{
       
  1099 		// if the 1st populated field has a greater index than counter, 
       
  1100 		//	that means we'd get the same result from FindFirstPopulatedField.
       
  1101 		//	So, don't bother. Of course we always have to run it at least once.
       
  1102 
       
  1103 		if (firstField < counter)
       
  1104 			{
       
  1105 			first.Set(aFirst.FindFirstPopulatedField(counter, firstField));
       
  1106 			}
       
  1107 
       
  1108 		if (secondField < counter)
       
  1109 			{
       
  1110 			second.Set(aSecond.FindFirstPopulatedField(counter, secondField));
       
  1111 			}
       
  1112 
       
  1113 		// no fields in either item
       
  1114 		if ((firstField < 0) && (secondField < 0))
       
  1115 			{
       
  1116 			break;
       
  1117 			}
       
  1118 
       
  1119 		if (firstField < 0)
       
  1120 			{
       
  1121 			// first item sorts lower
       
  1122 			retval = -1;
       
  1123 			}
       
  1124 		else if (secondField < 0)
       
  1125 			{
       
  1126 			// second item sorts lower
       
  1127 			retval = 1;
       
  1128 			}
       
  1129 		else
       
  1130 			{
       
  1131 			// set counter to the first field populated by either contact
       
  1132 			while ((firstField > counter) && (secondField > counter))
       
  1133 				{
       
  1134 				++counter;
       
  1135 				}
       
  1136 			
       
  1137 			retval = first.CompareC(second, aCollationLevel, aCollateMethod);
       
  1138 			}
       
  1139 		}
       
  1140 
       
  1141 	return retval;
       
  1142 	}
       
  1143 	
       
  1144 
       
  1145 /** Allocates and returns a descriptor filled with the contents of all the fields 
       
  1146 in a contact item.
       
  1147 
       
  1148 The fields are separated by the specified separator.
       
  1149 
       
  1150 @param aContacts An array of contact items.
       
  1151 @param aIndex An index into the specified array.
       
  1152 @param aSeparator The text to use to separate the fields.
       
  1153 @return A pointer to a heap descriptor containing the contents of each of the 
       
  1154 contact item's fields. The field separator is appended to each field except 
       
  1155 the last one. The pointer is left on the cleanup stack. */
       
  1156 HBufC* CViewContactManager::FieldsWithSeparatorLC(const CViewContact& aViewContact,const TDesC& aSeparator) const
       
  1157 	{
       
  1158 	// Calculate the length of the buffer.
       
  1159 	TInt bufLength = 0;
       
  1160 	const TInt separatorLength = aSeparator.Length();
       
  1161 	const TInt numFields = aViewContact.FieldCount();
       
  1162 	
       
  1163 	for (TInt ii = 0; ii < numFields; ++ii)
       
  1164 		{
       
  1165 		bufLength += aViewContact.Field(ii).Length() + separatorLength;
       
  1166 		}
       
  1167 		
       
  1168 	HBufC* buf = HBufC::NewLC(bufLength);
       
  1169 	TPtr bufPtr(buf->Des());
       
  1170 
       
  1171 	// Fill the buffer.
       
  1172 	for (TInt j = 0; j < numFields; ++j)
       
  1173 		{
       
  1174 		bufPtr.Append(aViewContact.Field(j));
       
  1175 
       
  1176 		// Only put a separator in if this isn't the last field.
       
  1177 		if (j != numFields-1)
       
  1178 			{
       
  1179 			bufPtr.Append(aSeparator);
       
  1180 			}
       
  1181 		}
       
  1182 	return buf;
       
  1183 	}