predictivesearch/PcsAlgorithm/Algorithm1/src/CPcsAlgorithm1.cpp
branchRCL_3
changeset 15 e8e3147d53eb
parent 0 e686773b3f54
child 21 b3431bff8c19
--- a/predictivesearch/PcsAlgorithm/Algorithm1/src/CPcsAlgorithm1.cpp	Fri Mar 12 15:41:25 2010 +0200
+++ b/predictivesearch/PcsAlgorithm/Algorithm1/src/CPcsAlgorithm1.cpp	Mon Mar 15 12:39:26 2010 +0200
@@ -30,7 +30,7 @@
 #include "CPsDataPluginInterface.h"
 #include "CPcsDefs.h"
 
-const TInt KSpace = 32;
+const TText KSpace = ' ';
 
 // UID used for Publish and Subscribe mechanism
 // This should be same as the one defined in CPsPropertyHandler.cpp
@@ -95,15 +95,16 @@
     }
     
     // Initialize key map and pti engine
-    //keyMap = CPcsKeyMap::NewL();
+    //iKeyMap = CPcsKeyMap::NewL();
     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("**********************************************."));
+        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
@@ -185,7 +186,8 @@
 
 // ----------------------------------------------------------------------------
 // CPcsAlgorithm1::ReplaceZeroWithSpaceL
-// Replace all '0's in a search query with " "s
+// Replace all "0"s in a search query with " "s if those characters are on
+// the same key.
 // ----------------------------------------------------------------------------
 TBool  CPcsAlgorithm1::ReplaceZeroWithSpaceL(CPsQuery& aQuery)
 {   
@@ -195,43 +197,33 @@
 
     TBool queryModified = EFalse;    
 
-    if (iKeyMap->GetSpaceAndZeroOnSameKey())
-    {
-        /* In phones like E52 and E55, where the "0" and the " " characters are on
-         * the same key, then the "0"s EItut have to be considered as possible
-         * separators.
-         */
+    /* 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 EItut "0"s into spaces
-        TChar space(KSpace);    
-        for ( TInt index = skipIndex; index < aQuery.Count(); index++ )
+    // 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 )
         {
-            CPsQueryItem& item = aQuery.GetItemAtL(index);
-    
-            if ( item.Character().GetNumericValue() == 0 &&
-                 item.Mode() == EItut )
-            {
-                item.SetCharacter(space);
-                queryModified = ETrue;
-            }
-        }    
-    }
-    else
-    {
-        /* In phones like N97 and E72, where the "0" and the " " characters are on
-         * a different key, then the "0" EItut does not have to be considered as a
-         * possible separator.
-         */
-        
-        PRINT ( _L("CPcsAlgorithm1::ReplaceZeroWithSpaceL: \"0\" and \" \" are on different keys, not attepting to replace") );
+            item.SetCharacter(KSpace);
+            queryModified = ETrue;
+        }
     }
     
     //PRINTQUERY ( _L("CPcsAlgorithm1::ReplaceZeroWithSpaceL (AFTER): "), aQuery );
@@ -256,12 +248,13 @@
 	
 	__LATENCY_MARK ( _L("CPcsAlgorithm1::PerformSearchL") );
 
-	RPointerArray<CPsQuery> query;
-	
 	// 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
@@ -345,7 +338,7 @@
 	else
 	{
 		// Copy all the contents from tempSearchResults to the results stream
-		for(int i = 0; i < resultSet; i++)
+		for(TInt i = 0; i < resultSet; i++)
 		{
 			aSearchResults.Append(WriteClientDataL(*(tempSearchResults[i])));
 		}
@@ -353,9 +346,9 @@
 	// ------------------------------------------------------------------------
 
     // Cleanup local results array
-	tempSearchResults.Reset();    // Don't destroy
-    tempSearchResultsIni.Reset(); // Don't destroy
-    tempSearchResultsMod.Reset(); // Don't destroy
+    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") );
 
@@ -545,8 +538,8 @@
 // ----------------------------------------------------------------------------
 void  CPcsAlgorithm1::DoSearchL(const CPsSettings& aSettings,
 								CPsQuery& aQuery,
-								RPointerArray<CPsData>& searchResults,
-								RPointerArray<CPsPattern>& searchSeqs )
+								RPointerArray<CPsData>& aSearchResults,
+								RPointerArray<CPsPattern>& aSearchSeqs )
 {
     PRINT ( _L("Enter CPcsAlgorithm1::DoSearchL") );
 
@@ -554,10 +547,13 @@
     
     // -(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);
     
@@ -570,33 +566,32 @@
     	GetContactsInGroupL ( groupIdArray[0], contactsInGroup );
     }
     
