Moving a directory to another parent in a directory tree

Dear rooters, I have a directory tree in memory with several nested subdirectories and histograms (it’s created with the example attached below).

#include <iostream>
#include <map>
#include <vector>

#include <TFile>
#include <TH1F>
#include <TH2F>
#include <TH3F>

using namespace std ;

typedef vector<TH1>                 aHistogramList ;
typedef map<string>     aDirectoryMap  ;
typedef map<string>  aHistogramMap  ;

/*
0      1       2       3       4--> directoryLevel
------------------------------------------------- 
data.root
  |
  +--> dirOne			  
          |
          +--> h1
          |
          +--> dirTwo
          |	  |
          |	  +--> h2
          |	  |
          |	  +--> h3
          |	  |
          |	  +--> dirFour
          |       |       |
          |       |       +--> dirSix
          |       |
          |       +--> dirFive
          |               |
          |               +--> h4
          |               | 
          |               +--> h5
          |               |
          |               +--> dirSeven
          |                       |
          |                       +--> h9
          |	 
          +--> dirThree
        	  |
        	  +--> h6
        	  |
        	  +--> h7
        	  |
        	  +--> h8
*/

int main()
{

//  TFile * file = new TFile("data.root", "RECREATE") ;
  TDirectory * file = gROOT ;
  
  aDirectoryMap dirMap ;
  aHistogramMap hisMap ;

  dirMap["dirOne"]   =	           file->mkdir("dirOne",   "First  directory at top level") ;
  dirMap["dirTwo"]   =  dirMap["dirOne"]->mkdir("dirTwo",   "First  directory in dirOne"   ) ;
  dirMap["dirThree"] =  dirMap["dirOne"]->mkdir("dirThree", "Second directory in dirOne"   ) ;
  dirMap["dirFour"]  =  dirMap["dirTwo"]->mkdir("dirFour",  "First  directory in dirTwo"   ) ;
  dirMap["dirFive"]  =  dirMap["dirTwo"]->mkdir("dirFive",  "Second directory in dirTwo"   ) ;
  dirMap["dirSix"]   = dirMap["dirFour"]->mkdir("dirSix",   "First  directory in dirFour"  ) ;
  dirMap["dirSeven"] = dirMap["dirFive"]->mkdir("dirSeven", "First  directory in dirFive"  ) ;
  
  hisMap["dirOne"]  .push_back(new TH1F("h1","h1 in dirOne",   100, 0, 100 )) ;
  hisMap["dirTwo"]  .push_back(new TH1F("h2","h2 in dirTwo",   100, 0, 100 )) ;
  hisMap["dirTwo"]  .push_back(new TH1F("h3","h3 in dirTwo",   100, 0, 100 )) ;
  hisMap["dirFive"] .push_back(new TH1F("h4","h4 in dirFive",  100, 0, 100 )) ;
  hisMap["dirFive"] .push_back(new TH2F("h5","h5 in dirFive",  100, 0, 100, 100, 0, 100 )) ;
  hisMap["dirThree"].push_back(new TH1F("h6","h6 in dirThree", 100, 0, 100 )) ;
  hisMap["dirThree"].push_back(new TH1F("h7","h7 in dirThree", 100, 0, 100 )) ;
  hisMap["dirThree"].push_back(new TH3F("h8","h8 in dirThree",  10, 0, 100, 10, 0, 100, 10, 0, 100 )) ;
  hisMap["dirSeven"].push_back(new TH1F("h9","h9 in dirSeven", 100, 0, 100 )) ;

  for(aHistogramMap::iterator his=hisMap.begin(); his!=hisMap.end(); his++)
  {
    if( dirMap.find(his->first) == dirMap.end() )
    {
      cout << "No "
           <<his>first
           << " directory found to place histograms supposed to belong there"
	   <<endl>first]->cd() ;
    for(aHistogramList::iterator hisPtr=his->second.begin(); hisPtr!=his->second.end(); hisPtr++)
    {
//        (*hisPtr)->Write() ;
	(*hisPtr)->SetDirectory(dirMap[his->first]) ;
    } 
  }

   gROOT->ls() ;
   
   cout << "Swapping " 
         <<dirMap>GetName() 
         << " from "
         <<dirMap>GetMother()->GetName()
         << " to "
         <<dirMap>GetName() 
         <<endl>SetMother(dirMap["dirSix"]) ;
   
   gROOT->ls() ;

   cout << "Now, ancestor of " 
          <<dirMap>GetName() 
          << " is "
          <<dirMap>GetMother()->GetName()
          <<endl>ls() ;

//  file->Close() ;
  
}

