predictivesearch/PcsAlgorithm/Algorithm2/src/CPcsAlgorithm2.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 14 Sep 2010 20:54:53 +0300
branchRCL_3
changeset 21 9da50d567e3c
parent 20 f4a778e096c2
permissions -rw-r--r--
Revision: 201033 Kit: 201035

/*
* 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 <vpbkeng.rsg>
#include <PtiEngine.h>
#include <centralrepository.h>
#include <AknFepInternalCRKeys.h>

#include "CPcsAlgorithm2.h"
#include "CPcsAlgorithm2Helper.h"
#include "CPcsAlgorithm2MultiSearchHelper.h"
#include "CPcsAlgorithm2Utils.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"
#include "FindUtilChineseECE.h"


// ============================== MEMBER FUNCTIONS ============================

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::NewL
// Two Phase Construction
// ----------------------------------------------------------------------------
CPcsAlgorithm2* CPcsAlgorithm2::NewL()
    {
    PRINT ( _L("Enter CPcsAlgorithm2::NewL") );

    CPcsAlgorithm2* self = new (ELeave) CPcsAlgorithm2();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);

    PRINT ( _L("End CPcsAlgorithm2::NewL") );

    return self;
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::CPcsAlgorithm2
// Two Phase Construction
// ----------------------------------------------------------------------------
CPcsAlgorithm2::CPcsAlgorithm2()
    {
    PRINT ( _L("Enter CPcsAlgorithm2::CPcsAlgorithm2") );
    PRINT ( _L("End CPcsAlgorithm2::CPcsAlgorithm2") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::ConstructL
// Two Phase Construction
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::ConstructL()
    {
    PRINT ( _L("Enter CPcsAlgorithm2::ConstructL") );

    iCacheStatus = ECachingNotStarted; // Starting status
    iCacheError = KErrNone; // No error
    iCacheCount = 0; // No data

    iFindUtilECE = CFindUtilChineseECE::NewL(this);
    
    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( this ) );
    if (keyMapErr != KErrNone)
        {
        PRINT ( _L("**********************************************."));
        PRINT1 ( _L("CPcsAlgorithm2::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 = CPcsAlgorithm2Helper::NewL(this);
    iMultiSearchHelper = CPcsAlgorithm2MultiSearchHelper::NewL(this);

    if(!iPluginLauncher->IsActive())
        {
        iPluginLauncher->Start(TCallBack(CPcsAlgorithm2::DoLaunchPluginsL, this));
        }

    PRINT ( _L("End CPcsAlgorithm2::ConstructL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::~CPcsAlgorithm2
// Destructor
// ----------------------------------------------------------------------------
CPcsAlgorithm2::~CPcsAlgorithm2()
    {
    PRINT ( _L("Enter CPcsAlgorithm2::~CPcsAlgorithm2") );

    // 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 iFindUtilECE;
    
    delete iPluginLauncher;

    PRINT ( _L("End CPcsAlgorithm2::~CPcsAlgorithm2") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::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 CPcsAlgorithm2::DefinePropertyL( TPcsInternalKeyCacheStatus aPsKey )
    {
    TInt err = RProperty::Define( KPcsInternalUidCacheStatus, 
                                  aPsKey, 
                                  RProperty::EInt );
    if ( err != KErrAlreadyExists )
        {
        User::LeaveIfError(err);
        }
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::RemoveSpacesL
// Remove leading and trailing spaces of search query
// ----------------------------------------------------------------------------
void  CPcsAlgorithm2::RemoveSpacesL(CPsQuery& aQuery)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::RemoveSpacesL") );

    // 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);
        }
    
    PRINT ( _L("End CPcsAlgorithm2::RemoveSpacesL") );
    }    

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::ReplaceZeroWithSpaceL
// Replace first occurance of '0' in a sequence of '0's in ITU-T with space
// ----------------------------------------------------------------------------
TBool CPcsAlgorithm2::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.
       const TInt queryCount = aQuery.Count();
       for ( TInt index = skipIndex; index < queryCount; 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;
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::PerformSearchL
// Search function for cache
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::PerformSearchL(const CPsSettings& aSettings, 
                                    CPsQuery& aQuery, 
                                    RPointerArray<CPsClientData>& aSearchResults,
                                    RPointerArray<CPsPattern>& aSearchSeqs)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::PerformSearchL") );

    //__LATENCY_MARK ( _L("CPcsAlgorithm2::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> tempSearchResults1;
    CleanupClosePushL( tempSearchResults1 );

    // -------------------- Perform the basic search --------------------------

    RemoveSpacesL(aQuery);
    PRINTQUERY ( _L("CPcsAlgorithm2::PerformSearchL: 1st search query: "), aQuery );
    DoSearchL(aSettings, aQuery, tempSearchResults, 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.
     */
    TBool queryModified = ReplaceZeroWithSpaceL(aQuery);
    RemoveSpacesL(aQuery);
    // Perform query again if query got modified. 
    if (queryModified)
        {
        PRINTQUERY ( _L("CPcsAlgorithm2::PerformSearchL: 2nd search query: "), aQuery );
        DoSearchL(aSettings, aQuery, tempSearchResults1, aSearchSeqs);


        // Sort rule        
        TLinearOrder<CPsData> rule(CPcsAlgorithm2Utils::CompareDataBySortOrderL);

        // Avoid duplicates and add new results
        TIdentityRelation<CPsData> identityRule(CPsData::CompareById);
        const TInt tempSearchResults1Count = tempSearchResults1.Count();
        if (aSettings.GetSortType() != EAlphabetical)
            {
            TInt insertPos = 0;
            for (TInt i = 0; i < tempSearchResults1Count; i++)
                {
                if (tempSearchResults.Find(tempSearchResults1[i],
                                           identityRule) == KErrNotFound)
                    {
                    tempSearchResults.Insert(tempSearchResults1[i], insertPos);
                    insertPos++;
                    }
                }
            }
        else
            {
            for (TInt i = 0; i < tempSearchResults1Count; i++)
                {
                if (tempSearchResults.Find(tempSearchResults1[i],
                                           identityRule) == KErrNotFound)
                    {
                    tempSearchResults.InsertInOrderAllowRepeats(tempSearchResults1[i], 
                                                                rule);
                    }
                }
            }
        }
    // ------------------------------------------------------------------------

    // ---------------------- 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( &tempSearchResults1 ); // Close, don't destroy
    CleanupStack::PopAndDestroy( &tempSearchResults );  // Close, don't destroy

    // __LATENCY_MARKEND ( _L("CPcsAlgorithm2::PerformSearchL") );

    PRINT ( _L("End CPcsAlgorithm2::PerformSearchL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::SearchInputL
// Search function for input string
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::SearchInputL(CPsQuery& aQuery, 
                                  TDesC& aData,
                                  RPointerArray<TDesC>& aMatchSet,
                                  RArray<TPsMatchLocation>& aMatchLocation)
    {
    // __LATENCY_MARK ( _L("CPcsAlgorithm2::SearchInputL: ") );
    PRINT ( _L("Enter CPcsAlgorithm2::SearchInputL") );

    // Print input query for debug
    PRINTQUERY ( _L("CPcsAlgorithm2::SearchInputL: Search query: "), aQuery );

    // Print received search data
    PRINT1 ( _L("Search data received = %S"), &aData);


    // -------------------- Perform the basic search --------------------------

    RemoveSpacesL(aQuery);
    DoSearchInputL(aQuery, aData, aMatchSet, 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 queryModified = ReplaceZeroWithSpaceL(aQuery);
    RemoveSpacesL(aQuery);
    // If query got modified and the search query still contains something
    // perform a multi search again
    if (queryModified && aQuery.Count() > 0 && aMatchSet.Count() == 0  && aMatchLocation.Count() == 0 )
        {
        DoSearchInputL(aQuery, aData, aMatchSet, aMatchLocation);
        }
    // ------------------------------------------------------------------------

    // --- Remove overlapping items from aMatchLocation ---
    TInt i = 0;
    TBool incrementFirstCursor;
    while ( i < aMatchLocation.Count() )
        {
        incrementFirstCursor = ETrue;
        TInt j = i+1;
        while ( j < aMatchLocation.Count() )
            {
            if ( CPcsAlgorithm2Utils::MatchesOverlap( aMatchLocation[j], aMatchLocation[i] ) )
                {
                // 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 from aMatchSet items which no longer have corresponding item in aMatchLocation ---
    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;
            const TInt matchLocationCount = aMatchLocation.Count();
            for ( TInt i = 0; i < matchLocationCount; 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
            }
        }
    CleanupStack::PopAndDestroy( dataUpper );
    // --- Remove items End ---

    // Sort match set
    iHelper->SortSearchSeqsL(aMatchSet);

    PRINT ( _L("End CPcsAlgorithm2::SearchInputL") );
    //__LATENCY_MARKEND ( _L("CPcsAlgorithm2::SearchInputL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::SearchMatchStringL
// Search function for input string, result also as string
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::SearchMatchStringL( CPsQuery& /*aSearchQuery*/,
                                         TDesC& /*aSearchData*/,
                                         TDes& /*aMatch*/ )
    {
    PRINT ( _L("Enter CPcsAlgorithm2::SearchMatchStringL") );

    //__LATENCY_MARK ( _L("CPcsAlgorithm2::SearchMatchStringL") ); 
    
    // TODO: Implementation missing
    
    //__LATENCY_MARKEND ( _L("CPcsAlgorithm2::SearchMatchStringL") );

    PRINT ( _L("End CPcsAlgorithm2::SearchMatchStringL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::DoSearchL
// Search function helper
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::DoSearchL( const CPsSettings& aSettings, 
                                CPsQuery& aQuery, 
                                RPointerArray<CPsData>& aSearchResults,
                                RPointerArray<CPsPattern>& aSearchSeqs )
    {
    PRINT ( _L("Enter CPcsAlgorithm2::DoSearchL") );

    //__LATENCY_MARK ( _L("CPcsAlgorithm2::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 );

    // (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)
        {
        CPsQuery* query = queryList[0];

        // Search results
        iHelper->SearchSingleL(*tempSettings, *query, isGroupSearch,
                               contactsInGroup, aSearchResults, aSearchSeqs);
        }
    // ------------------------------------------------------------------------

    // (3)-------------------- Perform a multi query search -------------------
    else // multiple query
        {
        PRINT ( _L("Query received is in multiple. Performing a multi search.") );

        // 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("CPcsAlgorithm2::DoSearchL") );

    PRINT ( _L("End CPcsAlgorithm2::DoSearchL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::DoSearchInputL
// Search function helper
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::DoSearchInputL(CPsQuery& aQuery, 
                                    const TDesC& aData,
                                    RPointerArray<TDesC>& aMatchSet,
                                    RArray<TPsMatchLocation>& aMatchLocation)
    {

    //__LATENCY_MARK ( _L("CPcsAlgorithm2::SearchInputL: ") );
    PRINT ( _L("Enter CPcsAlgorithm2::DoSearchInputL") );

    // Check if any seperator is there in the query
    RPointerArray<CPsQuery> queryList = iMultiSearchHelper->MultiQueryL(aQuery);
    CleanupResetAndDestroyPushL( queryList );

    // No query    
    if (queryList.Count() == 0)
        {
        PRINT ( _L("Query received is empty") );
        CleanupStack::PopAndDestroy( &queryList ); // ResetAndDestroy
        return;
        }

    // Single query
    if (queryList.Count() == 1)
        {
        iHelper->SearchMatchSeqL(aQuery, aData, aMatchSet, aMatchLocation);
        }

    if (queryList.Count() > 1) // multiple query
        {
        PRINT ( _L("Query received is in multiple. Performing a multi search.") );

        // Search results
        iMultiSearchHelper->SearchMatchSeqMultiL(queryList, 
                                                 aData, 
                                                 aMatchSet,
                                                 aMatchLocation);
        }

    // Delete all the query elements
    CleanupStack::PopAndDestroy( &queryList ); // ResetAndDestroy
    PRINT ( _L("End CPcsAlgorithm2::DoSearchInputL") );
    //__LATENCY_MARKEND ( _L("CPcsAlgorithm2::SearchInputL") );

    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::AddData
// Add a data element to the pool
// ----------------------------------------------------------------------------    
void CPcsAlgorithm2::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("CPcsAlgorithm2::AddDataL Unknown data store"));
        return;
        }
    TRAPD(err, cache->AddToCacheL(*aData));

    if (err != KErrNone)
        {
        SetCachingError(aDataStore, err);
        }
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::RemoveData
// Remove a data element from the pool
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::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);
        }
    }

// ---------------------------------------------------------------------
// CPcsAlgorithm2::RemoveAll
// Remove all the contacts from a datastore
// ---------------------------------------------------------------------
void CPcsAlgorithm2::RemoveAll(TDesC& aDataStore)
    {
    TInt dataStoreIndex = GetCacheIndex(aDataStore);

    if (dataStoreIndex < 0)
        return;

    CPcsCache* cache = iPcsCache[dataStoreIndex];
    cache->RemoveAllFromCache();
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::GetCacheIndex
// Return the cache index for a data store
// ----------------------------------------------------------------------------
TInt CPcsAlgorithm2::GetCacheIndex(const TDesC& aDataStore)
    {
    const TInt pcsCacheCount = iPcsCache.Count();
    for (int i = 0; i <pcsCacheCount; i++)
        {
        CPcsCache* cache = iPcsCache[i];

        if (cache->GetURI().CompareC(aDataStore) == 0)
            return i;
        }

    return -1;
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::AddDataStore
// Adds a new store
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::AddDataStore(TDesC& aDataStore)
    {
    // Check if the datastore cache already exists
    TInt index = GetCacheIndex(aDataStore);
    if (index != -1)
        {
        // Already exists
        return;
        }

    // Create a new cache    
    CPcsCache* cache = NULL;
    TRAPD(err, cache = CPcsCache::NewL( this, aDataStore, *iKeyMap, iCacheCount));
    if (err != KErrNone)
        {
        SetCachingError(aDataStore, err);
        return;
        }

    // Increment the cachecount
    iCacheCount++;

    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)
        {
        SetCachingError(aDataStore, err);
        UpdateCachingStatus(aDataStore, ECachingCompleteWithErrors);
        return;
        }
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::RemoveDataStore
// Removes an existing data store
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::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);
            iCacheCount--; 
            }
        }
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::IsLanguageSupportedL
// Returns ETrue if this language is supported
// ----------------------------------------------------------------------------
TBool CPcsAlgorithm2::IsLanguageSupportedL(TUint32 aLang)
    {
    return iKeyMap->IsLanguageSupportedL(aLang);
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::GetUriForIdL
// Get the URI string for this internal id
// ----------------------------------------------------------------------------
const TDesC& CPcsAlgorithm2::GetUriForIdL(TUint8 aUriId)
    {
    TBool found = EFalse;
    TInt i = 0;
    const TInt pcsCacheCount = iPcsCache.Count();
    for (i = 0; i < pcsCacheCount; i++)
        {
        if (iPcsCache[i]->GetUriId() == aUriId)
            {
            found = ETrue;
            break;
            }
        }

    if (!found)
        {
        User::Leave(KErrNotFound);
        }

    return *(iPcsCache[i]->GetUri());
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::FindStoreUri
// Checks if this store exists
// ----------------------------------------------------------------------------
TInt CPcsAlgorithm2::FindStoreUri(const TDesC& aDataStore)
    {
    const TInt pcsCacheCount = iPcsCache.Count();
    for ( TInt i = 0; i < pcsCacheCount; i++ )
        {
        if ( aDataStore.CompareC(*(iPcsCache[i]->GetUri())) == 0 )
            {
            return i;
            }
        }

    return KErrNotFound;
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::UpdateCachingStatus
// Update caching status
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::UpdateCachingStatus(TDesC& aDataStore, TInt aStatus)
{
    PRINT ( _L("Enter CPcsAlgorithm2::UpdateCachingStatus") );
    PRINT2 ( _L("CPcsAlgorithm2::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("CPcsAlgorithm2::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("CPcsAlgorithm2::UpdateCachingStatus: Cumulative caching status is %d"),
             status );

    // Check if status changed
    if ( status != iCacheStatus )
    {
        PRINT2 ( _L("CPcsAlgorithm2::UpdateCachingStatus: Cumulative caching changed: %d -> %d"),
                 iCacheStatus, status );

        iCacheStatus = status;
        RProperty::Set(KPcsInternalUidCacheStatus, EPsKeyCacheStatus, iCacheStatus );
    }

    PRINT( _L("End CPcsAlgorithm2::UpdateCachingStatus") );
}


// ----------------------------------------------------------------------------
// CPcsAlgorithm2::SetCachingError
// Updates cachinge error
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::SetCachingError(const TDesC& aDataStore, TInt aError)
    {
    PRINT2 ( _L("SetCachingError::URI %S ERROR %d"), &aDataStore, aError );

    iCacheError = aError;
    RProperty::Set( KPcsInternalUidCacheStatus, EPsKeyCacheError, iCacheError );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::GetAllContentsL
// Returns all the contents of a store
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::GetAllContentsL(const CPsSettings& aSettings,
                                     RPointerArray<CPsData>& aResults)
    {
    //__LATENCY_MARK ( _L("CPcsAlgorithm2::GetAllContentsL") );

    PRINT ( _L("Enter CPcsAlgorithm2::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
    const TInt dataStoresCount = dataStores.Count(); 
    for (TInt dsIndex = 0; dsIndex < dataStoresCount; 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
    CPcsAlgorithm2Utils::FormCompleteSearchResultsL(searchResultsArr, aResults);

    // Cleanup the local arrays
    const TInt seaerchResultsArrCount = searchResultsArr.Count(); 
    for (TInt i = 0; i < seaerchResultsArrCount; i++)
        {
        searchResultsArr[i]->Reset();
        }

    CleanupStack::PopAndDestroy( &searchResultsArr ); // ResetAndDestroy

    PRINT1 ( _L("Number of results = %d"), aResults.Count() );

    PRINT ( _L("End CPcsAlgorithm2::GetAllContentsL") );

    //__LATENCY_MARKEND ( _L("CPcsAlgorithm2::GetAllContentsL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::IsGroupSearchL
// Checks if a group search is required
// ----------------------------------------------------------------------------
TBool CPcsAlgorithm2::IsGroupSearchL(CPsSettings& aSettings,
                                     RArray<TInt>& aGroupIdArray)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::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 CPcsAlgorithm2::IsGroupSearchL") );

    if (aGroupIdArray.Count() == 1)
        return ETrue;

    return EFalse;
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::ReplaceGroupsUriL
// Replace groups uri to contacts uri
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::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
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::FilterSearchResultsForGroupsL
// Filters the results that belong to a group
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::FilterSearchResultsForGroupsL(RArray<TInt>& contactsInGroup, 
                                                   RPointerArray<CPsData>& aSearchResults)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::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 CPcsAlgorithm2::FilterSearchResultsForGroupsL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::GetContactsInGroupL
// Recover contacts that belong to a group
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::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
        const TInt groupsCount = groups.Count(); 
        for (TInt i = 0; i < groupsCount; i++)
            {
            if (groups[i]->Id() == aGroupId)
                {
                groups[i]->IntDataExt(aGroupContactIds); // All contacts in group
                break;
                }
            }

        CleanupStack::PopAndDestroy( &groups ); // Close
        }
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::GetDataOrderL
// 
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::GetDataOrderL(TDesC& aURI, RArray<TInt>& aDataOrder)
    {
    PRINT ( _L("End CPcsAlgorithm2::GetDataOrderL") );

    TInt arrayIndex = -1;

    if (CPcsAlgorithm2Utils::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 CPcsAlgorithm2::GetDataOrderL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::GetSortOrderL
// 
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::GetSortOrderL(TDesC& aURI, RArray<TInt>& aDataOrder)
    {
    PRINT ( _L("End CPcsAlgorithm2::GetSortOrderL") );

    TInt arrayIndex = -1;

    if (CPcsAlgorithm2Utils::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 CPcsAlgorithm2::GetSortOrderL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2::ChangeSortOrderL
// 
// ----------------------------------------------------------------------------
void CPcsAlgorithm2::ChangeSortOrderL(TDesC& aURI, RArray<TInt>& aSortOrder)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::ChangeSortOrderL.") );

    PRINT ( _L("CPcsAlgorithm2::ChangeSortOrderL. Sort order change received.") );
    PRINT1 ( _L("URI = %S"), &aURI );

    // If URI is search in a group URI return       
    if (CPcsAlgorithm2Utils::IsGroupUri(aURI))
        {
        PRINT ( _L("CPcsAlgorithm2::ChangeSortOrderL. Sort order change not supported.") );
        return;
        }

    // Check if a cache exists
    TInt arrayIndex = GetCacheIndex(aURI);
    if (arrayIndex < 0)
        {
        PRINT ( _L("CPcsAlgorithm2::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;
        const TInt mySourtOrderCount = mySortOrder.Count(); 
        for ( TInt i = 0; i < mySourtOrderCount ; i++ )
            {
            if (mySortOrder[i] != aSortOrder[i])
                {
                same = EFalse;
                break;
                }
            }

        if (same)
            {
            PRINT ( _L("CPcsAlgorithm2::ChangeSortOrderL. Same sort order received. Ignoring ...") );
            PRINT ( _L("End CPcsAlgorithm2::ChangeSortOrderL.") );
            mySortOrder.Reset();
            return;
            }
        }

    mySortOrder.Reset();

    PRINT ( _L("CPcsAlgorithm2::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);

    // Request for data again
    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 CPcsAlgorithm2::ChangeSortOrderL.") );
    }

// ---------------------------------------------------------------------------------
// Read the persisted sort order from the central repository
// Persisted sort order is of form URI Field1 Field2 Field3 .. FieldN (space delimited)
// ---------------------------------------------------------------------------------
void CPcsAlgorithm2::ReadSortOrderFromCenRepL(const TDesC& aURI, RArray<TInt>& aSortOrder)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::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 CPcsAlgorithm2::ReadSortOrderFromCenRepL.") );
    }

// ---------------------------------------------------------------------------------
// Write the sort order into the central repository
// Persisted sort order is of form URI Field1 Field2 Field3 .. FieldN (space delimited)
// ---------------------------------------------------------------------------------
void CPcsAlgorithm2::WriteSortOrderToCenRepL(const TDesC& aURI, RArray<TInt>& aSortOrder)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::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("CPcsAlgorithm2::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("CPcsAlgorithm2::WriteSortOrderToCenRepL. cenrep error.") );
                return;
                }

            if (str == KNullDesC)
                {
                keyIndex = i; // i has the next free location
                break;
                }
            }
        }

    if (keyIndex == -1)
        {
        PRINT ( _L("CPcsAlgorithm2::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
    const TInt sortOrderCount =  aSortOrder.Count();
    for (TInt j = 0; j < sortOrderCount; 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 CPcsAlgorithm2::WriteSortOrderToCenRepL.") );
    }

// ---------------------------------------------------------------------------------
// WriteClientDataL.
// Write the content required by client
// ---------------------------------------------------------------------------------
CPsClientData* CPcsAlgorithm2::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
    const TInt dataElementCount = aPsData.DataElementCount(); 
    for (TInt i = 0; i <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 CPcsAlgorithm2::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;
        }
    
    // Increment the relevant counter in P&S by one to signal the clients about
    // the cache update.
    if( psKey != KErrNotFound )
        {
        TInt counter( KErrNotFound );
        TInt err = RProperty::Get( KPcsInternalUidCacheStatus, psKey, counter );
        if ( !err )
            {
            counter++;
            RProperty::Set( KPcsInternalUidCacheStatus, psKey, counter );
            }
        }
    }

// ---------------------------------------------------------------------------------
// ReconstructCacheDataL.
// ---------------------------------------------------------------------------------
void CPcsAlgorithm2::ReconstructCacheDataL()
    {
    PRINT ( _L("Enter CPcsAlgorithm2::ReconstructCacheDataL.") );

    TInt err;
    TRAP( err, iKeyMap->ReconstructKeymapL());
    if (err != KErrNone)
        {    
        PRINT1 ( _L("keyMap ReconstructKeymapL, err =%d"),err );
        }
    
    for (TInt index = 0; index < iCacheCount; index++)
        {
        CPcsCache* cache = iPcsCache[index];

        HBufC* uri = cache->GetUri();
        // Clear the cache
        cache->RemoveAllFromCache();

        if (err != KErrNone)
            {
            SetCachingError(*uri, err);
            }
        //Update the caching status as ECachingInProgress, since now the caching
        // would be started again
        UpdateCachingStatus(*uri, ECachingInProgress);

        // Request for data again
        TRAP(err, iPsDataPluginInterface->RequestForDataL(*uri));
        PRINT1 ( _L("iPsDataPluginInterface->RequestForDataL, err =%d"),err );

        if (err != KErrNone)
            {
            SetCachingError(*uri, err);
            }
        }
    }

// ---------------------------------------------------------------------------------
// DoLaunchPluginsL.
// launch plugins by idle
// ---------------------------------------------------------------------------------
TInt CPcsAlgorithm2::DoLaunchPluginsL(TAny* aPtr)
    {
    CPcsAlgorithm2* ptr = (CPcsAlgorithm2*) aPtr;
    ptr->DoLaunchPluginsL();
    return EFalse;
    }

// ---------------------------------------------------------------------------------
// DoLaunchPluginsL.
// lauch plugins
// ---------------------------------------------------------------------------------
void CPcsAlgorithm2::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 CPcsAlgorithm2Utils::CompareDataBySortOrder
    // to see how this is being used.
    User::LeaveIfError(Dll::SetTls(&iPcsCache));

    // Initialize cache
    RPointerArray<TDesC> dataStores;
    CleanupClosePushL( dataStores );

    iPsDataPluginInterface->GetAllSupportedDataStoresL(dataStores);

    const TInt dataStoresCount = dataStores.Count();
    for (TInt dIndex = 0; dIndex < dataStoresCount; dIndex++)
        {
        AddDataStore(*(dataStores[dIndex]));
        }

    CleanupStack::PopAndDestroy( &dataStores ); // Close
    }

/**
* Returns the Adaptive Grid for one or more URI
* 
*/
 void CPcsAlgorithm2::GetAdaptiveGridL( const MDesCArray& /*aURIs*/,
                               const TBool /*aCompanyName*/,
                               TDes& /*aAdaptiveGrid*/ )
    {
     PRINT ( _L("Enter CPcsAlgorithm2::GetAdaptiveGridL") );


     PRINT ( _L("End CPcsAlgorithm2::GetAdaptiveGridL") );

    }
// End of file