-   	groupIdArray.Close();
-   	
     // -----------------------------------------------------------------------
         
     // Extract query list. 
     RPointerArray<CPsQuery> queryList = iMultiSearchHelper->MultiQueryL(aQuery);
+    CleanupResetAndDestroyPushL( queryList );
     PRINTQUERYLIST ( _L("CPcsAlgorithm1::DoSearchL: "), queryList );
 
-    // (1)-------------------- No query return all contacts -------------------    
+    // (1)-------------------- No query return all contacts -------------------
     if ( queryList.Count() == 0 )
     {
-    	GetAllContentsL(*tempSettings, searchResults);   
+    	GetAllContentsL(*tempSettings, aSearchResults);
     	
     	if ( isGroupSearch ) 
     	{
-    		FilterSearchResultsForGroupsL ( contactsInGroup, searchResults );
+    		FilterSearchResultsForGroupsL( contactsInGroup, aSearchResults );
     	}
     }
     // ------------------------------------------------------------------------
 
     // (2)-------------------- Perform a single query search ------------------
-    else if ( queryList.Count() == 1 ) // single qwery
+    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, searchResults, searchSeqs);
+                               contactsInGroup, aSearchResults, aSearchSeqs);
     }
     // ------------------------------------------------------------------------
 
@@ -607,17 +602,16 @@
 
 		// Search results
 		iMultiSearchHelper->SearchMultiL(*tempSettings, queryList, isGroupSearch,
-		                                 contactsInGroup, searchResults, searchSeqs);
+		                                 contactsInGroup, aSearchResults, aSearchSeqs);
     }
     // -------------------------------------------------------------------------
 
     // Cleanup
-    delete tempSettings;
-    tempSettings = NULL;
     
-    groupIdArray.Close();
-    contactsInGroup.Close();
-	queryList.ResetAndDestroy();
+    CleanupStack::PopAndDestroy( &queryList ); // ResetAndDestroy
+    CleanupStack::PopAndDestroy( tempSettings );
+    CleanupStack::PopAndDestroy( &groupIdArray ); // Close
+    CleanupStack::PopAndDestroy( &contactsInGroup ); // Close
 
 	__LATENCY_MARKEND ( _L("CPcsAlgorithm1::DoSearchL") );
 
@@ -629,7 +623,7 @@
 // Search function helper
 // ----------------------------------------------------------------------------
 void  CPcsAlgorithm1::DoSearchInputL(CPsQuery& aQuery,
-		                             TDesC& aData,
+		                             const TDesC& aData,
 		                             RPointerArray<TDesC>& aMatchSet,
 		                             RArray<TPsMatchLocation>& aMatchLocation )
 {
@@ -736,19 +730,14 @@
 	
     CPcsCache* cache = iPcsCache[dataStoreIndex];
     	
-	TRAPD(err, cache->RemoveAllFromCacheL());
-	
-	if ( err != KErrNone )
-	{
-		SetCachingError(aDataStore, err);
-	}
+	cache->RemoveAllFromCache();
 }
 
 // ----------------------------------------------------------------------------
 // CPcsAlgorithm1::GetCacheIndex
 // Return the cache index for a data store
 // ----------------------------------------------------------------------------
