1    	// @(#)root/table:$Name:  $:$Id: StFileIter.cxx,v 1.6 2009/03/27 17:40:16 fine Exp $
2    	// Author: Valery Fine(fine@bnl.gov)   01/03/2001
3    	
4    	/*************************************************************************
5    	 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
6    	 * Copyright (C) 2001 [BNL] Brookhaven National Laboratory.              *
7    	 * All rights reserved.                                                  *
8    	 *                                                                       *
9    	 * For the licensing terms see $ROOTSYS/LICENSE.                         *
10   	 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
11   	 *************************************************************************/
12   	
13   	///////////////////////////////////////////////////////////////////////////
14   	//                                                                       //
15   	// Class to iterate (read / write ) the events written to TFile.         //
16   	// The event is supposed to assign an unique ID in form of               //
17   	//                                                                       //
18   	//  TKey <event Id> ::= eventName "." run_number "." event_number        //
19   	//                                                                       //
20   	// and stored as the TKey name of the object written                     //
21   	//                                                                       //
22   	//        ///////        //////////      ////////        ///////     //////
23   	//
24   	// void TesStFileIter(){
25   	// // This macros tests the various methods of StFileIter class.
26   	//   gSystem->Load("libTable");
27   	//
28   	//   //First create simple ROOT file
29   	//   TDataSet *ds = new TDataSet("event");
30   	//   TObject *nextObject = 0;
31   	//   TRandom run;
32   	//   TRandom event;
33   	//   {
34   	//     StFileIter outSet("test.root","RECREATE");
35   	//     UInt_t totalEvent = 10;
36   	//     UInt_t runNumber  = 20010301;
37   	//     Int_t i=0;
38   	//     Int_t j=0;
39   	//     for (;j < 10;j++) {
40   	//       for (i = 1;i<totalEvent;i++) {
41   	//         outSet.NextEventPut(ds,UInt_t(i),UInt_t(runNumber+j+10*run.Rndm()-5));
42   	//       }
43   	//     }
44   	//   }
45   	//   printf(" ----------------------> TFile has been created <--------------------\n");
46   	//   TFile *f = new TFile("test.root");
47   	//   StFileIter readObj(f);
48   	//   // the number of the object available directly from "MyDataSet.root"
49   	//   Int_t size = readObj.TotalKeys();
50   	//   printf(" The total number of the objects: %d\n",size);
51   	//
52   	//   //-----------------------------------------------------------------------
53   	//   // Loop over all objects, read them in to memory one by one
54   	//
55   	//   printf(" -- > Loop over all objects, read them in to memory one by one < -- \n");
56   	//   for( readObj = 0; int(readObj) < size; ++readObj){
57   	//       nextObject = *readObj;
58   	//       printf(" %d bytes of the object \"%s\" of class \"%s\" written with TKey \"%s\"  has been read from file\n"
59   	//                ,readObj.GetObjlen()
60   	//                ,nextObject->GetName()
61   	//                ,nextObject->IsA()->GetName()
62   	//                ,(const char *)readObj
63   	//             );
64   	//       delete nextObject;
65   	//  }
66   	// //-----------------------------------------------------------------------
67   	// //  Now loop over all objects in inverse order
68   	//  printf(" -- > Now loop over all objects in inverse order < -- \n");
69   	//  for( readObj = size-1; (int)readObj >= 0; --readObj)
70   	//  {
71   	//       nextObject = *readObj;
72   	//       if (nextObject) {
73   	//          printf(" Object \"%s\" of class \"%s\" written with TKey \"%s\"  has been read from file\n"
74   	//                 ,nextObject->GetName()
75   	//                 , nextObject->IsA()->GetName()
76   	//                 ,(const char *)readObj
77   	//                );
78   	//         delete nextObject;
79   	//      } else {
80   	//        printf("Error reading file by index\n");
81   	//      }
82   	//  }
83   	// //-----------------------------------------------------------------------
84   	// // Loop over the objects starting from the object with the key name "event.02.01"
85   	//   printf(" -- > Loop over the objects starting from the object with the key name \"event.02.01\" < -- \n");
86   	//   for( readObj = "event.02.01"; (const char *)readObj != 0; ++readObj){
87   	//       nextObject = *readObj;
88   	//       printf(" Object \"%s\" of class \"%s\" written with Tkey \"%s\"  has been read from file\n"
89   	//               , nextObject->GetName()
90   	//               , nextObject->IsA()->GetName()
91   	//               , (const char *)readObj
92   	//             );
93   	//       delete nextObject;
94   	//   }
95   	//
96   	//   printf(" -- > Loop over the objects starting from the 86-th object" < -- \n");
97   	//   for( readObj = (const char *)(readObj = 86); (const char *)readObj != 0; ++readObj){
98   	//       nextObject = *readObj;
99   	//       printf(" Object \"%s\" of class \"%s\" written with Tkey \"%s\"  has been read from file\n"
100  	//               , nextObject->GetName()
101  	//               , nextObject->IsA()->GetName()
102  	//               , (const char *)readObj
103  	//             );
104  	//       delete nextObject;
105  	//   }
106  	//
107  	// }
108  	//-----------------------------------------------------------------------
109  	///////////////////////////////////////////////////////////////////////////
110  	
111  	
112  	#include <assert.h>
113  	
114  	#include "TEnv.h"
115  	#include "TSystem.h"
116  	#include "TFile.h"
117  	#include "TKey.h"
118  	
119  	#include "StFileIter.h"
120  	#include "TDsKey.h"
121  	
122  	ClassImp(StFileIter)
123  	
124  	//__________________________________________________________________________
125  	StFileIter::StFileIter(TFile *file) : fFileBackUp(0),fDirectoryBackUp(0), fNestedIterator(0)
126  	         , fRootFile(file)
127  	         , fEventName("event"), fRunNumber(UInt_t(-1)),fEventNumber(UInt_t(-1))
128  	         , fCursorPosition(-1),  fOwnTFile(kFALSE)
129  	{ 
130  	   // Create iterator over all objects from the TFile provided
131  	   Initialize(); 
132  	}
133  	
134  	//__________________________________________________________________________
135  	StFileIter::StFileIter(TDirectory *directory) :  fFileBackUp(0),fDirectoryBackUp(0), fNestedIterator(0)
136  	         , fRootFile(directory)
137  	         , fEventName("event"), fRunNumber(UInt_t(-1)),fEventNumber(UInt_t(-1))
138  	         , fCursorPosition(-1),  fOwnTFile(kFALSE)
139  	{ 
140  	   // Create iterator over all objects from the TDirectory provided
141  	   Initialize(); 
142  	}
143  	//__________________________________________________________________________
144  	StFileIter::StFileIter(const char *name, Option_t *option, const char *ftitle
145  	                     , Int_t compress, Int_t /*netopt*/) :fNestedIterator(0),fRootFile (0)
146  	{
147  	   // Open ROOT TFile by the name provided;
148  	   // This TFile is to be deleted by the StFileIter alone
149  	   if (name && name[0]) {
150  	      fOwnTFile = kTRUE;
151  	      // Map a special file system to rfio
152  	      //   /hpss/in2p3.fr/group/atlas/cppm/data/genz
153  	      //   #setenv HPSSIN bnlhpss:/home/atlasgen/evgen
154  	      // #example for castor:   /castor/cern.ch/user/p/paniccia/evgen
155  	      fRootFile = TFile::Open(MapName(name),option,ftitle,compress);
156  	      Initialize();
157  	   }
158  	}
159  	
160  	//__________________________________________________________________________
161  	StFileIter::StFileIter(const StFileIter &dst) : TListIter()
162  	          ,fFileBackUp(0),  fDirectoryBackUp(0), fNestedIterator(0)
163  	          ,fRootFile(dst.fRootFile),fEventName(dst.fEventName), fRunNumber(dst.fRunNumber)
164  	          ,fEventNumber(dst.fRunNumber),
165  	           fCursorPosition(-1),  fOwnTFile(dst.fOwnTFile)
166  	{
167  	   // Copy ctor can be used with the "read only" files only.
(1) Event deref_ptr_in_call: Dereferencing pointer "this->fRootFile". (The dereference happens because this is a virtual function call.)
Also see events: [check_after_deref]
168  	   assert(!fRootFile->IsWritable());
(2) Event check_after_deref: Null-checking "this->fRootFile" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.
Also see events: [deref_ptr_in_call]
169  	   if (fRootFile && fOwnTFile && !fRootFile->IsWritable()) {
170  	      // Reopen the file
171  	      if (fRootFile->InheritsFrom(TFile::Class())) 
172  	      {
173  	         TFile *thisFile = (TFile *)fRootFile;
174  	         fRootFile = TFile::Open(MapName(fRootFile->GetName())
175  	            ,fRootFile->GetOption()
176  	            ,fRootFile->GetTitle()
177  	            ,thisFile->GetCompressionLevel());
178  	      }
179  	   }
180  	
181  	   Initialize();
182  	   // Adjust this iterator position
183  	   SkipObjects(dst.fCursorPosition);
184  	}
185  	//__________________________________________________________________________
186  	StFileIter::~StFileIter()
187  	{
188  	   // StFileIter dtor
189  	   StFileIter *deleteit = fNestedIterator; fNestedIterator = 0;
190  	   delete deleteit;
191  	   if (fRootFile && fOwnTFile ) {  // delete own TFile if any
192  	      if (fRootFile->IsWritable()) fRootFile->Write();
193  	      fRootFile->Close();
194  	      delete fRootFile;
195  	      fRootFile = 0;
196  	   }
197  	}
198  	
199  	//__________________________________________________________________________
200  	void StFileIter::Initialize()
201  	{
202  	   //to be documented
203  	   if (fRootFile) {
204  	      fDirection =  kIterForward;
205  	      if (IsOpen()) Reset();
206  	      else  {
207  	         if (fRootFile && fOwnTFile ) delete fRootFile;
208  	         fRootFile = 0;
209  	      }
210  	   }
211  	}
212  	//__________________________________________________________________________
213  	Bool_t  StFileIter::IsOpen() const
214  	{
215  	   Bool_t iOpen = kFALSE;
216  	   if (fRootFile && !fRootFile->IsZombie() ) {
217  	      iOpen = kTRUE;
218  	      if (fRootFile->InheritsFrom(TFile::Class()) && !((TFile*)fRootFile)->IsOpen()) 
219  	         iOpen = kFALSE;
220  	   }
221  	   return iOpen;
222  	}
223  	
224  	//__________________________________________________________________________
225  	TKey *StFileIter::GetCurrentKey() const
226  	{
227  	  // return the pointer to the current TKey
228  	   
229  	   return ((StFileIter*)this)->SkipObjects(0);
230  	}
231  	//__________________________________________________________________________
232  	Int_t StFileIter::GetDepth() const
233  	{
234  	   // return the current number of the nested subdirectroies;
235  	   //      = 0 - means there is no subdirectories
236  	   return fNestedIterator ? fNestedIterator->GetDepth()+1 : 0;
237  	}
238  	
239  	//__________________________________________________________________________
240  	const char *StFileIter::GetKeyName() const
241  	{
242  	   // return the name of the current TKey
243  	   const char *name = 0;
244  	   TKey *key  = GetCurrentKey();
245  	   if (key) name = key->GetName();
246  	   return name;
247  	}
248  	//__________________________________________________________________________
249  	TObject *StFileIter::GetObject() const
250  	{
251  	  // read the object from TFile defined by the current TKey
252  	  //
253  	  // ATTENTION:  memory leak danger !!!
254  	  // ---------
255  	  // This method does create a new object and it is the end-user
256  	  // code responsibility to take care about this object
257  	  // to avoid memory leak.
258  	  //
259  	   return ReadObj(GetCurrentKey());
260  	}
261  	//__________________________________________________________________________
262  	Int_t StFileIter::GetObjlen() const
263  	{
264  	   // Returns the uncompressed length of the current object
265  	   Int_t lenObj = 0;
266  	   TKey *key = GetCurrentKey();
267  	   if (key) lenObj = ((TKey *)key)->GetObjlen();
268  	   return lenObj;
269  	}
270  	//__________________________________________________________________________
271  	Int_t StFileIter::TotalKeys() const
272  	{
273  	  // The total number of the TKey keys in the current TDirectory only
274  	  // Usually this means the total number of different objects
275  	  // those can be read one by one.
276  	  // It does NOT count the nested sub-TDirectory. 
277  	  // It is too costly and it can be abused.
278  	   
279  	   Int_t size = 0;
280  	   if(fList) size +=  fList->GetSize();
281  	   return size;
282  	}
283  	//__________________________________________________________________________
284  	TObject *StFileIter::Next(Int_t  nSkip)
285  	{
286  	  // return the pointer to the object defined by next TKey
287  	  // This method is not recommended. It was done for the sake
288  	  // of the compatibility with TListIter
289  	
290  	   SkipObjects(nSkip);
291  	   return GetObject();
292  	}
293  	
294  	//__________________________________________________________________________
295  	void StFileIter::PurgeKeys(TList *listOfKeys) {
296  	  assert(listOfKeys);
297  	  // Remove the TKey duplication,
298  	  // leave the keys with highest cycle number only
299  	  // Sort if first
300  	  listOfKeys->Sort();
301  	  TObjLink *lnk   = listOfKeys->FirstLink();
302  	  while(lnk) {
303  	     TKey *key = (TKey *)lnk->GetObject();
304  	     Short_t cycle = key->GetCycle(); 
305  	     const char *keyName = key->GetName();
306  	     // Check next object
307  	     lnk = lnk->Next();
308  	     if (lnk) {
309  	        TKey *nextkey = 0;
310  	        TObjLink *lnkThis = lnk;
311  	        while (     lnk
312  	             &&   (nextkey = (TKey *)lnk->GetObject()) 
313  	             &&  !strcmp(nextkey->GetName(), keyName) 
314  	            ) {
315  	            // compare the cycles
316  	            Short_t nextCycle = nextkey->GetCycle() ;
317  	            //printf(" StFileIter::PurgeKeys found new cycle %s :%d : %d\n",
318  	            //      keyName,cycle ,nextCycle);
319  	            assert(cycle != nextCycle);
320  	            TObjLink *lnkNext = lnk->Next();
321  	            if (cycle > nextCycle ) { 
322  	               delete listOfKeys->Remove(lnk);
323  	            } else {
324  	               delete listOfKeys->Remove(lnkThis);
325  	               cycle   = nextCycle;
326  	               lnkThis = lnk;
327  	            } 
328  	            lnk = lnkNext;
329  	         }
330  	      } 
331  	   }
332  	}
333  	
334  	//__________________________________________________________________________
335  	void StFileIter::Reset()
336  	{
337  	   // Reset the status of the iterator
338  	   if (fNestedIterator) { 
339  	      StFileIter *it = fNestedIterator; 
340  	      fNestedIterator=0;
341  	      delete it;
342  	   }
343  	   TListIter::Reset();
344  	   if (!fRootFile->IsWritable()) {
345  	      TList *listOfKeys = fRootFile->GetListOfKeys();
346  	      if (listOfKeys) {
347  	         if (!listOfKeys->IsSorted()) PurgeKeys(listOfKeys);
348  	         fList = listOfKeys;
349  	         if (fDirection == kIterForward) {
350  	            fCursorPosition = 0;
351  	            fCurCursor = fList->FirstLink();
352  	            if (fCurCursor) fCursor = fCurCursor->Next();
353  	         } else {
354  	            fCursorPosition = fList->GetSize()-1;
355  	            fCurCursor = fList->LastLink();
356  	            if (fCurCursor) fCursor = fCurCursor->Prev();
357  	         }
358  	      }
359  	   }
360  	}
361  	//__________________________________________________________________________
362  	void StFileIter::SetCursorPosition(const char *keyNameToFind)
363  	{
364  	   // Find the key by the name provided
365  	   Reset();
366  	   while( (*this != keyNameToFind) && SkipObjects() );
367  	}
368  	//__________________________________________________________________________
369  	TKey *StFileIter::SkipObjects(Int_t  nSkip)
370  	{
371  	 //
372  	 // Returns the TKey pointer to the nSkip TKey object from the current one
373  	 // nSkip = 0; the state of the iterator is not changed
374  	 //
375  	 // nSkip > 0; iterator skips nSkip objects in the container.
376  	 //            the direction of the iteration is
377  	 //            sign(nSkip)*kIterForward
378  	 //
379  	 // Returns: TKey that can be used to fetch the object from the TDirectory
380  	 //
381  	   TKey *nextObject  = fNestedIterator ? fNestedIterator->SkipObjects(nSkip): 0;
382  	   if (!nextObject) {
383  	      if (fNestedIterator) {
384  	         StFileIter *it = fNestedIterator;
385  	         fNestedIterator = 0;
386  	         delete it;
387  	      }
388  	      Int_t collectionSize = 0;
389  	      if (fList && (collectionSize = fList->GetSize())  ) {
390  	         if (fDirection !=kIterForward) nSkip = -nSkip;
391  	         Int_t newPos = fCursorPosition + nSkip;
392  	         if (0 <= newPos && newPos < collectionSize) {
393  	            do {
394  	               if (fCursorPosition < newPos) {
395  	                  fCursorPosition++;
396  	                  fCurCursor = fCursor;
397  	                  fCursor    = fCursor->Next();
398  	               } else if (fCursorPosition > newPos) {
399  	                  fCursorPosition--;
400  	                  fCurCursor = fCursor;
401  	                  fCursor    = fCursor->Prev();
402  	               }
403  	            } while (fCursorPosition != newPos);
404  	            if (fCurCursor) nextObject = dynamic_cast<TKey *>(fCurCursor->GetObject());
405  	         } else  {
406  	            fCurCursor = fCursor = 0;
407  	            if (newPos < 0) {
408  	               fCursorPosition = -1;
409  	               if (fList) fCursor = fList->FirstLink();
410  	            } else  {
411  	               fCursorPosition = collectionSize;
412  	               if (fList) fCursor = fList->LastLink();
413  	            }
414  	         }
415  	      }
416  	   }
417  	   return nextObject;
418  	}
419  	//__________________________________________________________________________
420  	TKey *StFileIter::NextEventKey(UInt_t eventNumber, UInt_t runNumber, const char *name)
421  	{
422  	
423  	   // Return the key that name matches the "event" . "run number" . "event number" schema
424  	   // Attention: Side effect: Been called from the end-user code this method causes
425  	   // =========  the  StFileIter::NextObjectGet() to get the "next" object, 
426  	   //            defined by "next" key. 
427  	
428  	   Bool_t reset = kFALSE;
429  	   if (name && name[0] && name[0] != '*') { if (fEventName > name) reset = kTRUE; fEventName   = name; }
430  	   if (runNumber   !=UInt_t(-1) ) { if (fRunNumber > runNumber)     reset = kTRUE; fRunNumber   = runNumber;}
431  	   if (eventNumber !=UInt_t(-1) ) { if (fEventNumber > eventNumber) reset = kTRUE; fEventNumber = eventNumber;}
432  	
433  	   if (reset) Reset();
434  	   //   TIter &nextKey = *fKeysIterator;
435  	   TKey *key = 0;
436  	   TDsKey thisKey;
437  	   while ( (key = SkipObjects()) ) {
438  	      if (fDirection==kIterForward) fCursorPosition++;
439  	      else                          fCursorPosition--;
440  	      if ( strcmp(name,"*") )  {
441  	         thisKey.SetKey(key->GetName());
442  	         if (thisKey.GetName() < name)  continue;
443  	         if (thisKey.GetName() > name) { key = 0; break; }
444  	      }
445  	      // Check "run number"
446  	      if (runNumber != UInt_t(-1)) {
447  	         UInt_t thisRunNumber = thisKey.RunNumber();
448  	         if (thisRunNumber < runNumber) continue;
449  	         if (thisRunNumber < runNumber) { key = 0; break; }
450  	      }
451  	      // Check "event number"
452  	      if (eventNumber != UInt_t(-1)) {
453  	         UInt_t thisEventNumber = thisKey.EventNumber();
454  	         if (thisEventNumber < eventNumber) continue;
455  	         if (thisEventNumber > eventNumber) {key = 0; break; }
456  	      }
457  	      break;
458  	   }
459  	   return key;
460  	}
461  	//__________________________________________________________________________
462  	TObject *StFileIter::NextEventGet(UInt_t eventNumber, UInt_t runNumber, const char *name)
463  	{
464  	  // reads, creates and returns the object by TKey name that matches
465  	  // the "name" ."runNumber" ." eventNumber" schema
466  	  // Attention: This method does create a new TObject and it is the user
467  	  // code responsibility to take care (delete) this object to avoid
468  	  // memory leak.
469  	
470  	   return ReadObj(NextEventKey(eventNumber,runNumber,name));
471  	}
472  	
473  	//__________________________________________________________________________
474  	TObject *StFileIter::ReadObj(const TKey *key)  const
475  	{
476  	   //Read the next TObject from for the TDirectory by TKey provided
477  	   TObject *obj = 0;
478  	   if (fNestedIterator) obj = fNestedIterator->ReadObj(key);
479  	   else if (key)  {
480  	      obj = ((TKey *)key)->ReadObj();
481  	      if (obj && obj->InheritsFrom(TDirectory::Class()) ) 
482  	      {
483  	         // create the next iteration level.
484  	         assert(!fNestedIterator);
485  	         ((StFileIter*)this)->fNestedIterator = new StFileIter((TDirectory *)obj);
486  	         // FIXME:  needs to set  fDirection if needed 02/11/2007 vf
487  	      }
488  	   }
489  	   return obj;
490  	}
491  	
492  	//__________________________________________________________________________
493  	Int_t  StFileIter::NextEventPut(TObject *obj, UInt_t eventNum,  UInt_t runNumber
494  	                              , const char *name)
495  	{
496  	   // Create a special TKey name with obj provided and write it out.
497  	
498  	   Int_t wBytes = 0;
499  	   if (obj && IsOpen() && fRootFile->IsWritable()) {
500  	      TDsKey thisKey(runNumber,eventNum);
501  	      if (name && name[0])
502  	         thisKey.SetName(name);
503  	      else
504  	         thisKey.SetName(obj->GetName());
505  	
506  	      if (fRootFile != gDirectory) {
507  	         SaveFileScope();
508  	         fRootFile->cd();
509  	      }
510  	      wBytes = obj->Write(thisKey.GetKey());
511  	      if (fRootFile->InheritsFrom(TFile::Class())) ((TFile*)fRootFile)->Flush();
512  	      if (fRootFile != gDirectory)     RestoreFileScope();
513  	   }
514  	   return wBytes;
515  	}
516  	//__________________________________________________________________________
517  	TString StFileIter::MapName(const char *name, const char *localSystemKey,const char *mountedFileSystemKey)
518  	{
519  	   // --------------------------------------------------------------------------------------
520  	   // MapName(const char *name, const char *localSystemKey,const char *mountedFileSystemKey)
521  	   // --------------------------------------------------------------------------------------
522  	   // Substitute the logical name with the real one if any
523  	   // 1. add a line into system.rootrc or ~/.rootrc or ./.rootrc
524  	   //
525  	   //  StFileIter.ForeignFileMap  mapFile // the name of the file
526  	                                         // to map the local name
527  	                                         // to the global file service
528  	   //
529  	   //  If this line is omitted then StFileIter class seeks for
530  	   //  the default mapping file in the current directory "io.config"
531  	
532  	   // 2. If the "io.config" file found then it defines the mapping as follows:
533  	   //
534  	   //  StFileIter.LocalFileSystem   /castor
535  	   //  StFileIter.MountedFileSystem rfio:/castor
536  	
537  	   // If "io.config" doesn't exist then no mapping is to be performed
538  	   // and all file names are treated "as is"
539  	
540  	   if ( !localSystemKey)       localSystemKey       = GetLocalFileNameKey();
541  	   if ( !mountedFileSystemKey) mountedFileSystemKey = GetForeignFileSystemKey();
542  	   TString newName = name;
543  	   TString fileMap = gEnv->GetValue(GetResourceName(),GetDefaultMapFileName());
544  	   const char *localName    = 0;
545  	   const char *foreignName  = 0;
546  	   if ( gSystem->AccessPathName(fileMap) == 0 ){
547  	      TEnv myMapResource(fileMap);
548  	      localName    = myMapResource.Defined(localSystemKey) ?
549  	                                    myMapResource.GetValue(localSystemKey,"") : 0;
550  	      foreignName  = myMapResource.Defined(mountedFileSystemKey) ?
551  	                                    myMapResource.GetValue(mountedFileSystemKey,""):0;
552  	   } else {
553  	      localName    = "/castor";      // This is the default CERN name
554  	      foreignName  = "rfio:/castor"; // and it needs "RFIO"
555  	   }
556  	   if (localName && localName[0]
557  	                 && foreignName
558  	                 && foreignName[0]
559  	                 && newName.BeginsWith(localName) )
560  	      newName.Replace(0,strlen(localName),foreignName);
561  	   return newName;
562  	}
563