I would like to move one directory and all it’s content (subdirectories included) from one directory in the hierarchy to another. If I run the example below, where I (think) reassign the dirThree directory from belonging to the dirOne ancestor to the dirSix, the program apparently does what requested but the gROOT->ls() dump does not show that the hierarchy has been actually reshuffled.

[code] g++ -g -o createFile createFile.cpp root-config --cflags --libs && ./createFile

TROOT* root The ROOT of EVERYTHING
TDirectory* dirOne First directory at top level
TDirectory* dirTwo First directory in dirOne
TDirectory* dirFour First directory in dirTwo
TDirectory* dirSix First directory in dirFour
TDirectory* dirFive Second directory in dirTwo
TDirectory* dirSeven First directory in dirFive
OBJ: TH1F h9 h9 in dirSeven : 0 at: 0x9ae5570
OBJ: TH1F h4 h4 in dirFive : 0 at: 0x9ad8110
OBJ: TH2F h5 h5 in dirFive : 0 at: 0x9ad8630
OBJ: TH1F h2 h2 in dirTwo : 0 at: 0x9ad76d0
OBJ: TH1F h3 h3 in dirTwo : 0 at: 0x9ad7bf0
TDirectory* dirThree Second directory in dirOne
OBJ: TH1F h6 h6 in dirThree : 0 at: 0x9ae2c68
OBJ: TH1F h7 h7 in dirThree : 0 at: 0x9ae3188
OBJ: TH3F h8 h8 in dirThree : 0 at: 0x9ae36a8
OBJ: TH1F h1 h1 in dirOne : 0 at: 0x9ad7198

Swapping dirThree from dirOne to dirSix

TROOT* root The ROOT of EVERYTHING
TDirectory* dirOne First directory at top level
TDirectory* dirTwo First directory in dirOne
TDirectory* dirFour First directory in dirTwo
TDirectory* dirSix First directory in dirFour
TDirectory* dirFive Second directory in dirTwo
TDirectory* dirSeven First directory in dirFive
OBJ: TH1F h9 h9 in dirSeven : 0 at: 0x9ae5570
OBJ: TH1F h4 h4 in dirFive : 0 at: 0x9ad8110
OBJ: TH2F h5 h5 in dirFive : 0 at: 0x9ad8630
OBJ: TH1F h2 h2 in dirTwo : 0 at: 0x9ad76d0
OBJ: TH1F h3 h3 in dirTwo : 0 at: 0x9ad7bf0
TDirectory* dirThree Second directory in dirOne
OBJ: TH1F h6 h6 in dirThree : 0 at: 0x9ae2c68
OBJ: TH1F h7 h7 in dirThree : 0 at: 0x9ae3188
OBJ: TH3F h8 h8 in dirThree : 0 at: 0x9ae36a8
OBJ: TH1F h1 h1 in dirOne : 0 at: 0x9ad7198

Now, ancestor of dirThree is dirSix
[/code]

Even if I save the directory tree to file and reopen it, I do not see the reshuffling effect.
Am I maybe missing something? (This is root version 4.04/02b)

Thanks to everyone answering, as usual…

Dear rooters, I realize now that the code snippet I pasted in my previous note was screwd up (damn clipboard…).

Here it is agian, the correct version

[code]#include
#include
#include

#include
#include
#include
#include

using namespace std ;

typedef vector aHistogramList ;
typedef map aDirectoryMap ;
typedef map aHistogramMap ;

/*
0 1 2 3 4–> directoryLevel

data.root
|
±-> dirOne
|
±-> h1
|
±-> dirTwo
| |
| ±-> h2
| |
| ±-> h3
| |
| ±-> dirFour
| | |
| | ±-> dirSix
| |
| ±-> dirFive
| |
| ±-> h4
| |
| ±-> h5
| |
| ±-> dirSeven
| |
| ±-> h9
|
±-> dirThree
|
±-> h6
|
±-> h7
|
±-> h8
*/