-TInt CPcsAlgorithm1::GetCacheIndex(TDesC& aDataStore)
+TInt CPcsAlgorithm1::GetCacheIndex(const TDesC& aDataStore)
 {
     for ( int i = 0; i < iPcsCache.Count(); i++ )
     {
@@ -781,13 +770,13 @@
 	{
 		SetCachingError(aDataStore, err);
 		return;
-	}	
+	}
 	
 	// Increment the cachecount
 	iCacheCount++;
 	
-    RArray<TInt> dataFields;        	   
-    TRAP(err, iPsDataPluginInterface->GetSupportedDataFieldsL(cache->GetURI(), dataFields));   
+    RArray<TInt> dataFields;
+    TRAP(err, iPsDataPluginInterface->GetSupportedDataFieldsL(cache->GetURI(), dataFields));
 	if ( err != KErrNone )
 	{
 		SetCachingError(aDataStore, err);
@@ -860,7 +849,7 @@
 // CPcsAlgorithm1::GetUriForIdL
 // Get the URI string for this internal id
 // ----------------------------------------------------------------------------
-TDesC& CPcsAlgorithm1::GetUriForIdL(TUint8 aUriId)
+const TDesC& CPcsAlgorithm1::GetUriForIdL(TUint8 aUriId)
 {
     TBool found = EFalse;
     TInt i = 0;
@@ -886,7 +875,7 @@
 // CPcsAlgorithm1::FindStoreUri
 // Checks if this store exists
 // ----------------------------------------------------------------------------
-TInt CPcsAlgorithm1::FindStoreUri ( TDesC& aDataStore )
+TInt CPcsAlgorithm1::FindStoreUri ( const TDesC& aDataStore )
 {
     for ( int i = 0; i < iPcsCache.Count(); i++ )
     {
@@ -961,14 +950,12 @@
 // CPcsAlgorithm1::SetCachingError
 // Updates cachinge error
 // ----------------------------------------------------------------------------
-void CPcsAlgorithm1::SetCachingError(TDesC& aDataStore, TInt aError)
+void CPcsAlgorithm1::SetCachingError(const TDesC& aDataStore, TInt aError)
 {
-	TBuf<KBufferMaxLen> store;
-	store.Copy(aDataStore);
-	PRINT2 ( _L("SetCachingError::URI %S ERROR %d"), &store, aError );
+	PRINT2 ( _L("SetCachingError::URI %S ERROR %d"), &aDataStore, aError );
 
 	iCacheError = aError;
-	RProperty::Set(KCStatus,1,iCacheError );
+	RProperty::Set( KCStatus,1,iCacheError );
 }
 
 // ----------------------------------------------------------------------------
@@ -982,46 +969,50 @@
     
     PRINT ( _L("Enter CPcsAlgorithm1::GetAllContentsL") );   
     
-    // Get the data stores
-    RPointerArray<TDesC> aDataStores;
-    aSettings.SearchUrisL(aDataStores);
-
     // To hold array of results from different data stores
     typedef RPointerArray<CPsData> CPSDATA_R_PTR_ARRAY;
-    RPointerArray<CPSDATA_R_PTR_ARRAY> iSearchResultsArr;
+    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 ( int dsIndex = 0; 
-          dsIndex < aDataStores.Count(); 
+    for ( TInt dsIndex = 0; 
+          dsIndex < dataStores.Count(); 
           dsIndex++ )
     {	        	
         RPointerArray<CPsData> *temp = new (ELeave) RPointerArray<CPsData>();
-        iSearchResultsArr.Append(temp);
+        searchResultsArr.Append(temp);
         
-        TInt arrayIndex = GetCacheIndex(*(aDataStores[dsIndex]));
+        TInt arrayIndex = GetCacheIndex(*(dataStores[dsIndex]));
         
 		if ( arrayIndex < 0 ) continue;
 		
 		CPcsCache* cache = GetCache(arrayIndex);
         	      
-        cache->GetAllContentsL(*(iSearchResultsArr[dsIndex]));
-    }	   		   
-    	    	         	
-    aDataStores.ResetAndDestroy();    
+        cache->GetAllContentsL(*(searchResultsArr[dsIndex]));
+    }
+    
+    CleanupStack::PopAndDestroy( &dataStores ); // ResetAndDestroy
   
     // Merge the results from different data stores
-    CPcsAlgorithm1Utils::FormCompleteSearchResultsL(iSearchResultsArr,
+    CPcsAlgorithm1Utils::FormCompleteSearchResultsL(searchResultsArr,
     												aResults);
   
     // Cleanup the local arrays
-    for(TInt i = 0; i < iSearchResultsArr.Count(); i++)
+    for(TInt i = 0; i < searchResultsArr.Count(); i++)
     {
-    	iSearchResultsArr[i]->Reset();
-    	delete iSearchResultsArr[i];
-    	iSearchResultsArr[i] = NULL;
-    }    
-    
-    iSearchResultsArr.Reset();    
+    	searchResultsArr[i]->Reset();
+    }
+    CleanupStack::PopAndDestroy( &searchResultsArr ); // ResetAndDestroy
 
     PRINT1 ( _L("Number of results = %d"), aResults.Count() );
    
@@ -1044,18 +1035,18 @@
      
     // 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
-    	searchUris.ResetAndDestroy();
     	aGroupIdArray.Close();
     	User::Leave(KErrArgument); 
     }
     
-    searchUris.ResetAndDestroy();
+    CleanupStack::PopAndDestroy( &searchUris ); // ResetAndDestroy
         
     PRINT ( _L("End CPcsAlgorithm1::IsGroupSearchL") );    
     
@@ -1069,18 +1060,19 @@
 // CPcsAlgorithm1::ReplaceGroupsUriL
 // Replace groups uri to contacts uri
 // ----------------------------------------------------------------------------
-void CPcsAlgorithm1::ReplaceGroupsUriL ( CPsSettings& aSettings )
+void CPcsAlgorithm1::ReplaceGroupsUriL( CPsSettings& aSettings )
 {
-	RPointerArray<TDesC> uri; 
+	RPointerArray<TDesC> uri;
+	CleanupResetAndDestroyPushL( uri );
     
     // Set contacts db uri
-	HBufC* cntdb = HBufC::NewL(KBufferMaxLen);
-	cntdb->Des().Copy(KVPbkDefaultCntDbURI);
-	uri.Append(cntdb);
+	HBufC* cntdb = KVPbkDefaultCntDbURI().AllocLC();
+	uri.AppendL(cntdb);
+	CleanupStack::Pop( cntdb ); // ownership transferred
 	aSettings.SetSearchUrisL(uri);
 	
 	// Cleanup
-	uri.ResetAndDestroy();
+	CleanupStack::PopAndDestroy( &uri ); // ResetAndDestroy
 }
 
 // ----------------------------------------------------------------------------
@@ -1119,27 +1111,20 @@
 // CPcsAlgorithm1::GetContactsInGroupL
 // Recover contacts that belong to a group
 // ----------------------------------------------------------------------------
-void CPcsAlgorithm1::GetContactsInGroupL ( TInt aGroupId, 
-                                           RArray<TInt>& aGroupContactIds )
+void CPcsAlgorithm1::GetContactsInGroupL( TInt aGroupId, 
+                                          RArray<TInt>& aGroupContactIds )
 {	    
     // Clear results array
     aGroupContactIds.Reset();
     
-    // Groups URI
-    HBufC* groupURI = HBufC::NewL(50);
-    groupURI->Des().Copy(KVPbkDefaultGrpDbURI);
-       
-    // Cache Index   
-    TInt cacheIndex = GetCacheIndex(*groupURI);
-    
-    // Cleanup
-    delete groupURI;
-    groupURI = NULL;
+    // 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);
@@ -1154,7 +1139,7 @@
 			}		
 		}
 		
-		groups.Reset();	
+		CleanupStack::PopAndDestroy( &groups ); // Close
 	}
 }
 
@@ -1263,7 +1248,7 @@
     if ( aSortOrder.Count() == mySortOrder.Count() )    
     {
          TBool same = ETrue;
-         for ( int i = 0; i < mySortOrder.Count(); i++ )	
+         for ( TInt i = 0; i < mySortOrder.Count(); i++ )	
          {
             if ( mySortOrder[i] != aSortOrder[i] )
      		{
@@ -1308,7 +1293,7 @@
 // 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(TDesC& aURI, 
+void CPcsAlgorithm1::ReadSortOrderFromCenRepL(const TDesC& aURI, 
                                               RArray<TInt>& aSortOrder)
 {
     PRINT ( _L("Enter CPcsAlgorithm1::ReadSortOrderFromCenRepL.") );
@@ -1343,7 +1328,7 @@
 		        // Extract the sort order
 		        token.Set(lex.NextToken());
 		        
-				while ( token.Length() != 0 )				
+				while ( token.Length() != 0 )
 				{	
 				    TLex lex1(token);
 				    
@@ -1353,16 +1338,16 @@
 					if ( KErrNone == err )
 					{
 						aSortOrder.Append(intVal);
-					}				    	        		    				    
+					}
 				    
 					// Next token
-					token.Set(lex.NextToken());				
+					token.Set(lex.NextToken());
 				}	
 				
 				break;
-		    }		    		    
+		    }
 	    }
-	    		
+	
     }
     
     delete repository;
