predictivesearch/PcsAlgorithm/Algorithm1/src/CPcsAlgorithm1.cpp
branchRCL_3
changeset 63 f4a778e096c2
child 64 c1e8ba0c2b16
child 85 38bb213f60ba
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/predictivesearch/PcsAlgorithm/Algorithm1/src/CPcsAlgorithm1.cpp	Wed Sep 01 12:29:52 2010 +0100
@@ -0,0 +1,1788 @@
+/*
+* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Predictive Contact Search Algorithm 1 main class
+*
+*/
+
+
+// INCLUDES
+#include "CPcsAlgorithm1.h"
+#include "CPcsAlgorithm1Helper.h"
+#include "CPcsAlgorithm1MultiSearchHelper.h"
+#include "CPcsAlgorithm1Utils.h"
+#include "CPcsDebug.h"
+#include "CPcsCache.h"
+#include "CPcsKeyMap.h"
+#include "CPsData.h"
+#include "CWords.h"
+#include "CPsQuery.h"
+#include "CPsDataPluginInterface.h"
+#include "CPcsDefs.h"
+
+const TText KSpace = ' ';
+
+
+// ============================== MEMBER FUNCTIONS ============================
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::NewL
+// Two Phase Construction
+// ----------------------------------------------------------------------------
+CPcsAlgorithm1* CPcsAlgorithm1::NewL()
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::NewL") );
+    
+	CPcsAlgorithm1* self = new ( ELeave ) CPcsAlgorithm1();
+	CleanupStack::PushL( self );
+	self->ConstructL();
+	CleanupStack::Pop( self );
+
+    PRINT ( _L("End CPcsAlgorithm1::NewL") );
+    
+	return self;
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::CPcsAlgorithm1
+// Two Phase Construction
+// ----------------------------------------------------------------------------
+CPcsAlgorithm1::CPcsAlgorithm1()
+{		
+    PRINT ( _L("Enter CPcsAlgorithm1::CPcsAlgorithm1") );
+    PRINT ( _L("End CPcsAlgorithm1::CPcsAlgorithm1") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::ConstructL
+// Two Phase Construction
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::ConstructL()
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::ConstructL") );
+    
+    iCacheStatus = ECachingNotStarted;  // Starting status
+    iCacheError = KErrNone;             // No error
+        
+    iPluginLauncher = CIdle::NewL( CActive::EPriorityStandard );
+    
+    // Define cache status property used to inform clients about the caching status.
+    DefinePropertyL( EPsKeyCacheStatus );
+    
+    // Define cache error property used to inform client about the errors.
+    DefinePropertyL( EPsKeyCacheError );
+    
+    // Define properties for notifying about cache updates
+    DefinePropertyL( EPsKeyContactRemovedCounter );
+    DefinePropertyL( EPsKeyContactModifiedCounter );
+    DefinePropertyL( EPsKeyContactAddedCounter );
+
+    // Initialize key map and pti engine
+    TInt keyMapErr = KErrNone;
+    TRAP( keyMapErr, iKeyMap = CPcsKeyMap::NewL() );
+    if ( keyMapErr != KErrNone )
+    {
+        PRINT ( _L("**********************************************."));
+        PRINT1( _L("CPcsAlgorithm1::ConstructL() KeyMap construction error. The keymap crashed with error code %d."),keyMapErr );
+        PRINT ( _L("Please check the keypad/language for which keymap got crashed.") );
+        PRINT ( _L("**********************************************."));
+        User::Leave( keyMapErr ); // we can't go on without a key map; constructing cache needs it
+    }
+    
+    // Initialize helpers
+    iHelper = CPcsAlgorithm1Helper::NewL(this);
+    iMultiSearchHelper = CPcsAlgorithm1MultiSearchHelper::NewL(this);
+    
+    if(!iPluginLauncher->IsActive())
+        {
+        iPluginLauncher->Start(TCallBack(CPcsAlgorithm1::DoLaunchPluginsL, this));
+        }
+     
+    PRINT ( _L("End CPcsAlgorithm1::ConstructL") );
+} 
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::~CPcsAlgorithm1
+// Destructor
+// ----------------------------------------------------------------------------
+CPcsAlgorithm1::~CPcsAlgorithm1()
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::~CPcsAlgorithm1") );
+    
+    // Clear TLS
+    Dll::SetTls(NULL);
+    
+    // Cleanup cache
+    iPcsCache.ResetAndDestroy();
+    
+    // Cleanup adapters interface and key handling
+    delete iKeyMap;
+    delete iPsDataPluginInterface;
+    
+    // Cleanup helpers
+    delete iHelper;
+    delete iMultiSearchHelper;
+    
+    delete iPluginLauncher;
+
+    PRINT ( _L("End CPcsAlgorithm1::~CPcsAlgorithm1") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::DefinePropertyL
+// Define a P&S property with given key under the internal category 
+// UID of PCS. Leave if definition fails for any other reason than
+// key already existing. 
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::DefinePropertyL( TPcsInternalKeyCacheStatus aPsKey )
+    {
+    TInt err = RProperty::Define( KPcsInternalUidCacheStatus, 
+                                  aPsKey, 
+                                  RProperty::EInt );
+    if ( err != KErrAlreadyExists )
+        {
+        User::LeaveIfError(err);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::RemoveSpacesL
+// Remove leading and trailing spaces of search query
+// ----------------------------------------------------------------------------
+void  CPcsAlgorithm1::RemoveSpacesL(CPsQuery& aQuery)
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::RemoveSpacesL") );
+
+    //PRINTQUERY ( _L("CPcsAlgorithm1::RemoveSpacesL (BEFORE): "), aQuery );
+
+    // Remove all leading " "
+    while ( aQuery.Count() > 0 )
+    {
+        CPsQueryItem& item = aQuery.GetItemAtL(0); // First
+        if ( !item.Character().IsSpace() )
+        {
+            break;
+        }
+        aQuery.Remove(0);
+    }
+
+    // Remove all trailing " "
+    while ( aQuery.Count() > 0 )
+    {
+        CPsQueryItem& item = aQuery.GetItemAtL(aQuery.Count()-1); // Last
+        if ( !item.Character().IsSpace() )
+        {
+            break;
+        }
+        aQuery.Remove(aQuery.Count()-1);
+    }
+    
+    //PRINTQUERY ( _L("CPcsAlgorithm1::RemoveSpacesL (AFTER): "), aQuery );
+    
+    PRINT ( _L("End CPcsAlgorithm1::RemoveSpacesL") );
+}    
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::ReplaceZeroWithSpaceL
+// Replace all "0"s in a search query with " "s if those characters are on
+// the same key.
+// ----------------------------------------------------------------------------
+TBool  CPcsAlgorithm1::ReplaceZeroWithSpaceL(CPsQuery& aQuery)
+{   
+    PRINT ( _L("Enter CPcsAlgorithm1::ReplaceZeroWithSpaceL") );
+
+    //PRINTQUERY ( _L("CPcsAlgorithm1::ReplaceZeroWithSpaceL (BEFORE): "), aQuery );
+
+    TBool queryModified = EFalse;    
+
+    /* In phones like E52 and E55, where the "0" and the " " characters are on
+     * the same key, the "0"s have to be considered as possible separators.
+     *
+     * In phones like N97 and E72, where the "0" and the " " characters are on
+     * different keys, the "0"s must not be considered as possible separators.
+     */
+
+    // Skip initial "0"s, they are not replaced into spaces
+    TInt skipIndex = 0;
+    while ( (skipIndex < aQuery.Count()) && 
+            (aQuery.GetItemAtL(skipIndex).Character().GetNumericValue() == 0) )
+    {
+        skipIndex++;
+    }
+    
+    // Replace remaining "0"s into spaces in case they are entered with a keyboard
+    // that has "0" and " " on the same key.
+    for ( TInt index = skipIndex; index < aQuery.Count(); index++ )
+    {
+        CPsQueryItem& item = aQuery.GetItemAtL(index);
+
+        if ( iKeyMap->GetSpaceAndZeroOnSameKey( item.Mode() ) &&
+             item.Character().GetNumericValue() == 0 )
+        {
+            item.SetCharacter(KSpace);
+            queryModified = ETrue;
+        }
+    }
+    
+    //PRINTQUERY ( _L("CPcsAlgorithm1::ReplaceZeroWithSpaceL (AFTER): "), aQuery );
+
+    PRINT1 ( _L("CPcsAlgorithm1::ReplaceZeroWithSpaceL: Query modified (0=not, 1=yes): %d"), queryModified );
+
+    PRINT ( _L("End CPcsAlgorithm1::ReplaceZeroWithSpaceL") );
+
+    return  queryModified;
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::PerformSearchL
+// Search function for cache
+// ----------------------------------------------------------------------------
+void  CPcsAlgorithm1::PerformSearchL(const CPsSettings& aSettings,
+                                     CPsQuery& aQuery,
+                                     RPointerArray<CPsClientData>& aSearchResults,
+                                     RPointerArray<CPsPattern>& aSearchSeqs)
+{
+	PRINT ( _L("Enter CPcsAlgorithm1::PerformSearchL") );
+	
+	__LATENCY_MARK ( _L("CPcsAlgorithm1::PerformSearchL") );
+
+    // Check aSettings   
+    RPointerArray<TDesC> searchUris;
+    CleanupResetAndDestroyPushL( searchUris );
+    aSettings.SearchUrisL(searchUris);
+         
+    if ( searchUris.Count() <= 0)
+    {
+        PRINT ( _L("searchUris.Count() <= 0, Leave from CPcsAlgorithm1::PerformSearchL") );
+        User::Leave(KErrArgument); 
+    }
+    CleanupStack::PopAndDestroy( &searchUris ); // ResetAndDestroy
+  
+	// Local arrays to hold the search results 
+	RPointerArray<CPsData> tempSearchResults;
+	CleanupClosePushL( tempSearchResults );
+    RPointerArray<CPsData> tempSearchResultsIni;
+    CleanupClosePushL( tempSearchResultsIni );
+    RPointerArray<CPsData> tempSearchResultsMod;
+    CleanupClosePushL( tempSearchResultsMod );
+
+    // ----------------------- Perform the basic search -----------------------
+	/* Even before replacing zeroes with spaces the query can have multiple words
+	 * Some phones support infact double keyboards with possibility to type a space in qwerty mode
+	 * A space in qwerty mode can even be typed disabling temporarily PCS
+	 */
+    RemoveSpacesL(aQuery);
+    PRINTQUERY ( _L("CPcsAlgorithm1::PerformSearchL: 1st search query: "), aQuery );
+	DoSearchL(aSettings, aQuery, tempSearchResultsIni, aSearchSeqs );
+    // ------------------------------------------------------------------------
+
+    // ---- Perform new search after "0" replacement if query is not empty ----
+    /* Examples:
+     * - If the original search string is "Abc0" then we will search again with "Abc".
+     * - If the original search string is "00" then we will not search again.
+     * - If the original search string is "00Zxc" then we will search again with "Zxc".
+     */
+    TBool isQueryModified = ReplaceZeroWithSpaceL(aQuery);
+    RemoveSpacesL(aQuery);
+    if ( isQueryModified && (aQuery.Count() > 0) )
+    {
+        PRINTQUERY ( _L("CPcsAlgorithm1::PerformSearchL: 2nd search query: "), aQuery );
+        DoSearchL(aSettings, aQuery, tempSearchResultsMod, aSearchSeqs );
+    }
+    // ------------------------------------------------------------------------
+
+    // ------------------ Merge and sort the search results -------------------
+	if ( tempSearchResultsIni.Count() + tempSearchResultsMod.Count() > 0)
+	{
+		// Sort rule        
+		TLinearOrder<CPsData> rule( CPcsAlgorithm1Utils::CompareDataBySortOrderL );
+
+		// Avoid duplicates and add new results
+		TIdentityRelation<CPsData> identityRule(CPsData::CompareById);
+
+        for ( TInt i = 0; i < tempSearchResultsIni.Count(); i++ )
+        {
+            if ( tempSearchResults.Find( tempSearchResultsIni[i],identityRule ) == KErrNotFound )
+            {
+                if (aSettings.GetSortType() == EAlphabetical)
+                {
+                    tempSearchResults.InsertInOrderAllowRepeats(tempSearchResultsIni[i], rule);
+                }
+                else
+                {
+                    tempSearchResults.AppendL(tempSearchResultsIni[i]);
+                }
+            }
+        }
+
+        for ( TInt i = 0; i < tempSearchResultsMod.Count(); i++ )
+        {
+            if ( tempSearchResults.Find( tempSearchResultsMod[i],identityRule ) == KErrNotFound )
+            {
+                if (aSettings.GetSortType() == EAlphabetical)
+                {
+                    tempSearchResults.InsertInOrderAllowRepeats(tempSearchResultsMod[i], rule);
+                }
+                else
+                {
+                    tempSearchResults.AppendL(tempSearchResultsMod[i]);
+                }
+            }
+        }
+	}
+	// ------------------------------------------------------------------------
+
+    // ------------------ Write result objects to the stream ------------------
+    // Truncate the result set if required
+    TInt maxNumToDisplay = aSettings.MaxResults();
+    TInt resultSetCount = tempSearchResults.Count();
+    TInt numToDisplay = 0;
+    if ( maxNumToDisplay == -1 )
+        {
+        numToDisplay = resultSetCount;
+        }
+    else
+        {
+        numToDisplay = Min( maxNumToDisplay, resultSetCount );
+        }
+
+    // Copy desired number of results from tempSearchResults to the results stream
+    for (TInt i = 0; i < numToDisplay; i++)
+        {
+        aSearchResults.Append(WriteClientDataL(*(tempSearchResults[i])));
+        }
+	// ------------------------------------------------------------------------
+
+    // Cleanup local results array
+    CleanupStack::PopAndDestroy( &tempSearchResultsMod ); // Close, don't destroy
+    CleanupStack::PopAndDestroy( &tempSearchResultsIni ); // Close, don't destroy
+    CleanupStack::PopAndDestroy( &tempSearchResults );    // Close, don't destroy
+
+	__LATENCY_MARKEND ( _L("CPcsAlgorithm1::PerformSearchL") );
+
+	PRINT ( _L("End CPcsAlgorithm1::PerformSearchL") );			
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::SearchInputL
+// Search function for input string
+// ----------------------------------------------------------------------------
+void  CPcsAlgorithm1::SearchInputL(CPsQuery& aQuery,
+                                   TDesC& aData,
+                                   RPointerArray<TDesC>& aMatchSet,
+                                   RArray<TPsMatchLocation>& aMatchLocation )
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::SearchInputL") );
+
+    __LATENCY_MARK ( _L("CPcsAlgorithm1::SearchInputL") ); 
+
+    // ----------------------- Perform the basic search -----------------------
+    /* Even before replacing zeroes with spaces the query can have multiple words
+     * Some phones support infact double keyboards with possibility to type a space in qwerty mode
+     * A space in qwerty mode can even be typed disabling temporarily PCS
+     */
+    RemoveSpacesL(aQuery);
+    DoSearchInputL(aQuery, aData, aMatchSet, aMatchLocation);
+    PRINTQUERY    ( _L("CPcsAlgorithm1::SearchInputL: 1st search: "), aQuery );
+    PRINT1        ( _L("CPcsAlgorithm1::SearchInputL: 1st search: Search Data: %S"), &aData );
+    PRINTMATCHSET ( _L("CPcsAlgorithm1::SearchInputL: 1st search: "), aMatchSet );
+    PRINTMATCHLOC ( _L("CPcsAlgorithm1::SearchInputL: 1st search: "), aMatchLocation );
+    // ------------------------------------------------------------------------
+    
+    // ---- Perform new search after "0" replacement if query is not empty ----
+    /* Examples:
+     * - If the original search string is "Abc0" then we will search again with "Abc".
+     * - If the original search string is "00" then we will not search again.
+     */
+    TBool isQueryModified = ReplaceZeroWithSpaceL(aQuery);
+    RemoveSpacesL(aQuery);
+    if ( isQueryModified && (aQuery.Count() > 0) )
+    {
+        DoSearchInputL(aQuery, aData, aMatchSet, aMatchLocation);
+        PRINTQUERY    ( _L("CPcsAlgorithm1::SearchInputL: 2nd search: "), aQuery );
+        PRINT1        ( _L("CPcsAlgorithm1::SearchInputL: 2nd search: Search Data: %S"), &aData );
+        PRINTMATCHSET ( _L("CPcsAlgorithm1::SearchInputL: 2nd search: "), aMatchSet );
+        PRINTMATCHLOC ( _L("CPcsAlgorithm1::SearchInputL: 2nd search: "), aMatchLocation );
+    }
+    /* In cases like we have a contact "Nik0 Abc" and the search query is "Nik0" we do:
+     * - a 1st search with "Nik0" that matches "Nik0" (4 chars) of "Nik0 Abc",
+     * - a 2nd search with "Nik" that matches "Nik" (3 chars) of "Nik0 Abc".
+     * We have to remove from aMatchLocation the occurrence that has a shorter match length.
+     * We have to remove the match ("Nik" in this case) even from aMatchSet if there is not
+     * any other match for it than the one in "Nik0".
+     */
+
+    // --- Remove duplicate items from aMatchLocation ---
+    TInt i = 0;
+    TBool incrementFirstCursor;
+    while ( i < aMatchLocation.Count() )
+    {
+        incrementFirstCursor = ETrue;
+        TInt j = i+1;
+        while ( j < aMatchLocation.Count() )
+        {
+            if (aMatchLocation[j].index == aMatchLocation[i].index)
+            {
+                // Remove match location item with smallest length if 2 items have same index
+                if ( aMatchLocation[j].length <= aMatchLocation[i].length )
+                {
+                    aMatchLocation.Remove(j);
+                    continue; // Do not increment j
+                }
+                else
+                {
+                    aMatchLocation.Remove(i);
+                    incrementFirstCursor = EFalse; // Do not increment i
+                    break;
+                }
+            }
+            j++;
+        }
+        if ( incrementFirstCursor )
+        {
+            i++;
+        }
+    }
+
+    // --- Remove duplicate items from aMatchSet ---
+    HBufC* dataUpper = HBufC::NewLC(aData.Length());
+    dataUpper->Des().Copy(aData);
+    dataUpper->Des().UpperCase(); // Get uppercase, as aMatchSet is in upper case
+
+    TInt k = 0;
+    while ( k < aMatchSet.Count() )
+    {
+        TBool keepMatch = EFalse;
+        TInt startCursor = -1;
+
+        TInt offsetCursor;
+        while ( KErrNotFound != (offsetCursor = dataUpper->Mid(startCursor + 1).Find(*aMatchSet[k])) )
+        {
+            // startCursor = index of match item *aMatchSet[k] into search string aData
+            startCursor = offsetCursor + startCursor + 1;
+            for ( TInt i = 0; i < aMatchLocation.Count(); i++ )
+            {
+                // If match item was found in the location list, then keep it
+                if ( (aMatchLocation[i].index == startCursor) &&
+                     (aMatchLocation[i].length == aMatchSet[k]->Length()) )
+                {
+                    keepMatch = ETrue;
+                    break;
+                }
+            }
+        }
+
+        if ( keepMatch )
+        {
+            k++;
+        }
+        else
+        {
+            aMatchSet.Remove(k); // Do not increment k
+        }
+        // --- Remove items End ---
+
+    }
+    CleanupStack::PopAndDestroy( dataUpper );
+    // ------------------------------------------------------------------------
+
+    // Sort match set
+	iHelper->SortSearchSeqsL(aMatchSet);
+	
+    PRINTQUERY    ( _L("CPcsAlgorithm1::SearchInputL: Final: "), aQuery );
+    PRINT1        ( _L("CPcsAlgorithm1::SearchInputL: Final: Search Data: %S"), &aData );
+    PRINTMATCHSET ( _L("CPcsAlgorithm1::SearchInputL: Final: "), aMatchSet );
+    PRINTMATCHLOC ( _L("CPcsAlgorithm1::SearchInputL: Final: "), aMatchLocation );
+	
+	__LATENCY_MARKEND ( _L("CPcsAlgorithm1::SearchInputL") );
+
+    PRINT ( _L("End CPcsAlgorithm1::SearchInputL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::SearchMatchStringL
+// Search function for input string, result also as string
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::SearchMatchStringL( CPsQuery& aSearchQuery,
+                                         TDesC& aSearchData,
+                                         TDes& aMatch )
+    {
+    PRINT ( _L("Enter CPcsAlgorithm1::SearchMatchStringL") );
+
+    __LATENCY_MARK ( _L("CPcsAlgorithm1::SearchMatchStringL") ); 
+    
+    // ---------------------- Perform the initial search ----------------------
+    iMultiSearchHelper->LookupMatchL( aSearchQuery, aSearchData, aMatch );
+    PRINTQUERY ( _L("CPcsAlgorithm1::SearchMatchStringL: 1st search: "), aSearchQuery );
+    PRINT1     ( _L("CPcsAlgorithm1::SearchMatchStringL: 1st search: Search Data: %S"), &aSearchData );
+    PRINT1     ( _L("CPcsAlgorithm1::SearchMatchStringL: 1st search: Result: %S"), &aMatch );
+    // ------------------------------------------------------------------------
+    
+    // ---- Perform new search after "0" replacement if query is not empty ----
+    /* Examples:
+     * - If the original search string is "Abc0" then we will search again with "Abc".
+     * - If the original search string is "00" then we will not search again.
+     */
+    if ( aMatch.Length() <= 0 )
+    {
+        TBool isQueryModified = ReplaceZeroWithSpaceL(aSearchQuery);
+        if ( isQueryModified && (aSearchQuery.Count() > 0) )
+        {
+            iMultiSearchHelper->LookupMatchL( aSearchQuery, aSearchData, aMatch );
+            PRINTQUERY ( _L("CPcsAlgorithm1::SearchMatchStringL: 2nd search: "), aSearchQuery );
+            PRINT1     ( _L("CPcsAlgorithm1::SearchMatchStringL: 2nd search: Search Data: %S"), &aSearchData );
+            PRINT1     ( _L("CPcsAlgorithm1::SearchMatchStringL: 2nd search: Result: %S"), &aMatch );            
+        }
+    }
+    
+    __LATENCY_MARKEND ( _L("CPcsAlgorithm1::SearchMatchStringL") );
+
+    PRINT ( _L("End CPcsAlgorithm1::SearchMatchStringL") );
+    }
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::DoSearchL
+// Search function helper
+// ----------------------------------------------------------------------------
+void  CPcsAlgorithm1::DoSearchL( const CPsSettings& aSettings,
+                                 CPsQuery& aQuery,
+                                 RPointerArray<CPsData>& aSearchResults,
+                                 RPointerArray<CPsPattern>& aSearchSeqs )
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::DoSearchL") );
+
+    __LATENCY_MARK ( _L("CPcsAlgorithm1::DoSearchL") ); 
+    
+    // (0)------------------ Check if group search is required ---------------
+    RArray<TInt> contactsInGroup;
+    CleanupClosePushL( contactsInGroup );
+    RArray<TInt> groupIdArray;
+    CleanupClosePushL( groupIdArray );
+    
+    // Create a new settings instance
+    CPsSettings* tempSettings = aSettings.CloneL();
+    CleanupStack::PushL( tempSettings );
+    
+    TBool isGroupSearch = IsGroupSearchL(*tempSettings, groupIdArray);
+    
+    if ( isGroupSearch )
+    {    
+    	// Replace groups URI with contacts DB URI in new search settings
+    	ReplaceGroupsUriL(*tempSettings);
+        	
+        // List of contacts in this group	
+    	GetContactsInGroupL ( groupIdArray[0], contactsInGroup );
+    }
+    
+    // -----------------------------------------------------------------------
+        
+    // Extract query list. 
+    RPointerArray<CPsQuery> queryList = iMultiSearchHelper->MultiQueryL(aQuery);
+    CleanupResetAndDestroyPushL( queryList );
+    PRINTQUERYLIST ( _L("CPcsAlgorithm1::DoSearchL: "), queryList );
+
+    // (1)-------------------- No query return all contacts -------------------
+    if ( queryList.Count() == 0 )
+    {
+    	GetAllContentsL(*tempSettings, aSearchResults);
+    	
+    	if ( isGroupSearch ) 
+    	{
+    		FilterSearchResultsForGroupsL( contactsInGroup, aSearchResults );
+    	}
+    }
+    // ------------------------------------------------------------------------
+
+    // (2)-------------------- Perform a single query search ------------------
+    else if ( queryList.Count() == 1 ) // single query
+    {
+        PRINT ( _L("CPcsAlgorithm1::DoSearchL: Query received is Single. Performing a SingleSearch") );
+
+        iHelper->SearchSingleL(*tempSettings, *queryList[0], isGroupSearch,
+                               contactsInGroup, aSearchResults, aSearchSeqs);
+    }
+    // ------------------------------------------------------------------------
+
+    // (3)-------------------- Perform a multi query search -------------------
+    else // multiple query
+    {
+    	PRINT ( _L("CPcsAlgorithm1::DoSearchL: Query received is Multiple. Performing a MultiSearch") );
+
+		// Search results
+		iMultiSearchHelper->SearchMultiL(*tempSettings, queryList, isGroupSearch,
+		                                 contactsInGroup, aSearchResults, aSearchSeqs);
+    }
+    // -------------------------------------------------------------------------
+
+    // Cleanup
+    
+    CleanupStack::PopAndDestroy( &queryList ); // ResetAndDestroy
+    CleanupStack::PopAndDestroy( tempSettings );
+    CleanupStack::PopAndDestroy( &groupIdArray ); // Close
+    CleanupStack::PopAndDestroy( &contactsInGroup ); // Close
+
+	__LATENCY_MARKEND ( _L("CPcsAlgorithm1::DoSearchL") );
+
+	PRINT ( _L("End CPcsAlgorithm1::DoSearchL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::DoSearchInputL
+// Search function helper
+// ----------------------------------------------------------------------------
+void  CPcsAlgorithm1::DoSearchInputL( CPsQuery& aQuery,
+                                      const TDesC& aData,
+                                      RPointerArray<TDesC>& aMatchSet,
+                                      RArray<TPsMatchLocation>& aMatchLocation )
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::DoSearchInputL") );
+
+    TInt queryWords = iMultiSearchHelper->CountMultiQueryWordsL(aQuery);
+
+    // No query    
+    if ( queryWords == 0 )
+    {
+    	PRINT ( _L("CPcsAlgorithm1::DoSearchInputL: Query received is empty") );
+    	return;
+    }
+    
+    RPointerArray<CPsQuery> queryList = iMultiSearchHelper->MultiQueryL(aQuery);
+    CleanupResetAndDestroyPushL( queryList );
+    
+    PRINTQUERYLIST ( _L("CPcsAlgorithm1::DoSearchInputL: "), queryList );
+
+    if ( queryList.Count() == 1 ) // Single query
+    {
+        PRINT ( _L("CPcsAlgorithm1::DoSearchInputL: Query received is single. Performing a single search.") );
+        iHelper->SearchMatchSeqL(aQuery, 
+                             	 aData, 
+                              	 aMatchSet,
+                             	 aMatchLocation);
+    }
+    else // multiple query
+    {
+    	PRINT ( _L("CPcsAlgorithm1::DoSearchInputL: Query received is Multiple. Performing a MultiSearch") );
+    	iMultiSearchHelper->SearchMatchSeqMultiL(queryList, 
+		                                         aData,
+		                                         aMatchSet,
+		                                         aMatchLocation);
+    }
+
+	// Delete all the query elements
+	CleanupStack::PopAndDestroy( &queryList ); // ResetAndDestroy
+
+	PRINT ( _L("End CPcsAlgorithm1::DoSearchInputL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::AddData
+// Add a data element to the pool
+// ----------------------------------------------------------------------------    
+void CPcsAlgorithm1::AddData(TDesC& aDataStore, CPsData* aData)
+{
+    TInt arrayIndex = GetCacheIndex(aDataStore);
+    
+    if ( arrayIndex < 0 ) return;
+	
+	CPcsCache* cache = iPcsCache[arrayIndex];
+	
+	// Fill the data store index
+	TInt dataStoreIndex = FindStoreUri(aDataStore);
+    if ( dataStoreIndex >= 0 )
+    {
+    	aData->SetUriId(dataStoreIndex);
+    }
+    else 
+    {
+    	PRINT(_L("CPcsAlgorithm1::AddData: Unknown data store"));
+    	return;
+    }
+    
+	TRAPD(err, cache->AddToCacheL(*aData));
+	
+	if ( err != KErrNone )
+	{
+		SetCachingError(aDataStore, err);
+	}
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::RemoveData
+// Remove a data element from the pool
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::RemoveData(TDesC& aDataStore, TInt aItemId)
+{
+    TInt arrayIndex = GetCacheIndex(aDataStore);
+    
+    if ( arrayIndex < 0 ) return;
+	
+	CPcsCache* cache = iPcsCache[arrayIndex];
+	
+	TRAPD(err, cache->RemoveFromCacheL(aItemId));
+	
+	if ( err != KErrNone )
+	{
+		SetCachingError(aDataStore, err);
+	}
+}
+
+// ---------------------------------------------------------------------
+// CPcsAlgorithm1::RemoveAll
+// Remove all the contacts from a datastore
+// ---------------------------------------------------------------------
+void CPcsAlgorithm1::RemoveAll(TDesC& aDataStore)
+{
+	TInt dataStoreIndex = GetCacheIndex(aDataStore);
+	
+	if(dataStoreIndex < 0) return;
+	
+    CPcsCache* cache = iPcsCache[dataStoreIndex];
+    	
+	cache->RemoveAllFromCache();
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetCacheIndex
+// Return the cache index for a data store
+// ----------------------------------------------------------------------------
+TInt CPcsAlgorithm1::GetCacheIndex(const TDesC& aDataStore)
+{
+    for ( int i = 0; i < iPcsCache.Count(); i++ )
+    {
+    	CPcsCache* cache = iPcsCache[i];
+    	
+    	if ( cache->GetURI().CompareC(aDataStore) == 0 ) return i;
+    }
+    
+	return KErrNotFound;
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::AddDataStore
+// Adds a new store
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::AddDataStore(TDesC& aDataStore)
+{
+    // Check if the datastore cache already exists
+    TInt index = GetCacheIndex(aDataStore);
+    if ( index != KErrNotFound )
+    {
+    	// Already exists
+    	return;
+    }
+
+    // Create a new cache
+    CPcsCache* cache = NULL;
+	TRAPD(err, cache = CPcsCache::NewL(aDataStore, *iKeyMap, (TUint8) iPcsCache.Count()));
+	if ( err != KErrNone )
+	{
+		SetCachingError(aDataStore, err);
+		return;
+	}
+	
+    RArray<TInt> dataFields;
+    TRAP(err, iPsDataPluginInterface->GetSupportedDataFieldsL(cache->GetURI(), dataFields));
+	if ( err != KErrNone )
+	{
+		SetCachingError(aDataStore, err);
+		return;
+	}
+    cache->SetDataFields(dataFields); 
+    
+    // Check if sort order is persisted already
+    RArray<TInt> sortOrder;
+    TRAP(err, ReadSortOrderFromCenRepL(cache->GetURI(), sortOrder));
+    if ( err != KErrNone )
+	{
+		SetCachingError(aDataStore, err);
+		return;
+	}
+    
+    if ( sortOrder.Count() == 0 )
+    {
+    	cache->SetSortOrder(dataFields); // Initial sort order	
+    }
+    else 
+    {
+    	cache->SetSortOrder(sortOrder); // Persisted sort order
+    }
+    
+    sortOrder.Close();
+    dataFields.Close();
+    
+    iPcsCache.Append(cache);
+    
+	TRAP(err, iPsDataPluginInterface->RequestForDataL(aDataStore));
+	if ( err != KErrNone )
+	{
+		PRINT ( _L("CPcsAlgorithm1::AddDataStore() Set Caching Error ") );
+		SetCachingError(aDataStore, err);
+		UpdateCachingStatus(aDataStore,ECachingCompleteWithErrors);
+		return;
+	}
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::RemoveDataStore
+// Removes an existing data store
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::RemoveDataStore(TDesC& aDataStore)
+{
+    for ( int i = 0; i < iPcsCache.Count(); i++ )
+    {
+    	CPcsCache* cache = iPcsCache[i];
+    	
+    	if ( cache->GetURI().CompareC(aDataStore) == 0 )
+    	{
+    		delete iPcsCache[i];
+    		iPcsCache.Remove(i);
+    	}
+    }
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::IsLanguageSupportedL
+// Returns ETrue if this language is supported
+// ----------------------------------------------------------------------------
+TBool CPcsAlgorithm1::IsLanguageSupportedL(TUint32 aLang)
+{
+    return iKeyMap->IsLanguageSupported(aLang);
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetUriForIdL
+// Get the URI string for this internal id
+// ----------------------------------------------------------------------------
+const TDesC& CPcsAlgorithm1::GetUriForIdL(TUint8 aUriId)
+{
+    TBool found = EFalse;
+    TInt i = 0;
+    
+    for ( i = 0; i < iPcsCache.Count(); i++ )
+    {
+    	if ( iPcsCache[i]->GetUriId() == aUriId ) 
+    	{
+    	   found = ETrue;
+    	   break;
+    	}
+    }	
+	
+	if ( ! found )
+	{
+		User::Leave(KErrNotFound);
+	}
+	
+	return iPcsCache[i]->GetURI(); 
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::FindStoreUri
+// Checks if this store exists
+// ----------------------------------------------------------------------------
+TInt CPcsAlgorithm1::FindStoreUri ( const TDesC& aDataStore )
+{
+    for ( TInt i = 0; i < iPcsCache.Count(); i++ )
+    {
+        if ( aDataStore.CompareC(iPcsCache[i]->GetURI()) == 0 ) 
+    	{
+    	   return i;
+    	}
+    }
+
+    return KErrNotFound;
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::UpdateCachingStatus
+// Update caching status
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::UpdateCachingStatus(TDesC& aDataStore, TInt aStatus)
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::UpdateCachingStatus") );
+    PRINT2 ( _L("CPcsAlgorithm1::UpdateCachingStatus: Request received for URI=%S with status=%d"),
+             &aDataStore, aStatus );
+
+    // Handle data store update events
+    if ( aStatus == ECacheUpdateContactRemoved ||
+         aStatus == ECacheUpdateContactModified ||
+         aStatus == ECacheUpdateContactAdded )
+        {
+        HandleCacheUpdated( static_cast<TCachingStatus>(aStatus) );
+        return;
+        }
+
+    // If not a cache update event, then this event is related to the initial
+    // cache construction.
+
+    // Check if any error occurred and update the cache error
+    if ( aStatus < 0 )
+    {
+        SetCachingError(aDataStore, aStatus);
+    }
+    else
+    {
+        TInt index = FindStoreUri(aDataStore);
+        iPcsCache[index]->UpdateCacheStatus(aStatus);
+    }
+
+    TCachingStatus status = ECachingNotStarted;
+    TUint countNotStarted = 0;
+    TUint countInProgress = 0;
+    TUint countCompleted = 0;
+    TUint countCompletedWithErrors = 0;
+    TInt cacheCount = iPcsCache.Count();
+    for ( TInt i = 0; i < cacheCount; i++ )
+    {
+        PRINT3 ( _L("CPcsAlgorithm1::UpdateCachingStatus: URI[%d]=%S, cache status=%d"),
+                 i, &iPcsCache[i]->GetURI(), iPcsCache[i]->GetCacheStatus() );
+
+        switch ( iPcsCache[i]->GetCacheStatus() )
+        {
+            case ECachingNotStarted:         
+            {
+                countNotStarted++;          
+                break;
+            }
+            case ECachingInProgress:         
+            {
+                countInProgress++;         
+                break;
+            }
+            case ECachingComplete:           
+            {
+                countCompleted++;           
+                break;
+            }
+            case ECachingCompleteWithErrors: 
+            {
+                countCompletedWithErrors++; 
+                break;
+            }
+            default:                         
+            { 
+                // Default completed state
+                countCompleted++;           
+                break;
+            }
+        }
+    }
+
+    // Calculate cumulative status according to single caches statuses
+    if ( countCompleted > 0 && ( countCompleted + countNotStarted ) == cacheCount )
+    {
+        // If at least one caching is finished
+        // set status to ECachingComplete or ECachingCompleteWithErrors
+        // according to iCacheError
+        status = ( iCacheError == KErrNone ) ? ECachingComplete : ECachingCompleteWithErrors;
+    }
+    else if ( countInProgress > 0 )
+    {
+        // Else if at least one caching is in progress,
+        // set status to ECachingInProgress
+        status = ECachingInProgress;
+    }
+    else if ( countCompletedWithErrors > 0 )
+    {
+        // Else if at least one caching is completed with errors, 
+        //set status to ECachingCompleteWithErrors
+        status = ECachingCompleteWithErrors;
+    }
+    else
+    {
+        // countNotStarted == cacheCount
+        // status is set to default ECachingNotStarted
+    }
+
+    PRINT1 ( _L("CPcsAlgorithm1::UpdateCachingStatus: Cumulative caching status is %d"),
+             status );
+
+    // Check if status changed
+    if ( status != iCacheStatus )
+    {
+        PRINT2 ( _L("CPcsAlgorithm1::UpdateCachingStatus: Cumulative caching changed: %d -> %d"),
+                 iCacheStatus, status );
+
+        iCacheStatus = status;
+        RProperty::Set(KPcsInternalUidCacheStatus, EPsKeyCacheStatus, iCacheStatus );
+    }
+
+    PRINT( _L("End CPcsAlgorithm1::UpdateCachingStatus") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::SetCachingError
+// Updates cachinge error
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::SetCachingError(const TDesC& aDataStore, TInt aError)
+{
+    PRINT2 ( _L("CPcsAlgorithm1::SetCachingError: URI=%S, ERROR=%d"), &aDataStore, aError );
+
+    iCacheError = aError;
+    RProperty::Set( KPcsInternalUidCacheStatus, EPsKeyCacheError, iCacheError );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetAllContentsL
+// Returns all the contents of a store
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::GetAllContentsL ( const CPsSettings& aSettings,
+                                       RPointerArray<CPsData>& aResults )
+{
+	__LATENCY_MARK ( _L("CPcsAlgorithm1::GetAllContentsL") );
+    
+    PRINT ( _L("Enter CPcsAlgorithm1::GetAllContentsL") );
+    
+    // To hold array of results from different data stores
+    typedef RPointerArray<CPsData> CPSDATA_R_PTR_ARRAY;
+    RPointerArray<CPSDATA_R_PTR_ARRAY> searchResultsArr;
+    CleanupResetAndDestroyPushL( searchResultsArr );
+    // TODO: Here's still a potential memory leak if a leave happens. The child
+    // arrays of searchResultsArr are not Reset in that case. The CPsData objects
+    // may leak as well. Handling this safely is somewhat complicated because some of
+    // the CPsData objects may already be transferred to ownership of aResults array
+    // at the time the leave happens, and those items must not be deleted.
+    
+    // Get the data stores
+    RPointerArray<TDesC> dataStores;
+    CleanupResetAndDestroyPushL( dataStores );
+    aSettings.SearchUrisL(dataStores);
+
+    // Get all contacts for each data store
+    for ( TInt dsIndex = 0; 
+          dsIndex < dataStores.Count(); 
+          dsIndex++ )
+    {	        	
+        RPointerArray<CPsData> *temp = new (ELeave) RPointerArray<CPsData>();
+        searchResultsArr.Append(temp);
+        
+        TInt arrayIndex = GetCacheIndex(*(dataStores[dsIndex]));
+        
+		if ( arrayIndex < 0 ) continue;
+		
+		CPcsCache* cache = GetCache(arrayIndex);
+        	      
+        cache->GetAllContentsL(*(searchResultsArr[dsIndex]));
+    }
+    
+    CleanupStack::PopAndDestroy( &dataStores ); // ResetAndDestroy
+  
+    // Merge the results from different data stores
+    CPcsAlgorithm1Utils::FormCompleteSearchResultsL(searchResultsArr,
+    												aResults);
+  
+    // Cleanup the local arrays
+    for(TInt i = 0; i < searchResultsArr.Count(); i++)
+    {
+    	searchResultsArr[i]->Reset();
+    }
+    CleanupStack::PopAndDestroy( &searchResultsArr ); // ResetAndDestroy
+
+    PRINT1 ( _L("Number of results = %d"), aResults.Count() );
+   
+    PRINT ( _L("End CPcsAlgorithm1::GetAllContentsL") );
+    
+    __LATENCY_MARKEND ( _L("CPcsAlgorithm1::GetAllContentsL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::IsGroupSearchL
+// Checks if a group search is required
+// ----------------------------------------------------------------------------
+TBool CPcsAlgorithm1::IsGroupSearchL ( CPsSettings& aSettings,
+                                       RArray<TInt>& aGroupIdArray )
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::IsGroupSearchL") );   
+    
+    // Get the groupIds in the seach settings
+    aSettings.GetGroupIdsL(aGroupIdArray);
+     
+    // Get the current URIs defined in settings    
+    RPointerArray<TDesC> searchUris;
+    CleanupResetAndDestroyPushL( searchUris );
+    aSettings.SearchUrisL(searchUris);
+    
+    if ( aGroupIdArray.Count() && (searchUris.Count() > aGroupIdArray.Count() ) )
+    {
+    	// There is an error, either there are more than one groups
+    	// or the settings contain a combination of group/non-group Uris
+    	aGroupIdArray.Close();
+    	User::Leave(KErrArgument); 
+    }
+    
+    CleanupStack::PopAndDestroy( &searchUris ); // ResetAndDestroy
+        
+    PRINT ( _L("End CPcsAlgorithm1::IsGroupSearchL") );    
+    
+    if ( aGroupIdArray.Count() == 1 )
+        return ETrue;
+    
+    return EFalse;
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::ReplaceGroupsUriL
+// Replace groups uri to contacts uri
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::ReplaceGroupsUriL( CPsSettings& aSettings )
+{
+	RPointerArray<TDesC> uri;
+	CleanupResetAndDestroyPushL( uri );
+    
+    // Set contacts db uri
+	HBufC* cntdb = KVPbkDefaultCntDbURI().AllocLC();
+	uri.AppendL(cntdb);
+	CleanupStack::Pop( cntdb ); // ownership transferred
+	aSettings.SetSearchUrisL(uri);
+	
+	// Cleanup
+	CleanupStack::PopAndDestroy( &uri ); // ResetAndDestroy
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::FilterSearchResultsForGroupsL
+// Filters the results that belong to a group
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::FilterSearchResultsForGroupsL(RArray<TInt>& contactsInGroup, 
+                                                   RPointerArray<CPsData>& aSearchResults)
+{
+	PRINT ( _L("Enter CPcsAlgorithm1::FilterSearchResultsForGroupsL") );   
+   
+	// for each search result
+	// Note: aSearchResults.Count() is to be checked everytime,
+	//       since the elements are being removed dynamically.
+	for ( TInt j = 0 ; j < aSearchResults.Count(); j++ )
+	{
+		TBool includeResult = EFalse;
+
+		if ( contactsInGroup.Find(aSearchResults[j]->Id()) != KErrNotFound )
+		{
+			includeResult = ETrue;
+		}
+
+		if ( includeResult == EFalse )
+		{
+			aSearchResults.Remove(j);
+			j--; // j is decremented, since that object is removed
+		}
+	}
+	        
+	PRINT ( _L("End CPcsAlgorithm1::FilterSearchResultsForGroupsL") );    
+}
+
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetContactsInGroupL
+// Recover contacts that belong to a group
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::GetContactsInGroupL( TInt aGroupId, 
+                                          RArray<TInt>& aGroupContactIds )
+{	    
+    // Clear results array
+    aGroupContactIds.Reset();
+    
+    // Cache Index for group database
+    TInt cacheIndex = GetCacheIndex(KVPbkDefaultGrpDbURI);
+    
+    // Get the groups contact ids 
+	if ( cacheIndex != -1 )
+	{
+		RPointerArray<CPsData> groups;
+		CleanupClosePushL( groups );
+		
+		// Get all groups
+		iPcsCache[cacheIndex]->GetAllContentsL(groups);
+	
+	    // Get all contacts in group
+		for ( TInt i = 0; i < groups.Count(); i++ )
+		{
+			if ( groups[i]->Id() == aGroupId )
+			{
+				groups[i]->IntDataExt(aGroupContactIds); // All contacts in group
+				break;
+			}		
+		}
+		
+		CleanupStack::PopAndDestroy( &groups ); // Close
+	}
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetDataOrderL
+// 
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::GetDataOrderL ( TDesC& aURI,
+                                     RArray<TInt>& aDataOrder )
+{
+    PRINT ( _L("End CPcsAlgorithm1::GetDataOrderL") );
+
+    TInt arrayIndex = KErrNotFound;
+    
+    if ( CPcsAlgorithm1Utils::IsGroupUri(aURI) )
+    {
+        // If search in a group uri, use contacts db
+        arrayIndex = GetCacheIndex(KVPbkDefaultCntDbURI);
+    }
+    else 
+    {
+        arrayIndex = GetCacheIndex(aURI);
+    }
+    
+    if ( arrayIndex < 0 ) return;
+        
+    CPcsCache* cache = iPcsCache[arrayIndex];
+    
+    aDataOrder.Reset();
+    
+    // Get the data fields for this cache
+    cache->GetDataFields(aDataOrder);
+    
+    PRINT ( _L("End CPcsAlgorithm1::GetDataOrderL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetSortOrderL
+// 
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::GetSortOrderL ( TDesC& aURI,
+                                     RArray<TInt>& aDataOrder )
+{
+    PRINT ( _L("End CPcsAlgorithm1::GetSortOrderL") );
+
+    TInt arrayIndex = KErrNotFound;
+    
+    if ( CPcsAlgorithm1Utils::IsGroupUri(aURI) )
+    {
+        // If search in a group uri, use contacts db
+        arrayIndex = GetCacheIndex(KVPbkDefaultCntDbURI);
+    }
+    else 
+    {
+        arrayIndex = GetCacheIndex(aURI);
+    }
+    
+    if ( arrayIndex < 0 ) return;
+    
+    CPcsCache* cache = iPcsCache[arrayIndex];
+    
+    aDataOrder.Reset();
+    
+    // Get the data fields for this cache
+    cache->GetSortOrder(aDataOrder);
+    
+    PRINT ( _L("End CPcsAlgorithm1::GetSortOrderL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::ChangeSortOrderL
+// 
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::ChangeSortOrderL ( TDesC& aURI,
+                                        RArray<TInt>& aSortOrder )
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::ChangeSortOrderL") );
+    
+    PRINT ( _L("CPcsAlgorithm1::ChangeSortOrderL. Sort order change received.") );
+    PRINT1 ( _L("CPcsAlgorithm1::ChangeSortOrderL. URI = %S"), &aURI );
+    
+    // If URI is search in a group URI return
+    if ( CPcsAlgorithm1Utils::IsGroupUri(aURI) )
+    {
+       PRINT ( _L("CPcsAlgorithm1::ChangeSortOrderL. Sort order change not supported.") );
+       return;	
+    }
+    
+    // Check if a cache exists
+    TInt arrayIndex = GetCacheIndex(aURI);
+    if ( arrayIndex < 0 )
+    {
+        PRINT ( _L("CPcsAlgorithm1::ChangeSortOrderL. Cache for URI doesn't exist.") );
+    	return;
+    }
+    
+    // Cache instance for this URI
+    CPcsCache* cache = iPcsCache[arrayIndex];
+        
+    // Check if received sort order is same as before
+    RArray<TInt> mySortOrder;
+    cache->GetSortOrder(mySortOrder);
+    
+    if ( aSortOrder.Count() == mySortOrder.Count() )
+    {
+        TBool same = ETrue;
+        for ( TInt i = 0; i < mySortOrder.Count(); i++ )
+        {
+            if ( mySortOrder[i] != aSortOrder[i] )
+            {
+                same = EFalse;
+                break;
+            }
+        }
+         
+        if ( same )
+        {
+            PRINT ( _L("CPcsAlgorithm1::ChangeSortOrderL. Same sort order received. Ignoring ...") );
+            PRINT ( _L("End CPcsAlgorithm1::ChangeSortOrderL.") );
+            mySortOrder.Reset();
+            return;
+        }
+    }
+    
+    mySortOrder.Reset();
+    
+    PRINT ( _L("CPcsAlgorithm1::ChangeSortOrderL. New sort order received. Refreshing ...") );
+	// Set the new sort order on the cache
+	cache->SetSortOrder(aSortOrder);
+	
+    // Persist the changes in sort order
+    WriteSortOrderToCenRepL(aURI, aSortOrder);
+	
+	// Resort data
+	TInt err = KErrNone; 
+	TRAP(err, cache->ResortdataInPoolsL());
+	if ( err != KErrNone )
+	{
+		PRINT ( _L("CPcsAlgorithm1::ChangeSortOrderL() Set Caching Error ") ); 
+		SetCachingError(aURI, err);
+		UpdateCachingStatus(aURI,ECachingCompleteWithErrors);
+		return;
+	}
+	
+	PRINT ( _L("End CPcsAlgorithm1::ChangeSortOrderL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetAdaptiveGridL
+// 
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::GetAdaptiveGridL( const MDesCArray& aURIs,
+                                       const TBool aCompanyName,
+                                       TDes& aAdaptiveGrid )
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::GetAdaptiveGridL") );
+
+    PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridL. Request of Adaptive Grid for %d URI(s)"),
+             aURIs.MdcaCount() );
+
+    if ( iCacheStatus != ECachingComplete )
+        {
+        PRINT ( _L("CPcsAlgorithm1::GetAdaptiveGridL: PCS Caching is not ready, returning empty Adaptive Grid") );
+        aAdaptiveGrid.Zero();
+        }
+    else
+        {
+        GetAdaptiveGridFromCacheL( aURIs, aCompanyName, aAdaptiveGrid );
+        }
+
+    PRINT ( _L("End CPcsAlgorithm1::GetAdaptiveGridL") );
+}
+
+// ----------------------------------------------------------------------------
+// CPcsAlgorithm1::GetAdaptiveGridFromCacheL
+// 
+// ----------------------------------------------------------------------------
+void CPcsAlgorithm1::GetAdaptiveGridFromCacheL( const MDesCArray& aURIs,
+                                                const TBool aCompanyName,
+                                                TDes& aAdaptiveGrid )
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::GetAdaptiveGridFromCacheL") );
+
+    PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Request of Adaptive Grid for %d URI(s)"),
+             aURIs.MdcaCount() );
+
+    RArray<TInt> cacheIds;
+    CleanupClosePushL( cacheIds );
+    
+    // Create the list of the cache indexes that will form the Adaptive Grid
+    for ( TInt i=0; i < aURIs.MdcaCount(); i++ )
+    {
+        TPtrC16 uri = aURIs.MdcaPoint(i);
+
+        // If URI is a group URI skip it
+        if ( CPcsAlgorithm1Utils::IsGroupUri( uri ) )
+        {
+            PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Adaptive Grid for URI \"%S\" is not supported. Skipping"),
+                     &uri );
+            continue;
+        }
+
+        TInt cacheIndex = GetCacheIndex( uri );
+        if ( cacheIndex == KErrNotFound )
+        {
+            PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Cache for URI \"%S\" doesn't exist"),
+                     &uri );
+            continue;
+        }
+
+        PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Cache for URI \"%S\" will be used to form the Adaptive Grid"),
+                 &uri );
+
+        cacheIds.AppendL( cacheIndex );
+    }
+
+    PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Number of caches that will be used to form the grid is %d"),
+             cacheIds.Count( ) );
+
+    // Create the Adaptive Grid from the cache(s)
+    if ( cacheIds.Count() == 1 ) // No merge if we have only one cache
+        {
+        // Cache instance for this URI
+        CPcsCache* cache = iPcsCache[cacheIds[0]];
+
+        // Get the Adaptive Grid    
+        cache->GetAdaptiveGridL( aCompanyName, aAdaptiveGrid );
+
+        PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Adaptive Grid: \"%S\" (No merge was needed)"),
+                 &aAdaptiveGrid );
+        }
+    else if ( cacheIds.Count() > 1 ) // Merge if we have more than one cache
+        {
+        RArray<TChar> gridAll;
+        CleanupClosePushL( gridAll );
+        TUint gridSize = 0;
+
+        HBufC16* gridOne = HBufC::NewLC(KPsAdaptiveGridStringMaxLen);
+        TPtr16 gridOnePtr( gridOne->Des( ));
+
+        TLinearOrder<TChar> rule( CPcsAlgorithm1Utils::CompareByCharacter );
+
+        // Loop through the caches that form the Adaptive Grid
+        for ( TUint i=0;
+              gridSize < KPsAdaptiveGridStringMaxLen && i < cacheIds.Count();
+              i++ )
+            {
+            // Cache instance for this URI
+            CPcsCache* cache = iPcsCache[cacheIds[i]];
+
+            // Get the Adaptive Grid    
+            gridOnePtr.Zero();
+            cache->GetAdaptiveGridL( aCompanyName, gridOnePtr );
+
+            PRINT2 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Adaptive Grid for cache \"%S\" is \"%S\""),
+                     &cache->GetURI(), &gridOnePtr );
+
+            // Loop through the characters of the Adaptive Grid for the cache
+            for ( TUint j=0;
+                  gridSize < KPsAdaptiveGridStringMaxLen && j < gridOnePtr.Length();
+                  j++ )
+                {
+                if ( i == 0 ) // Grid from one cache is already ordered with no repetitions
+                    {
+                    gridAll.Append( gridOnePtr[j]);
+                    }
+                else // Grids from more caches can have repeated characters
+                    {
+                    gridAll.InsertInOrder( gridOnePtr[j], rule ); // No repeats !!! 
+                    }
+                gridSize++;
+                }
+            }
+
+        // Form the Adaptive Grid to be returned
+        aAdaptiveGrid.Zero();
+        for ( TUint i=0; i < gridAll.Count(); i++ )
+            {
+            aAdaptiveGrid.Append( gridAll[i] );
+            }
+
+        PRINT1 ( _L("CPcsAlgorithm1::GetAdaptiveGridFromCacheL. Adaptive Grid: \"%S\" (Merge was done)"),
+                 &aAdaptiveGrid );
+        
+        CleanupStack::PopAndDestroy( gridOne );
+        CleanupStack::PopAndDestroy( &gridAll ); // Close
+        }
+
+    CleanupStack::PopAndDestroy( &cacheIds ); // Close
+    
+    PRINT ( _L("End CPcsAlgorithm1::GetAdaptiveGridFromCacheL") );
+}
+
+// ---------------------------------------------------------------------------------
+// Read the persisted sort order from the central repository
+// Persisted sort order is of form URI Field1 Field2 Field3 .. FieldN (space delimited)
+// ---------------------------------------------------------------------------------
+void CPcsAlgorithm1::ReadSortOrderFromCenRepL(const TDesC& aURI, 
+                                              RArray<TInt>& aSortOrder)
+{
+    PRINT ( _L("Enter CPcsAlgorithm1::ReadSortOrderFromCenRepL.") );
+
+    aSortOrder.Reset();
+    
+	CRepository *repository = CRepository::NewL( KCRUidPSSortOrder );
+
+    // Read the sort order from cenrep
+    TBuf<KCRMaxLen> str;
+    
+    for ( TInt i(KCenrepFieldsStartKey); 
+          i < KCenrepFieldsStartKey + KCenrepNumberOfFieldsCount; 
+          i++ )
+    {
+	    TInt err = repository->Get( i, str );
+	    
+	    if ( KErrNone != err )
+	    {
+		    break;
+	    }
+	    
+	    if (str != KNullDesC)
+	    {		    
+		    TLex lex(str);
+		    
+		    // Extract the URI
+		    TPtrC token = lex.NextToken();
+		    
+		    if ( aURI.Compare(token) == 0 )
+		    {
+		        // Extract the sort order
+		        token.Set(lex.NextToken());
+		        
+				while ( token.Length() != 0 )
+				{	
+				    TLex lex1(token);
+				    
+				    TInt intVal;				    
+				    TInt err = lex1.Val(intVal);
+				    
+					if ( KErrNone == err )
+					{
+						aSortOrder.Append(intVal);
+					}
+				    
+					// Next token
+					token.Set(lex.NextToken());
+				}	
+				
+				break;
+		    }
+	    }
+	
+    }
+    
+    delete repository;
+    
+    PRINT ( _L("End CPcsAlgorithm1::ReadSortOrderFromCenRepL.") );
+}
+
+// ---------------------------------------------------------------------------------
+// Write the sort order into the central repository
+// Persisted sort order is of form URI Field1 Field2 Field3 .. FieldN (space delimited)
+// ---------------------------------------------------------------------------------
+void CPcsAlgorithm1::WriteSortOrderToCenRepL(const TDesC& aURI, 
+                                             RArray<TInt>& aSortOrder)
+{   
+    PRINT ( _L("Enter CPcsAlgorithm1::WriteSortOrderToCenRepL.") );
+
+    CRepository *repository = CRepository::NewLC( KCRUidPSSortOrder );
+
+	// Check if there an entry for this URI in cenrep
+	TBuf<KCRMaxLen> str;
+	TInt keyIndex = -1;
+
+	for ( TInt i(KCenrepFieldsStartKey); 
+	      i < KCenrepFieldsStartKey + KCenrepNumberOfFieldsCount; 
+	      i++ )
+	{
+		TInt err = repository->Get( i, str );
+
+		if ( KErrNone != err )
+		{
+		    PRINT ( _L("CPcsAlgorithm1::WriteSortOrderToCenRepL. cenrep error.") );
+		    return;
+		}
+
+		if (str != KNullDesC)
+		{		    
+		    TLex lex(str);
+		    
+		    // Extract the URI
+		    TPtrC token = lex.NextToken();
+		    
+		    if ( aURI.Compare(token) == 0 )
+		    {
+		         keyIndex = i; // i has the key index for this URI
+		         break;
+		    }
+		}
+	}
+	
+	// No entry for this URI in cenrep
+	// Find the next free location in cenrep
+	if ( keyIndex == -1 )
+	{
+		// Find the next free key index
+		for ( TInt i(KCenrepFieldsStartKey); 
+		      i < KCenrepFieldsStartKey + KCenrepNumberOfFieldsCount; 
+		      i++ )
+	    {
+		    TInt err = repository->Get( i, str );
+
+			if ( KErrNone != err )
+			{
+			    PRINT ( _L("CPcsAlgorithm1::WriteSortOrderToCenRepL. cenrep error.") );
+			    return;
+			}
+
+			if (str == KNullDesC)
+			{
+			    keyIndex = i; // i has the next free location
+			    break;
+			}
+	    }
+	}
+	
+	if ( keyIndex == -1 )
+	{
+		PRINT ( _L("CPcsAlgorithm1::WriteSortOrderToCenRepL. Persist limit violated.") );
+		return;
+	}
+
+    // Persist the sort order
+	HBufC* str1 = HBufC::NewLC(KCRMaxLen);
+	TPtr ptr(str1->Des());
+
+	// Append the URI
+	ptr.Append(aURI);
+	ptr.Append(KSpace);
+
+	// Append the sort order fields
+	for ( TInt j = 0; j < aSortOrder.Count(); j++ )
+	{
+		ptr.AppendNum(aSortOrder[j]);
+	    ptr.Append(KSpace);
+	}
+
+	// Write to persistent store
+	TInt err = repository->Set(keyIndex, ptr); 
+
+	User::LeaveIfError(err);
+
+	CleanupStack::PopAndDestroy( str1 );
+  
+    CleanupStack::PopAndDestroy( repository );
+     
+    PRINT ( _L("End CPcsAlgorithm1::WriteSortOrderToCenRepL.") );
+}
+
+// ---------------------------------------------------------------------------------
+// WriteClientDataL.
+// Write the content required by client
+// ---------------------------------------------------------------------------------
+CPsClientData* CPcsAlgorithm1::WriteClientDataL( CPsData& aPsData )
+{
+    CPsClientData* clientData = CPsClientData::NewL();
+	CleanupStack::PushL(clientData);
+	
+	// set Id
+	clientData->SetId(aPsData.Id());
+	
+	// set Uri
+	clientData->SetUriL(GetUriForIdL(aPsData.UriId()));
+	
+	// set pointer to the each data element
+	for(TInt i = 0; i < aPsData.DataElementCount(); i++)
+	{
+		clientData->SetDataL(i, *(aPsData.Data(i)));
+	}
+	
+	// set data extension
+	clientData->SetDataExtensionL(aPsData.DataExtension());
+	    
+	// Set the Field match
+	clientData->SetFieldMatch(aPsData.DataMatch())  ;
+	CleanupStack::Pop(clientData);
+	
+	return clientData;
+}
+
+// ---------------------------------------------------------------------------------
+// HandleCacheUpdated.
+// ---------------------------------------------------------------------------------
+void CPcsAlgorithm1::HandleCacheUpdated( TCachingStatus aStatus )
+    {
+    TInt psKey( KErrNotFound );
+    
+    switch ( aStatus )
+        {
+        case ECacheUpdateContactRemoved:
+            psKey = EPsKeyContactRemovedCounter;
+            break;
+            
+        case ECacheUpdateContactModified:
+            psKey = EPsKeyContactModifiedCounter;
+            break;
+            
+        case ECacheUpdateContactAdded:
+            psKey = EPsKeyContactAddedCounter;
+            break;
+            
+        default:
+            break;
+        }
+
+    if ( psKey != KErrNotFound )
+        {
+        // Increment the related counter in P&S by one to signal the clients about
+        // the cache update.
+        TInt counter( KErrNotFound );
+        TInt err = RProperty::Get( KPcsInternalUidCacheStatus, psKey, counter );
+        if ( !err )
+            {
+            counter++;
+            RProperty::Set( KPcsInternalUidCacheStatus, psKey, counter );
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------------
+// DoLaunchPluginsL.
+// launch plugins by idle
+// ---------------------------------------------------------------------------------
+TInt CPcsAlgorithm1::DoLaunchPluginsL(TAny* aPtr)
+    {
+    CPcsAlgorithm1* ptr = (CPcsAlgorithm1*) aPtr;
+    ptr->DoLaunchPluginsL();
+    return EFalse;
+    }
+
+// ---------------------------------------------------------------------------------
+// DoLaunchPluginsL.
+// lauch plugins
+// ---------------------------------------------------------------------------------
+void CPcsAlgorithm1::DoLaunchPluginsL()
+    {
+    // Initialize available data adapters
+    iPsDataPluginInterface = CPsDataPluginInterface::NewL(this, this);    
+    iPsDataPluginInterface->InstantiateAllPlugInsL();
+    
+    // Store the cache list in TLS
+    // Required to support sort order changes in a memory efficient way
+    // This avoids storing sort order information in the CPsData element
+    // and storing it in CPcsCache. Refer CPcsAlgorithm1Utils::CompareDataBySortOrderL
+    // to see how this is being used.
+    User::LeaveIfError( Dll::SetTls(&iPcsCache) );
+    
+    // Initialize cache
+    RPointerArray<TDesC> dataStores;
+    CleanupClosePushL( dataStores );
+    
+    iPsDataPluginInterface->GetAllSupportedDataStoresL(dataStores);
+        
+    for ( TInt dIndex = 0; dIndex < dataStores.Count(); dIndex++ )
+        {
+        AddDataStore(*(dataStores[dIndex]));
+        }
+    
+    CleanupStack::PopAndDestroy( &dataStores ); // Close
+    }
+
+// End of file
+