commsfwtools/preparedefaultcommsdatabase/Tools/ced/src/input.cpp
changeset 0 dfb7c4ff071f
equal deleted inserted replaced
-1:000000000000 0:dfb7c4ff071f
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 /**
       
    17  @file 
       
    18  @internalComponent
       
    19 */
       
    20 
       
    21 #include "input.h"
       
    22 #include "filedump.h"
       
    23 
       
    24 namespace
       
    25 {
       
    26 TUint32 DoHash(const HBufC& aText)
       
    27     {
       
    28     const TDesC& ref = aText;
       
    29     return DefaultHash::Des16(ref);
       
    30     }
       
    31 
       
    32 TBool AreTheKeysIdentical(const HBufC& aKey1, const HBufC& aKey2)
       
    33     {
       
    34     return (aKey1 == aKey2);
       
    35     }
       
    36 } //anonymous namespace
       
    37 
       
    38 
       
    39 
       
    40 CInputFileParser* CInputFileParser::FactoryLC(TBool aIsXML, TDesC& aFile, CFileDump* aLog)
       
    41     {
       
    42     CInputFileParser* p = NULL;
       
    43     
       
    44     if (aIsXML)
       
    45         {
       
    46         p = new(ELeave) CXMLFileParser(aFile, aLog);
       
    47         }
       
    48     else
       
    49         {
       
    50         p = new(ELeave) CCfgFileParser(aFile, aLog);
       
    51         }
       
    52 
       
    53 	CleanupStack::PushL(p);
       
    54 	return p;
       
    55     }
       
    56 
       
    57 
       
    58 CInputFileParser::CInputFileParser(TDesC& aFile, CFileDump* aLog) :
       
    59     iFile(aFile),
       
    60     iLog(aLog),
       
    61     iTable(NULL)
       
    62     {
       
    63     }
       
    64 
       
    65 /**
       
    66 */
       
    67 TBool CInputFileParser::IsMeshCompatibleL()
       
    68 	{
       
    69 	iLog->Msg(_L("MESHINFO: Checking if input file <%S> is mesh compatible"), &iFile);
       
    70 	
       
    71     InitialiseL();
       
    72 
       
    73     const TText** pTable = iTable;
       
    74     
       
    75 	TBuf<MAX_BUFFER_LEN> table            = *pTable;
       
    76 	TBool                isMeshCompatible = EFalse;
       
    77 	while (table.Compare(TPtrC(NO_MORE_RECORDS)) != 0)
       
    78 		{
       
    79 		iLog->Msg(_L(" "));
       
    80 		iLog->Msg(_L("Parsing %S Table"), &table);
       
    81 		
       
    82 		if (CheckMeshTemplateL(table) || CheckMeshInsertsL(table))
       
    83         {
       
    84     		iLog->Msg(_L("MESHINFO: Found mesh table <%S>, the configuration file is assumed to be mesh-compatible"), &table);
       
    85 			isMeshCompatible = ETrue;
       
    86 			break;
       
    87 			}					
       
    88 		table = *(++pTable);
       
    89 		}	
       
    90 	return isMeshCompatible;
       
    91 	}
       
    92 
       
    93 
       
    94 void CInputFileParser::Exit(TInt aErr)
       
    95     {
       
    96 	iLog->Msg(_L("MESHERR: Configuration file [%S] could not be opened"), &iFile);
       
    97 	iLog->Msg(_L("==================="));
       
    98 	iLog->Msg(_L("ERROR"));
       
    99 	User::Leave(aErr);
       
   100     }
       
   101 
       
   102 
       
   103 CCfgFileParser::CCfgFileParser(TDesC& aFile, CFileDump* aLog) :
       
   104     CInputFileParser(aFile, aLog)
       
   105     {
       
   106     }
       
   107 
       
   108 
       
   109 CCfgFileParser::~CCfgFileParser()
       
   110     {
       
   111     iCfg.CloseConfigFile();
       
   112     }
       
   113 
       
   114 
       
   115 void CCfgFileParser::InitialiseL()
       
   116     {
       
   117     iTable = const_cast<const TText**>(tableMeshMarkerArray);
       
   118     if (!iCfg.OpenConfigFile(iFile))
       
   119 		{
       
   120 		Exit(KErrNotFound);
       
   121 		}
       
   122     }
       
   123     
       
   124     
       
   125 TBool CCfgFileParser::CheckMeshTemplateL(TDesC& aTable)
       
   126     {
       
   127 	return iCfg.OpenTemplateBlock(aTable);
       
   128     }
       
   129     
       
   130     
       
   131 TBool CCfgFileParser::CheckMeshInsertsL(TDesC& aTable)
       
   132     {
       
   133 	return iCfg.OpenAddBlock(aTable);
       
   134     }
       
   135 
       
   136 
       
   137 CXMLFileParser::CXMLFileParser(TDesC& aFile, CFileDump* aLog) :
       
   138     CInputFileParser(aFile, aLog),
       
   139     iPtr(NULL, 0)
       
   140     {
       
   141     }
       
   142 
       
   143 
       
   144 CXMLFileParser::~CXMLFileParser()
       
   145     {
       
   146     delete iPtr.Ptr();
       
   147     }
       
   148 
       
   149 
       
   150 void CXMLFileParser::InitialiseL()
       
   151     {
       
   152     iTable = const_cast<const TText**>(tableXMLMeshMarkerArray);
       
   153 	TRAPD(ret, OpenXMLFileL());
       
   154 	if (ret != KErrNone)
       
   155 		{
       
   156 		Exit(ret);
       
   157 		}
       
   158     }
       
   159     
       
   160     
       
   161 void CXMLFileParser::OpenXMLFileL()
       
   162     {
       
   163     // Shameless copy of 'CIniData::ConstructL()'
       
   164     
       
   165 	// Connect to file server
       
   166 	//
       
   167 	TAutoClose<RFs> fs;
       
   168 	User::LeaveIfError(fs.iObj.Connect());
       
   169 	fs.PushL();
       
   170 
       
   171 
       
   172 	// Open file
       
   173 	//
       
   174 	TAutoClose<RFile> file;
       
   175 	TInt size;
       
   176 	User::LeaveIfError(file.iObj.Open(fs.iObj, iFile, EFileStreamText | EFileRead));
       
   177 	file.PushL();
       
   178 
       
   179 
       
   180 	// Get file size and read in
       
   181 	//
       
   182 	User::LeaveIfError(file.iObj.Size(size));
       
   183 	TText* data = (TText*)User::AllocL(size);
       
   184 	iPtr.Set(data, TUint(size)/sizeof(TText), TUint(size)/sizeof(TText));
       
   185 	TPtr8 dest((TUint8*)data, 0, size);
       
   186 	User::LeaveIfError(file.iObj.Read(dest));
       
   187 	TUint8* ptr = (TUint8*)data;
       
   188 
       
   189 
       
   190 	// This is orderred as FEFF assuming the processor is Little Endian
       
   191 	// The data in the file is FFFE.		PRR 28/9/98
       
   192 	//
       
   193 	if (size >= (TInt)sizeof(TText) && iPtr[0]==0xFEFF)
       
   194 		{
       
   195 		// TODO: strip out UNICODE spaces.
       
   196 		
       
   197 		// UNICODE Text file so lose the FFFE
       
   198 		//
       
   199 		Mem::Copy(ptr, ptr+sizeof(TText), size-sizeof(TText));
       
   200 		iPtr.Set(data, TUint(size)/sizeof(TText)-1, TUint(size)/sizeof(TText)-1);
       
   201 		}
       
   202 	else if(size)
       
   203 		{
       
   204 		// NON-UNICODE so convert to UNICODE
       
   205 		//
       
   206 		TText* newdata = (TText*)User::AllocL(size * sizeof(TText));
       
   207 		iPtr.Set(newdata, size, size);
       
   208 		TInt i;
       
   209 		TInt actualLength = 0;
       
   210 		for (i = 0; i < size ; ++i)
       
   211 			{
       
   212 			// Lose the spaces (at the cost of having allocated a buffer slightly to big...).
       
   213 			// This makes the subsequent string searches so much easier and more efficient.
       
   214 			//
       
   215 			if (!TChar(ptr[i]).IsSpace())
       
   216 			    {
       
   217     			iPtr[actualLength++] = ptr[i];
       
   218     			}
       
   219 			}
       
   220 		iPtr.Set(newdata, actualLength, size);
       
   221 		delete data;
       
   222 		}
       
   223 
       
   224 	file.Pop();
       
   225 	fs.Pop();
       
   226     }
       
   227 
       
   228 
       
   229 // Assemble all the XML validation related strings together.
       
   230 //
       
   231 // In essence, a file is considered mesh compatible (has already been updated with mesh tables) if the record
       
   232 // names in 'dbdef.h::tableXMLMeshMarkerArray' exist in stringsof the form "operation="add">" or 
       
   233 // "operation="template">"
       
   234 //
       
   235 _LIT(KTemplate, "template");
       
   236 _LIT(KAdd, "add");
       
   237 _LIT(KRecordInsertFormat, "<%Soperation=\"%S\">");           
       
   238 
       
   239 
       
   240 TBool CXMLFileParser::CheckMeshTemplateL(TDesC& aTable)
       
   241     {
       
   242 	return (SearchXMLFile(KTemplate, aTable));
       
   243     }
       
   244     
       
   245     
       
   246 TBool CXMLFileParser::CheckMeshInsertsL(TDesC& aTable)
       
   247     {
       
   248 	return SearchXMLFile(KAdd, aTable);
       
   249     }
       
   250 
       
   251 
       
   252 TBool CXMLFileParser::SearchXMLFile(const TDesC& aOpType, const TDesC& aTable)
       
   253 	{
       
   254 	TBuf<MAX_BUFFER_LEN> xmlTableName;
       
   255 	xmlTableName.Format(KRecordInsertFormat, &aTable, &aOpType);
       
   256 	
       
   257 	// No folding or collation.
       
   258 	//
       
   259     return (iPtr.Find(xmlTableName) >= 0);
       
   260 	}
       
   261 
       
   262 //------------------------ LinkByTagResolver ---------------------------
       
   263 
       
   264 
       
   265 LinkByTagResolver* LinkByTagResolver::NewL(CfgFile* aIniData, CFileDump* aLogger)
       
   266     {
       
   267     LinkByTagResolver* self = LinkByTagResolver::NewLC(aIniData, aLogger);
       
   268     CleanupStack::Pop(self);
       
   269     return self;
       
   270     }
       
   271 
       
   272 LinkByTagResolver* LinkByTagResolver::NewLC(CfgFile* aIniData, CFileDump* aLogger)
       
   273     {
       
   274     LinkByTagResolver* self = new(ELeave) LinkByTagResolver(aIniData, aLogger);
       
   275     CleanupStack::PushL(self);
       
   276     self->ConstructL();
       
   277     return self;
       
   278     }
       
   279 
       
   280 //the ctor...
       
   281 LinkByTagResolver::LinkByTagResolver(CfgFile* aIniData, CFileDump* aLogger)
       
   282     : iLinkByTagRecIdPairs(0)
       
   283     , iConfigFile(aIniData)
       
   284     , iLogger(aLogger)
       
   285     {
       
   286     }
       
   287 
       
   288 //the dtor
       
   289 LinkByTagResolver::~LinkByTagResolver()
       
   290     {
       
   291     
       
   292     TPtrHashMapIter<HBufC, HBufC > iter(*iLinkByTagRecIdPairs);
       
   293     
       
   294     for (TInt i = 0; i < iLinkByTagRecIdPairs->Count(); ++i)
       
   295         {
       
   296         iter.NextValue();
       
   297         
       
   298         const HBufC* cKey = iter.CurrentKey();
       
   299         const HBufC* cValue = iter.CurrentValue();
       
   300         
       
   301         delete cKey;
       
   302         delete cValue;
       
   303         
       
   304         cKey = NULL;
       
   305         cValue = NULL;
       
   306         }
       
   307     
       
   308     iLinkByTagRecIdPairs->Close();
       
   309     delete iLinkByTagRecIdPairs;
       
   310     iLinkByTagRecIdPairs = NULL;
       
   311     
       
   312     }
       
   313 
       
   314 void LinkByTagResolver::ConstructL()
       
   315     {
       
   316     //the hash function for hashing the table
       
   317     const THashFunction32<HBufC > theHashFunc(&DoHash);
       
   318     
       
   319     //the key identity relation to campare the keys
       
   320     const TIdentityRelation<HBufC > theIdentityRelFunc(&AreTheKeysIdentical);
       
   321     
       
   322     //create the hashmap on the heap
       
   323     iLinkByTagRecIdPairs = new(ELeave) RPtrHashMap<HBufC, HBufC >(theHashFunc, theIdentityRelFunc);
       
   324     
       
   325     FillUpHashMapL();
       
   326     }
       
   327 
       
   328 void LinkByTagResolver::FillUpHashMapL()
       
   329     {
       
   330     TInt i = 0;
       
   331     TPtrC actTable = TPtrC(TablesWithLinkRecords[i]);
       
   332     
       
   333     
       
   334     while (actTable != TPtrC(NO_MORE_RECORDS))
       
   335         {
       
   336         //first search for template of the given table
       
   337         if (iConfigFile->OpenTemplateBlock(actTable))
       
   338         //got it, search for the possible link fields in the record
       
   339             {
       
   340             ExemineFieldsL(i);
       
   341             }
       
   342         
       
   343         //and now for ADD sections...
       
   344         if (iConfigFile->OpenAddBlock(actTable))
       
   345         //got it, search for the possible link fields in the record
       
   346             {
       
   347             ExemineFieldsL(i);
       
   348             }
       
   349 		
       
   350         actTable.Set(TPtrC(TablesWithLinkRecords[++i]));
       
   351         }
       
   352         //if everything went OK, we insert two empt string at the end of the RPtrHashMap
       
   353         //to be able to return an empty string instead of leave in the case if the given
       
   354         //search string not found...
       
   355         /*TBuf<0> temp;
       
   356         HBufC* key = temp.Alloc();
       
   357         HBufC* value = temp.Alloc();*/
       
   358         
       
   359         HBufC* key = HBufC::New(0);
       
   360         HBufC* value = HBufC::New(0);
       
   361         iLinkByTagRecIdPairs->Insert(key, value);
       
   362     }
       
   363 
       
   364 void LinkByTagResolver::ExemineFieldsL(const TInt aTableIndex)
       
   365     {
       
   366     TInt i = 0;
       
   367     TBuf<MAX_BUFFER_LEN> resolvedLink;
       
   368     HBufC* tempForHashSearch;
       
   369     //TBuf<MAX_BUFFER_LEN> tempForHashSearch;
       
   370     
       
   371     //the fileds form the config file from the section
       
   372     TPtrC actField = TPtrC(LinkRecordsArray[aTableIndex][i]);
       
   373     //the link setting from the file
       
   374     TPtrC setting;
       
   375     //the resolved link to Table.RecId format
       
   376     TPtrC link(resolvedLink);
       
   377                         
       
   378     TBool firstRec = ETrue;
       
   379     while (firstRec || iConfigFile->StepToNextBlock())
       
   380         {
       
   381         i = 0;
       
   382         actField.Set(TPtrC(LinkRecordsArray[aTableIndex][i]));
       
   383         
       
   384         while (actField != TPtrC(NO_MORE_RECORDS))
       
   385             {
       
   386             if (KErrNone == iConfigFile->GetSetting(actField, setting))
       
   387             //ok, got a field from the section
       
   388                 {
       
   389                 if (SearchForLink(setting))
       
   390                     {
       
   391                     //just for optimization - if the linking is already in the hashmap
       
   392                     //don't resolve it once again... tempForHashSearch should be empty!!!
       
   393                     tempForHashSearch = setting.Alloc();
       
   394                     CleanupStack::PushL(tempForHashSearch);
       
   395                     
       
   396                     if (NULL == iLinkByTagRecIdPairs->Find(*tempForHashSearch))
       
   397                         {
       
   398                         //save the state of the CIniFile object...
       
   399                         /**
       
   400                            This is needed here because in the following sections the pointers in the
       
   401                            CIniFile object will shift and the next call of StepToNextBlock could bring
       
   402                            some unexpected results...
       
   403                         */
       
   404                         TInt aBlockState = iConfigFile->file->BlockState;
       
   405                     	TInt aBlockStart = iConfigFile->file->blockStart;
       
   406                     	TInt aBlockEnd = iConfigFile->file->blockEnd;
       
   407                     	TInt aScanStart = iConfigFile->file->scanStart;
       
   408                         TPtr aSection = iConfigFile->file->section;
       
   409                 	    TPtr aBlock = iConfigFile->file->block;
       
   410                 	    
       
   411                 	    resolvedLink.Zero();
       
   412                         TRAPD(err,ResolveLinkToRecIdL(setting, resolvedLink));
       
   413                         if (err != KErrNone)
       
   414                         	{
       
   415                         	iLogger->Error(_L("ERROR: [%d] Failed to resolve LinkByTag: [%S] - database will be incomplete"), err, &setting);
       
   416                         	User::Leave(err);
       
   417                         	}
       
   418                         
       
   419                         //restore the state...
       
   420                         iConfigFile->file->BlockState = aBlockState;
       
   421                         iConfigFile->file->blockStart = aBlockStart;
       
   422                         iConfigFile->file->blockEnd = aBlockEnd;
       
   423                         iConfigFile->file->scanStart = aScanStart;
       
   424                         iConfigFile->file->section.Set(aSection);
       
   425                         iConfigFile->file->block.Set(aBlock);
       
   426                         
       
   427                         //these pointers will be deleted in the dtor!!!!!
       
   428                         HBufC* key = setting.Alloc();
       
   429                         HBufC* value = resolvedLink.Alloc();
       
   430                         
       
   431                         
       
   432                         
       
   433                         /*TBuf<MAX_BUFFER_LEN>* key = new(ELeave) TBuf<MAX_BUFFER_LEN>(setting);
       
   434                         TBuf<MAX_BUFFER_LEN>* value = new(ELeave) TBuf<MAX_BUFFER_LEN>(linkToResolve);*/
       
   435                         
       
   436                         iLogger->Msg(_L("LinkByTag resolved: [%S] to [%S]"), &setting, &resolvedLink);
       
   437                         
       
   438                         //the key and value are on the heap. What here happens is that 
       
   439                         //the 2 pointers pointing to the heap cells are copied into the 
       
   440                         //RPtrHasMap.
       
   441                         iLinkByTagRecIdPairs->Insert(key, value);
       
   442                         }
       
   443                         
       
   444                         CleanupStack::PopAndDestroy(tempForHashSearch);
       
   445                         //tempForHashSearch.Zero();
       
   446                     }
       
   447                 }
       
   448             actField.Set(TPtrC(LinkRecordsArray[aTableIndex][++i]));
       
   449             }
       
   450             
       
   451         firstRec = EFalse;
       
   452         }
       
   453     }
       
   454 
       
   455 /**
       
   456  if the param is 'Link.TableName.someId' format returns the position of the first
       
   457  '.' which is after the 'Link' word. Else returns 0
       
   458 */
       
   459 TInt LinkByTagResolver::SearchForLink(const TPtrC& aSetting)
       
   460     {
       
   461     _LIT(linkTag, "Link");
       
   462     
       
   463     //almost the same code as in DBAccess::SetLinkedRecord
       
   464     const TUint KTableColumnSeperator = '.';
       
   465 	TInt pos = aSetting.Locate(TChar(KTableColumnSeperator));
       
   466 	
       
   467 	if (pos != KErrNotFound) 
       
   468 	    {
       
   469 	    TBuf<KCommsDbSvrMaxColumnNameLength> buffer = aSetting.Left(pos);
       
   470 	    
       
   471 	    if(buffer == TPtrC(linkTag))
       
   472 	        {
       
   473 	        return pos;
       
   474 	        }
       
   475 	    }
       
   476 	
       
   477 	return 0;
       
   478     }
       
   479 
       
   480 
       
   481 void LinkByTagResolver::ResolveLinkToRecIdL(const TPtrC& aSetting, TBuf<MAX_BUFFER_LEN>& aResolvedLink)
       
   482     {
       
   483     TInt i = SearchForLink(aSetting);
       
   484     TPtrC tableName = aSetting.Mid(i+1); //now the tableName is TableName.someId
       
   485     
       
   486     //the Id setting from the linked record
       
   487     TPtrC idTagSetting;
       
   488     TPtrC recordId;
       
   489     
       
   490     _LIT(idTag, "Id");
       
   491     _LIT(commdID, "COMMDB_ID ");
       
   492     
       
   493     //now search for the next '.'
       
   494     const TUint KTableColumnSeperator = '.';
       
   495 	TInt pos = tableName.Locate(TChar(KTableColumnSeperator));
       
   496 	if (pos != KErrNotFound)
       
   497 	    {
       
   498 	    TPtrC idTagFromLink = tableName.Right(tableName.Length()-pos-1);
       
   499 	    tableName.Set(tableName.Left(pos));
       
   500 	    
       
   501 	    //ok, got the table name and the idTag. Now the tableName should be searched for.
       
   502 	    //As this cannot be a template record we use just the 'OpenAddBlock' method.
       
   503 
       
   504 	    TBool idTagFound = EFalse;	    
       
   505         if (iConfigFile->OpenAddBlock(tableName))
       
   506         //got it, search for the 'Id' fields in the record
       
   507             {
       
   508             TBool firstRec = ETrue;
       
   509             
       
   510     	    while ( firstRec || (!idTagFound && iConfigFile->StepToNextBlock()) )
       
   511                 {
       
   512                 if (KErrNone == iConfigFile->GetSetting(idTag, idTagSetting))
       
   513                     {
       
   514                     if (idTagFromLink == idTagSetting)
       
   515                     //found the record with the given Id field. Read the record id of it
       
   516                         {
       
   517                         if (KErrNone == iConfigFile->GetSetting(commdID, recordId))
       
   518                             {
       
   519                             idTagFound = ETrue;
       
   520                             
       
   521                             aResolvedLink.Append(tableName);
       
   522                             aResolvedLink.Append('.');
       
   523                             aResolvedLink.Append(recordId);
       
   524                             }
       
   525                         }
       
   526                     }
       
   527                 firstRec = EFalse;
       
   528                 }
       
   529             }
       
   530             
       
   531 	    if (!idTagFound)
       
   532 	        {
       
   533 	        //The linking should be valid
       
   534 	        User::Leave(KErrNotFound);
       
   535 	        }
       
   536 	    }
       
   537 	else
       
   538 	    {
       
   539 	    //The linking should be valid
       
   540 	    User::Leave(KErrNotFound);
       
   541 	    }
       
   542     }
       
   543 
       
   544 const HBufC* LinkByTagResolver::ResolvedIdForTagLink(const TPtrC& aLinkingString/*, TDesC& aTheResolvedPair*/)
       
   545     {
       
   546     HBufC* tempForHashSearch = aLinkingString.Alloc();
       
   547     CleanupStack::PushL(tempForHashSearch);
       
   548     
       
   549     //this will point to an element in the RPtrHashMap, so not needed to push
       
   550     //it on the CleanupStack
       
   551     HBufC* resolvedLink;
       
   552     
       
   553     if ( NULL == (resolvedLink = iLinkByTagRecIdPairs->Find(*tempForHashSearch)) )
       
   554     /**
       
   555     hm... Normaly we should leave here but this would cause the rewrite of the 
       
   556     DBAccess::SetLinkedRecord method which is a non leaving method. On the 
       
   557     other side TRAPPing would be very expensive as this method is called from 
       
   558     a loop...
       
   559     So we return the last element of the RPtrHashMap which will be an empty 
       
   560     descriptior.
       
   561     */
       
   562         {
       
   563         TPtrHashMapIter<HBufC, HBufC > iter(*iLinkByTagRecIdPairs);
       
   564         
       
   565         //iterating at the end of the hashMap...
       
   566         for (TInt i = 0; i < iLinkByTagRecIdPairs->Count(); ++i)
       
   567             {
       
   568             iter.NextValue();
       
   569             }
       
   570             
       
   571         //return the empty string...
       
   572         //return const_cast<HBufC*>(iter.CurrentKey())->Des();
       
   573         return const_cast<HBufC*>(iter.CurrentKey());
       
   574         
       
   575         //aTheResolvedPair(const_cast<HBufC*>(iter.CurrentKey())->Des());
       
   576         }
       
   577         
       
   578     CleanupStack::PopAndDestroy(tempForHashSearch);
       
   579     
       
   580     //return resolvedLink->Des();
       
   581     return resolvedLink;
       
   582     //aTheResolvedPair(resolvedLink->Des());
       
   583     }
       
   584 
       
   585 //EOF