@@ -1374,12 +1359,12 @@
 // Write the sort order into the central repository
 // Persisted sort order is of form URI Field1 Field2 Field3 .. FieldN (space delimited)
 // ---------------------------------------------------------------------------------
-void CPcsAlgorithm1::WriteSortOrderToCenRepL(TDesC& aURI, 
+void CPcsAlgorithm1::WriteSortOrderToCenRepL(const TDesC& aURI, 
                                              RArray<TInt>& aSortOrder)
 {   
     PRINT ( _L("Enter CPcsAlgorithm1::WriteSortOrderToCenRepL.") );
 
-    CRepository *repository = CRepository::NewL( KCRUidPSSortOrder );
+    CRepository *repository = CRepository::NewLC( KCRUidPSSortOrder );
 
 	// Check if there an entry for this URI in cenrep
 	TBuf<KCRMaxLen> str;
@@ -1444,7 +1429,7 @@
 	}
 
     // Persist the sort order
-	HBufC* str1 = HBufC::NewL(KCRMaxLen);
+	HBufC* str1 = HBufC::NewLC(KCRMaxLen);
 	TPtr ptr(str1->Des());
 
 	// Append the URI
@@ -1452,7 +1437,7 @@
 	ptr.Append(KSpace);
 
 	// Append the sort order fields
-	for ( int j = 0; j < aSortOrder.Count(); j++ )
+	for ( TInt j = 0; j < aSortOrder.Count(); j++ )
 	{
 		ptr.AppendNum(aSortOrder[j]);
 	    ptr.Append(KSpace);
@@ -1463,9 +1448,9 @@
 
 	User::LeaveIfError(err);
 
-	delete str1;   	
+	CleanupStack::PopAndDestroy( str1 );
   
-    delete repository;
+    CleanupStack::PopAndDestroy( repository );
      
     PRINT ( _L("End CPcsAlgorithm1::WriteSortOrderToCenRepL.") );
 }
@@ -1530,15 +1515,17 @@
     User::LeaveIfError( Dll::SetTls(&iPcsCache) );
     
     // Initialize cache
-    RPointerArray<TDesC> dataStores;    
+    RPointerArray<TDesC> dataStores;
+    CleanupClosePushL( dataStores );
     
     iPsDataPluginInterface->GetAllSupportedDataStoresL(dataStores);
         
-    for ( int dIndex = 0; dIndex < dataStores.Count(); dIndex++ )
-    {
-    AddDataStore(*(dataStores[dIndex]));
-    }
-    dataStores.Reset();
+    for ( TInt dIndex = 0; dIndex < dataStores.Count(); dIndex++ )
+        {
+        AddDataStore(*(dataStores[dIndex]));
+        }
+    
+    CleanupStack::PopAndDestroy( &dataStores ); // Close
     }
 // End of file