int main()
{

// TFile * file = new TFile(“data.root”, “RECREATE”) ;
TDirectory * file = gROOT ;

aDirectoryMap dirMap ;
aHistogramMap hisMap ;

dirMap[“dirOne”] = file->mkdir(“dirOne”, “First directory at top level”) ;
dirMap[“dirTwo”] = dirMap[“dirOne”]->mkdir(“dirTwo”, “First directory in dirOne” ) ;
dirMap[“dirThree”] = dirMap[“dirOne”]->mkdir(“dirThree”, “Second directory in dirOne” ) ;
dirMap[“dirFour”] = dirMap[“dirTwo”]->mkdir(“dirFour”, “First directory in dirTwo” ) ;
dirMap[“dirFive”] = dirMap[“dirTwo”]->mkdir(“dirFive”, “Second directory in dirTwo” ) ;
dirMap[“dirSix”] = dirMap[“dirFour”]->mkdir(“dirSix”, “First directory in dirFour” ) ;
dirMap[“dirSeven”] = dirMap[“dirFive”]->mkdir(“dirSeven”, “First directory in dirFive” ) ;

hisMap[“dirOne”] .push_back(new TH1F(“h1”,“h1 in dirOne”, 100, 0, 100 )) ;
hisMap[“dirTwo”] .push_back(new TH1F(“h2”,“h2 in dirTwo”, 100, 0, 100 )) ;
hisMap[“dirTwo”] .push_back(new TH1F(“h3”,“h3 in dirTwo”, 100, 0, 100 )) ;
hisMap[“dirFive”] .push_back(new TH1F(“h4”,“h4 in dirFive”, 100, 0, 100 )) ;
hisMap[“dirFive”] .push_back(new TH2F(“h5”,“h5 in dirFive”, 100, 0, 100, 100, 0, 100 )) ;
hisMap[“dirThree”].push_back(new TH1F(“h6”,“h6 in dirThree”, 100, 0, 100 )) ;
hisMap[“dirThree”].push_back(new TH1F(“h7”,“h7 in dirThree”, 100, 0, 100 )) ;
hisMap[“dirThree”].push_back(new TH3F(“h8”,“h8 in dirThree”, 10, 0, 100, 10, 0, 100, 10, 0, 100 )) ;
hisMap[“dirSeven”].push_back(new TH1F(“h9”,“h9 in dirSeven”, 100, 0, 100 )) ;

for(aHistogramMap::iterator his=hisMap.begin(); his!=hisMap.end(); his++)
{
dirMap[his->first]->cd() ;
for(aHistogramList::iterator hisPtr=his->second.begin(); hisPtr!=his->second.end(); hisPtr++)
{
// (*hisPtr)->Write() ;
(*hisPtr)->SetDirectory(dirMap[his->first]) ;
}
}

gROOT->ls() ;

cout << "Swapping "
<GetName()
<< " from "
<GetMother()->GetName()
<< " to "
<GetName()
<SetMother(dirMap[“dirSix”]) ;

gROOT->ls() ;

cout << "Now, ancestor of "
<GetName()
<< " is "
<GetMother()->GetName()
<ls() ;

// file->Close() ;

}
[/code]

Hi Dario,

I understand what you want to do. However your code is not legal C++.
You are missing arguments in your map declarations.
Could you fix your code such that it compiles?

Rene

Dear Rene,
now I know the reason why the code I posted does not compile: I didn’t know I had to turn off the HTML button on the submission form, so the code I posted was incorrect.

Now I post it again (see below). What I 'm basically trying to accomplish is to move one directory (with all its descendants) from one particular point in the directory tree to another one whithout having to explicitly traverse the linked-list of directories with a recursive method.

Here is what I guessed would work, but it doesn’t: where’s the gotcha?

[code]/*____________________________________________________________________________________
g++ -g -o example example.cpp root-config --cflags --libs && ./example


*/
#include
#include
#include

#include <TFile.h>
#include <TH1F.h>
#include <TH2F.h>
#include <TKey.h>
#include <TH3F.h>

using namespace std ;

typedef vector<TH1 *> aHistogramList ;
typedef map<string, TDirectory *> aDirectoryMap ;
typedef map<string, aHistogramList > aHistogramMap ;

aDirectoryMap dirMap ;
aHistogramMap hisMap ;

/*
0 1 2 3 4–> directoryLevel

data.root
|
±-> h0
|
±-> dirOne
|
±-> h1
|
±-> dirTwo
| |
| ±-> h2
| |
| ±-> h3
| |
| ±-> dirFour
| | |
| | ±-> dirSix
| |
| ±-> dirFive
| |
| ±-> h4
| |
| ±-> h5
| |
| ±-> dirSeven
| |
| ±-> h9
|
±-> dirThree
|
±-> h6
|
±-> h7
|
±-> h8
*/
int main()
{

TDirectory * file = gROOT ;

dirMap[“dirZero”] = file;
dirMap[“dirOne”] = file->mkdir(“dirOne”, “First directory at top level”) ;
dirMap[“dirTwo”] = dirMap[“dirOne”]->mkdir(“dirTwo”, “First directory in dirOne” ) ;
dirMap[“dirThree”] = dirMap[“dirOne”]->mkdir(“dirThree”, “Second directory in dirOne” ) ;
dirMap[“dirFour”] = dirMap[“dirTwo”]->mkdir(“dirFour”, “First directory in dirTwo” ) ;
dirMap[“dirFive”] = dirMap[“dirTwo”]->mkdir(“dirFive”, “Second directory in dirTwo” ) ;
dirMap[“dirSix”] = dirMap[“dirFour”]->mkdir(“dirSix”, “First directory in dirFour” ) ;
dirMap[“dirSeven”] = dirMap[“dirFive”]->mkdir(“dirSeven”, “First directory in dirFive” ) ;

hisMap[“dirZero”] .push_back(new TH1F(“h0”,“h0 at top level”,100, 0, 100 )) ;
hisMap[“dirOne”] .push_back(new TH1F(“h1”,“h1 in dirOne”, 100, 0, 100 )) ;
hisMap[“dirTwo”] .push_back(new TH1F(“h2”,“h2 in dirTwo”, 100, 0, 100 )) ;
hisMap[“dirTwo”] .push_back(new TH1F(“h3”,“h3 in dirTwo”, 100, 0, 100 )) ;
hisMap[“dirFive”] .push_back(new TH1F(“h4”,“h4 in dirFive”, 100, 0, 100 )) ;
hisMap[“dirFive”] .push_back(new TH2F(“h5”,“h5 in dirFive”, 100, 0, 100, 100, 0, 100 )) ;
hisMap[“dirThree”].push_back(new TH1F(“h6”,“h6 in dirThree”, 100, 0, 100 )) ;
hisMap[“dirThree”].push_back(new TH1F(“h7”,“h7 in dirThree”, 100, 0, 100 )) ;
hisMap[“dirThree”].push_back(new TH3F(“h8”,“h8 in dirThree”, 10, 0, 100, 10, 0, 100, 10, 0, 100 )) ;
hisMap[“dirSeven”].push_back(new TH1F(“h9”,“h9 in dirSeven”, 100, 0, 100 )) ;

for(aHistogramMap::iterator his=hisMap.begin(); his!=hisMap.end(); his++)
{
for(aHistogramList::iterator hisPtr=his->second.begin(); hisPtr!=his->second.end(); hisPtr++)
{
(*hisPtr)->SetDirectory(dirMap[his->first]) ;
}
}

gROOT->ls() ;

string directoryToMove = “dirThree” ;
string directoryDestination = “dirSix” ;

cout << "\n\nDirectory " << dirMap[directoryToMove]->GetName()
<< " currently descends from " << dirMap[directoryToMove]->GetMother()->GetName() << endl ;

dirMap[directoryToMove]->SetMother(dirMap[directoryDestination]) ;

cout << "\n\nDirectory " << dirMap[directoryToMove]->GetName()
<< " now descends from " << dirMap[directoryToMove]->GetMother()->GetName() << endl << endl;

gROOT->ls() ;

}
[/code]

Below is the output I get: apparently the dirThree directory has been set as child of dirSix, but the gROOT->ls() dump show otherwise.
Any hint?.. Thanks as usual.

[code]TROOT* root The ROOT of EVERYTHING
TDirectory* dirOne First directory at top level
TDirectory* dirTwo First directory in dirOne
TDirectory* dirFour First directory in dirTwo
TDirectory* dirSix First directory in dirFour
TDirectory* dirFive Second directory in dirTwo
TDirectory* dirSeven First directory in dirFive
OBJ: TH1F h9 h9 in dirSeven : 0 at: 0x942d648
OBJ: TH1F h4 h4 in dirFive : 0 at: 0x94201e8
OBJ: TH2F h5 h5 in dirFive : 0 at: 0x9420708
OBJ: TH1F h2 h2 in dirTwo : 0 at: 0x941f7a8
OBJ: TH1F h3 h3 in dirTwo : 0 at: 0x941fcc8
TDirectory* dirThree Second directory in dirOne
OBJ: TH1F h6 h6 in dirThree : 0 at: 0x942ad40
OBJ: TH1F h7 h7 in dirThree : 0 at: 0x942b260
OBJ: TH3F h8 h8 in dirThree : 0 at: 0x942b780
OBJ: TH1F h1 h1 in dirOne : 0 at: 0x941f288
OBJ: TH1F h0 h0 at top level : 0 at: 0x941ed50

Directory dirThree currently descends from dirOne

Directory dirThree now descends from dirSix

TROOT* root The ROOT of EVERYTHING
TDirectory* dirOne First directory at top level
TDirectory* dirTwo First directory in dirOne
TDirectory* dirFour First directory in dirTwo
TDirectory* dirSix First directory in dirFour
TDirectory* dirFive Second directory in dirTwo
TDirectory* dirSeven First directory in dirFive
OBJ: TH1F h9 h9 in dirSeven : 0 at: 0x942d648
OBJ: TH1F h4 h4 in dirFive : 0 at: 0x94201e8
OBJ: TH2F h5 h5 in dirFive : 0 at: 0x9420708
OBJ: TH1F h2 h2 in dirTwo : 0 at: 0x941f7a8
OBJ: TH1F h3 h3 in dirTwo : 0 at: 0x941fcc8
TDirectory* dirThree Second directory in dirOne
OBJ: TH1F h6 h6 in dirThree : 0 at: 0x942ad40
OBJ: TH1F h7 h7 in dirThree : 0 at: 0x942b260
OBJ: TH3F h8 h8 in dirThree : 0 at: 0x942b780
OBJ: TH1F h1 h1 in dirOne : 0 at: 0x941f288
OBJ: TH1F h0 h0 at top level : 0 at: 0x941ed50
[/code]

Dario,

In the current version, there is no way to achieve what you want.
However, Sergey Linev is currently making extensions to TDirectory.
He will investigate if your request is dable in his extensions.

If you use only directories in memory, why don’t you use TFolders instead?

Rene

Hi, Diaro, Rene

Major problem in implementing TDirectory::mv() is I/O. Once directory structure stored in the file, there is complex relationship between mother and child directory and keys list in mother directory. I speak about fSeekDir, fSeekParent, fNbytesName members in TDirectory class. These memebers stored in file and if you want to move the directory to other location, you first should move (recreate) correspondent key, assign fSeekDir and fSeekParent, and than reassign fSeekParent for all child keys. It sounds tricky, but can be done.

I agree with Rene. If you don’t want to save TDirectory structure into the file, use TFolder instead.

Sergey

Thanks for your reply.
I’m actually developing sort of a TBrowser, specialized to a pixel-test stand software: it’s implemented with Qt and QtRoot. I have a complex directory tree and the GUI allows users to move a directroy tree and its content from one location in the hierarchy to another with drag-and drop. The original hierarchy is stored in a file: once the file is opened by the user, the whole stuff is transfered in memory, preserving the directory relationship. The user then does his job, eventually drag-and-dropping things around, finally saving the modified structure in memory back to file (all this involves traversing the hierarchy tree with recursive calls).
The GUI already offers the capability of drag-and-dropping individual or groups of histograms around (using SetDirectory(newDir)) , of adding new directories and moving stuff into it, deleting directories (recursivley) etc…

To specifically answer your remark: I’m not working on stuff on residing on file, I first transfer everything to memory and close the file, then I move stuff around and only in the end I open a new file (or reopen the original one) and write the modified memory structure into it.

Do you mean that during the transfer of directories from file to memory they should actually become folders? I haven’t still investigated this possibility (actually I never tried working with Folders, so I have no experience with them, I will take a look).

Just for information I attach a snapshot of the GUI part that handles the drag-and-drop

Thanks, Dario


Hi, Dario

If I am right, you recreate complete directory/subdirectory structure yourself, when you read data from the file. That means, you manipulate with TDirectory objects, which are not connected with the file, isn’t it? Or you try “dettach” TDirectory from the open file to prevent delete it when file is closed?

In first case you can easily use TFolder instead TDirectory and only when you save your objects into file, recreate directory structure in file again. Be aware, that if you try to save TFolder structure in file, you get one key without possibility to navigate in the file with standard ROOT TBrowser.

By the way. Which version of QtROOT are you using: from BNL or GSI?

Sergey

Thanks Sergey.
I’m using the latest BNL version of QtROOT (works fine…)

Yes, I read the file content and recreate the directory structure present on file in memory and then close the input file. After the user has dragged and dropped stuff around, modifying things in memory only, the memory structure is dumped to file again. Both in reading and writing I recursively traverse the hierarchy (this is standard one-to-may linked-list traversal…)

I will follow your suggestion and look into the TFolders matter.