]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWG/muon/AliMergeableCollection.cxx
guess the run number from the input file path
[u/mrichter/AliRoot.git] / PWG / muon / AliMergeableCollection.cxx
1 /**************************************************************************
2 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3 *                                                                        *
4 * Author: The ALICE Off-line Project.                                    *
5 * Contributors are mentioned in the code where appropriate.              *
6 *                                                                        *
7 * Permission to use, copy, modify and distribute this software and its   *
8 * documentation strictly for non-commercial purposes is hereby granted   *
9 * without fee, provided that the above copyright notice appears in all   *
10 * copies and that both the copyright notice and this permission notice   *
11 * appear in the supporting documentation. The authors make no claims     *
12 * about the suitability of this software for any purpose. It is          *
13 * provided "as is" without express or implied warranty.                  *
14 **************************************************************************/
15
16 // $Id: AliMergeableCollection.cxx 50593 2011-07-14 17:42:28Z martinez $
17
18 ///
19 /// A mergeable object container. 
20 ///
21 /// For each tuple (key1,key2,..,keyN) a (hash)list of mergeable objects is associated.
22 /// Note that key1, key2 (optional), ..., keyN (optional) are strings. 
23 /// Those strings should not contain "/" themselves.
24 ///
25 /// More helper functions might be added in the future (e.g. Project, etc...)
26
27 #include "AliMergeableCollection.h"
28
29 using std::cout;
30 using std::endl;
31 ClassImp(AliMergeableCollection)
32
33 #include "AliLog.h"
34 #include "Riostream.h"
35 #include "TError.h"
36 #include "TFolder.h"
37 #include "TGraph.h"
38 #include "TH1.h"
39 #include "TH2.h"
40 #include "THashList.h"
41 #include "THnSparse.h"
42 #include "TKey.h"
43 #include "TMap.h"
44 #include "TObjArray.h"
45 #include "TObjString.h"
46 #include "TProfile.h"
47 #include "TRegexp.h"
48 #include "TROOT.h"
49 #include "TSystem.h"
50 #include <cassert>
51 #include <vector>
52 #include "TBrowser.h"
53
54 //_____________________________________________________________________________
55 AliMergeableCollection::AliMergeableCollection(const char* name, const char* title) 
56 : TFolder(name,title), fMap(0x0), fMustShowEmptyObject(0), fMapVersion(0), fMessages()
57 {
58   /// Ctor
59 }
60
61 //_____________________________________________________________________________
62 AliMergeableCollection::~AliMergeableCollection()
63 {
64   /// dtor. Note that the map is owner
65   delete fMap;
66 }
67
68 //_____________________________________________________________________________
69 Bool_t 
70 AliMergeableCollection::Adopt(TObject* obj)
71 {
72   /// Adopt a given object at top level (i.e. no key)
73   return InternalAdopt("",obj);
74 }
75
76 //_____________________________________________________________________________
77 Bool_t 
78 AliMergeableCollection::Adopt(const char* identifier, TObject* obj)
79 {
80   /// Adopt a given object, and associate it with pair key
81   TString sidentifier(identifier);
82   if ( ! sidentifier.IsNull() ){
83     if ( ! sidentifier.EndsWith("/") ) sidentifier.Append("/");
84     if ( ! sidentifier.BeginsWith("/") ) sidentifier.Prepend("/");
85   }
86   return InternalAdopt(sidentifier.Data(),obj);
87 }
88
89 //_____________________________________________________________________________
90 Bool_t AliMergeableCollection::Attach(AliMergeableCollection* mc, const char* identifier, Bool_t pruneFirstIfAlreadyExists)
91 {
92   /// Attach an already existing mergeable collection to this one.
93   /// It is attached at level identifier/
94   /// We take ownership of mc
95   /// If identifier is already existing we kill it if pruneFirstIfAlreadyExists is kTRUE
96   /// (and attach mc) otherwise we return kFALSE (and do *not* attach mc)
97   
98   THashList* hlist = dynamic_cast<THashList*>(Map()->GetValue(identifier));
99   
100   if (hlist)
101   {
102     if (!pruneFirstIfAlreadyExists)
103     {
104       AliError(Form("%s already exist. Will not overwrite it.",identifier));
105       return kFALSE;
106     }
107     else
108     {
109       Int_t n = Prune(identifier);
110       if (!n)
111       {
112         AliError(Form("Could not prune pre-existing %s",identifier));
113         return kFALSE;        
114       }
115     }
116   }
117
118   TIter next(mc->fMap);
119   TObjString* str;
120   
121   while ( ( str = static_cast<TObjString*>(next())) )
122   {
123     THashList* hl = dynamic_cast<THashList*>(mc->Map()->GetValue(str->String()));
124     TString newid(Form("/%s%s",identifier,str->String().Data()));
125     newid.ReplaceAll("//","/");                  
126     Map()->Add(new TObjString(newid.Data()),hl);
127   }
128   
129   return kTRUE;
130 }
131
132 //_____________________________________________________________________________
133 void AliMergeableCollection::Browse(TBrowser* b)
134 {
135   /// Create a TFolder structure pointing to our objects, so we
136   /// can be "browsed"
137   
138   if ( !fFolders ) return;
139   
140   TObjArray* ids = SortAllIdentifiers();
141   TIter nextIdentifier(ids);
142   TObjString* str;
143   
144   while ( ( str = static_cast<TObjString*>(nextIdentifier()) ) )
145   {
146     TObjArray* parts = str->String().Tokenize("/");
147     TObjString* s;
148     TIter nextPart(parts);
149     TFolder* base = this;
150     
151     while ( ( s = static_cast<TObjString*>(nextPart())))
152     {
153       TFolder* f = static_cast<TFolder*>(base->TFolder::FindObject(s->String()));
154       if (!f)
155       {
156         f = new TFolder(s->String(),"");
157         base->Add(f);
158       }
159       base = f;
160     }
161     delete parts;
162
163     TList* list = CreateListOfObjectNames(str->String());
164     if (list)
165     {
166       TObjString* oname;
167       TIter nextObject(list);
168       while ( ( oname = static_cast<TObjString*>(nextObject())) )
169       {
170         TObject* o = GetObject(str->String(),oname->String());
171         base->Add(o);
172       }
173     }
174     else
175     {
176       AliError("got list=0x0");
177     }
178     delete list;
179   }
180
181   TList* top = CreateListOfKeys(0);
182   TObjString* stop;
183   TIter nextTop(top);
184   
185   while ( ( stop = static_cast<TObjString*>(nextTop())) )
186   {
187     b->Add(TFolder::FindObject(stop->String()));
188   }
189   
190   delete top;
191   
192   delete ids;
193 }
194
195 //_____________________________________________________________________________
196 void AliMergeableCollection::ClearMessages()
197 {
198   /// clear pending messages
199   fMessages.clear();
200 }
201
202 //_____________________________________________________________________________
203 TIterator*
204 AliMergeableCollection::CreateIterator(Bool_t direction) const
205 {
206   /// Create an iterator (must be deleted by the client)
207   return fMap ? new AliMergeableCollectionIterator(this,direction) : 0x0;
208 }
209
210 //_____________________________________________________________________________
211 AliMergeableCollection*
212 AliMergeableCollection::Clone(const char* name) const
213 {
214   /// Clone this collection.
215   /// We loose the messages.
216   
217   AliMergeableCollection* newone = new AliMergeableCollection(name,GetTitle());
218   
219   newone->fMap = static_cast<TMap*>(fMap->Clone());
220   newone->fMustShowEmptyObject = fMustShowEmptyObject;
221   newone->fMapVersion = fMapVersion;  
222   
223   return newone;
224 }
225
226 //_____________________________________________________________________________
227 void 
228 AliMergeableCollection::Delete(Option_t*)
229 {
230   /// Delete all the objects
231   fMap->DeleteAll();
232   delete fMap;
233   fMap=0x0;
234 }
235
236 //_____________________________________________________________________________
237 TObject* 
238 AliMergeableCollection::FindObject(const char* fullIdentifier) const
239 {
240   /// Find an object by its full identifier.
241   
242   return GetObject(fullIdentifier);
243 }
244
245 //_____________________________________________________________________________
246 TObject* 
247 AliMergeableCollection::FindObject(const TObject *object) const
248 {
249   /// Find an object 
250   AliWarning("This method is awfully inefficient. Please improve it or use FindObject(const char*)");
251   TIter next(CreateIterator());
252   TObject* obj;
253   while ( ( obj=next() ) )
254   {
255     if ( obj->IsEqual(object) ) return obj;
256   }
257   return 0x0;
258 }
259
260
261 //_____________________________________________________________________________
262 TList*
263 AliMergeableCollection::CreateListOfKeys(Int_t index) const
264 {
265   /// Create the list of keys at level index
266   
267   TList* list = new TList;
268   list->SetOwner(kTRUE);
269   
270   TObjArray* ids = SortAllIdentifiers();
271   TIter next(ids);
272   TObjString* str;
273   
274   while ( ( str = static_cast<TObjString*>(next()) ) )
275   {
276     TString oneid = GetKey(str->String().Data(),index,kFALSE);
277     if (oneid.Length()>0 && !list->Contains(oneid))
278     {
279       list->Add(new TObjString(oneid));
280     }
281   }
282   
283   delete ids;
284   return list;
285 }
286
287 //_____________________________________________________________________________
288 TList* 
289 AliMergeableCollection::CreateListOfObjectNames(const char* identifier) const
290 {
291   /// Create list of object names for /key1/key2/key...
292   /// Returned list must be deleted by client
293   
294   TList* listOfNames = new TList;
295   listOfNames->SetOwner(kTRUE);
296   
297   TIter next(Map());
298   TObjString* str;
299   
300   while ( ( str = static_cast<TObjString*>(next()) ) )
301   {
302     TString currIdentifier = str->String();
303     if ( currIdentifier.CompareTo(identifier) ) continue;
304     
305     THashList* list = static_cast<THashList*>(Map()->GetValue(identifier));
306     
307     TIter nextObject(list);
308     TObject* obj;
309     
310     while ( ( obj = nextObject() ) )
311     {
312       listOfNames->Add(new TObjString(obj->GetName()));
313     }    
314   }
315   
316   return listOfNames;
317 }
318
319
320 //_____________________________________________________________________________
321 TString
322 AliMergeableCollection::GetIdentifier(const char* fullIdentifier) const
323 {
324   /// Extract the identifier from the fullIdentifier
325
326   TString identifier;
327   
328   Int_t n = TString(fullIdentifier).CountChar('/')-1;
329   
330   for (Int_t i=0; i < n; ++i)
331   {
332     identifier += "/";
333     identifier += InternalDecode(fullIdentifier,i);
334   }
335   identifier += "/";
336   return identifier;
337 }
338
339 //_____________________________________________________________________________
340 TString
341 AliMergeableCollection::GetKey(const char* identifier, Int_t index, Bool_t idContainsObjName) const
342 {
343   /// Extract the index element of the key pair from the fullIdentifier
344
345   if ( ! idContainsObjName )
346   {
347     TString sidentifier(identifier);
348     sidentifier.Append("/dummy");
349     return InternalDecode(sidentifier.Data(),index);
350   }
351   
352   return InternalDecode(identifier,index);
353 }
354
355 //_____________________________________________________________________________
356 TString
357 AliMergeableCollection::GetObjectName(const char* fullIdentifier) const
358 {
359   /// Extract the object name from an identifier
360   
361   return InternalDecode(fullIdentifier,-1);  
362 }
363
364 //_____________________________________________________________________________
365 TH1*
366 AliMergeableCollection::Histo(const char* fullIdentifier) const
367 {
368   /// Get histogram key1/key2/.../objectName:action
369   /// action is used for 2D histograms :
370   /// might be px for projection along x-axis
371   /// py for projection along y-axis
372   /// pfx for profile along x-axis
373   /// pfy for profile along y-axis
374   
375   TString sfullIdentifier(fullIdentifier);
376   
377   TString fullIdWithoutAction(fullIdentifier);
378   TString action;
379   
380   if ( sfullIdentifier.First(':') != kNPOS )
381   {
382     TObjArray* arr = sfullIdentifier.Tokenize(":");
383   
384     fullIdWithoutAction = static_cast<TObjString*>(arr->At(0))->String();
385   
386     if ( arr->GetLast() > 0 )
387     {
388       action = static_cast<TObjString*>(arr->At(1))->String();
389       action.ToUpper();
390     }
391   
392     delete arr;
393   }
394   
395   Int_t nslashes = sfullIdentifier.CountChar('/');
396   
397   TObject* o(0x0);
398   
399   if (!nslashes)
400   {
401     o = GetObject("", fullIdWithoutAction);    
402   }  
403   else
404   {
405     o = GetObject(GetIdentifier(fullIdWithoutAction).Data(), GetObjectName(fullIdWithoutAction));
406   }
407   
408   return HistoWithAction(fullIdWithoutAction.Data(),o,action);
409 }
410
411 //_____________________________________________________________________________
412 TH1*
413 AliMergeableCollection::Histo(const char* identifier,
414                               const char* objectName) const
415 {
416   TObject* o = GetObject(identifier,objectName);
417   
418   TObjArray* arr = TString(objectName).Tokenize(":");
419   TString action;
420   
421   if ( arr->GetLast() > 0 )
422   {
423     action = static_cast<TObjString*>(arr->At(1))->String();
424     action.ToUpper();
425   }
426   
427   delete arr;
428
429   return HistoWithAction(identifier,o,action);
430 }
431
432
433 //_____________________________________________________________________________
434 TH1*
435 AliMergeableCollection::HistoWithAction(const char* identifier, TObject* o, const TString& action) const
436 {
437   /// Convert o to an histogram if possible, applying a given action if there
438
439   if (!o) return 0x0;
440   
441   if (!o->InheritsFrom("TH1"))
442   {
443     AliError(Form("%s is not an histogram",o->GetName()));
444     return 0x0;
445   }
446   
447   TH2* h2 = dynamic_cast<TH2*>(o);
448   
449   if (h2)
450   {
451     if ( action == "PX" )
452     {
453       return h2->ProjectionX(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
454     }
455     if ( action == "PY" )
456     {
457       return h2->ProjectionY(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
458     }
459     if ( action == "PFX" )
460     {
461       return h2->ProfileX(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
462     }
463     if ( action == "PFY" )
464     {
465       return h2->ProfileY(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
466     }
467   }
468   
469   return static_cast<TH1*>(o);
470 }
471
472
473 //_____________________________________________________________________________
474 TObject* 
475 AliMergeableCollection::GetObject(const char* fullIdentifier) const
476 {
477   /// Get object key1/key2/.../objectName
478   /// Note that no action is allowed for generic objects (only for histograms,
479   /// see Histo() methods)
480   
481   TString sfullIdentifier(fullIdentifier);
482   
483   Int_t nslashes = sfullIdentifier.CountChar('/');
484   
485   if (!nslashes)
486   {
487     return GetObject("", sfullIdentifier);
488   }
489   else
490   {
491     return GetObject(GetIdentifier(fullIdentifier).Data(), GetObjectName(fullIdentifier));
492   }
493 }
494
495
496 //_____________________________________________________________________________
497 TObject* 
498 AliMergeableCollection::GetObject(const char* identifier, 
499                                   const char* objectName) const
500 {
501   /// Get object for (identifier,objectName) triplet
502   
503   TString sidentifier(identifier);
504   if ( ! sidentifier.IsNull() ) {
505     if ( ! sidentifier.BeginsWith("/") ) sidentifier.Prepend("/");
506     if ( ! sidentifier.EndsWith("/") ) sidentifier.Append("/");
507   }
508   return InternalObject(sidentifier.Data(),objectName);
509 }
510
511 //_____________________________________________________________________________
512 TObject* AliMergeableCollection::GetSum(const char* idPattern) const
513 {
514   /// Sum objects
515   /// The pattern must be in the form:
516   /// /key1_1,key1_2,.../key2_1,key2_2,.../.../objectName_1,objectName_2...
517   /// The logical or between patterns separated by commas is taken
518   /// Exact match is required for keys and objectNames
519   
520   TObject* sumObject = 0x0;
521   
522   // Build array of lists of pattern
523   TString idPatternString(idPattern);
524   TObjArray* keyList = idPatternString.Tokenize("/");
525   TObjArray keyMatrix(keyList->GetEntries());
526   keyMatrix.SetOwner();
527   for ( Int_t ikey=0; ikey<keyList->GetEntries(); ikey++ ) {
528     TObjArray* subKeyList = ((TObjString*)keyList->At(ikey))->GetString().Tokenize(",");
529     keyMatrix.AddAt(subKeyList, ikey);
530   }
531   delete keyList;
532   
533   TString debugMsg = "Adding objects:";
534   
535   //
536   // First handle the keys
537   //
538   TIter next(Map());
539   TObjString* str;
540   while ( ( str = static_cast<TObjString*>(next()) ) )
541   {
542     TString identifier = str->String();
543     
544     Bool_t listMatchPattern = kTRUE;
545     for ( Int_t ikey=0; ikey<keyMatrix.GetEntries()-1; ikey++ ) {
546       TString currKey = GetKey(identifier, ikey, kFALSE);
547       Bool_t matchKey = kFALSE;
548       TObjArray* subKeyList = static_cast<TObjArray*> ( keyMatrix.At(ikey) );
549       for ( Int_t isub=0; isub<subKeyList->GetEntries(); isub++ ) {
550         TString subKeyString = static_cast<TObjString*> (subKeyList->At(isub))->GetString();
551         if ( currKey == subKeyString ) {
552           matchKey = kTRUE;
553           break;
554         }
555       } // loop on the list of patterns of each key
556       if ( ! matchKey ) {
557         listMatchPattern = kFALSE;
558         break;
559       }
560     } // loop on keys in the idPattern
561     if ( ! listMatchPattern ) continue;
562     
563     
564     //
565     // Then handle the object name
566     //
567     THashList* list = static_cast<THashList*>(Map()->GetValue(identifier.Data()));
568     
569     TIter nextObj(list);
570     TObject* obj;
571     
572     while ( ( obj = nextObj()) )
573     {
574       TString currKey = obj->GetName();
575       Bool_t matchKey = kFALSE;
576       TObjArray* subKeyList = static_cast<TObjArray*> ( keyMatrix.Last() );
577       for ( Int_t isub=0; isub<subKeyList->GetEntries(); isub++ ) {
578         TString subKeyString = static_cast<TObjString*> (subKeyList->At(isub))->GetString();
579         if ( currKey == subKeyString ) {
580           matchKey = kTRUE;
581           break;
582         }
583       }
584       if ( ! matchKey ) continue;
585       if ( ! sumObject ) sumObject = obj->Clone();
586       else MergeObject(sumObject, obj);
587       debugMsg += Form(" %s%s",identifier.Data(),obj->GetName());
588     } // loop on objects in list
589   } // loop on identifiers in map
590   
591   AliDebug(1,debugMsg.Data());
592   
593   return sumObject;
594 }
595
596 //_____________________________________________________________________________
597 Bool_t AliMergeableCollection::InternalAdopt(const char* identifier, TObject* obj)
598 {
599   /// Adopt an obj
600   
601   if (!obj)
602   {
603     Error("Adopt","Cannot adopt a null object");
604     return kFALSE;
605   }
606   
607   if ( ! obj->IsA()->InheritsFrom(TObject::Class()) ||
608         ! obj->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
609     Error("Adopt","Cannot adopt an object which is not mergeable!"); 
610   }
611   
612   THashList* hlist = 0x0;
613   
614   hlist = static_cast<THashList*>(Map()->GetValue(identifier));
615   
616   if (!hlist)
617   {
618     hlist = new THashList;
619     hlist->SetOwner(kTRUE);
620     Map()->Add(new TObjString(identifier),hlist);
621     hlist->SetName(identifier);
622   }
623   
624   TObject* existingObj = hlist->FindObject(obj->GetName());
625   
626   if (existingObj)
627   {
628     AliError(Form("Cannot adopt an already existing object : %s -> %s",identifier,existingObj->GetName()));
629     return kFALSE;
630   }
631   
632   if ( obj->IsA()->InheritsFrom(TH1::Class()) ) (static_cast<TH1*> ( obj ))->SetDirectory(0);  
633   
634   hlist->AddLast(obj);
635   
636   // invalidate the TFolder structure, if any, so it will
637   // be recomputed next time Browse() is called
638   delete fFolders;
639   fFolders = 0x0;
640   
641   return kTRUE;
642   
643 }
644
645 //_____________________________________________________________________________
646 TString
647 AliMergeableCollection::InternalDecode(const char* identifier, Int_t index) const
648 {
649   /// Extract the index-th element of the identifier (/key1/key2/.../keyN/objectName)
650   /// object is index=-1 (i.e. last)
651   
652   if ( strlen(identifier) > 0 && identifier[0] != '/' ) 
653   {    
654     AliError(Form("identifier %s is malformed (should start with /)",identifier));
655     return "";
656   }
657   
658   std::vector<Int_t> splitIndex;
659   
660   Int_t start(0);
661   TString sidentifier(identifier);
662   
663   while (start < sidentifier.Length())
664   {
665     Int_t pos = sidentifier.Index('/', start);
666     if (pos == kNPOS) break;
667     splitIndex.push_back(pos);
668     start = pos + 1;
669   }
670
671   Int_t nkeys = splitIndex.size() - 1;
672   
673   if ( index >= nkeys )
674   {
675     AliError(Form("Requiring index %i of identifier %s which only have %i",index, identifier, nkeys));
676     return "";
677   }
678
679   if ( index < 0 )
680   {
681     return sidentifier(splitIndex.back()+1,sidentifier.Length()-splitIndex.back()-1);
682   }
683
684   return sidentifier(splitIndex[index]+1,splitIndex[index+1]-splitIndex[index]-1);
685 }
686
687 //_____________________________________________________________________________
688 TObject* 
689 AliMergeableCollection::InternalObject(const char* identifier,
690                                        const char* objectName) const
691 {
692   /// Get object for (identifier,objectName) 
693   
694   if (!fMap) 
695   {
696     return 0x0;
697   }
698   
699   THashList* hlist = static_cast<THashList*>(Map()->GetValue(identifier));
700   if (!hlist) 
701   {
702     TString msg(Form("Did not find hashlist for identifier=%s dir=%s",identifier,gDirectory ? gDirectory->GetName() : "" ));
703     fMessages[msg.Data()]++;
704     return 0x0;
705   }
706   
707   TObject* obj = hlist->FindObject(objectName);
708   if (!obj)
709   {
710     TString msg(Form("Did not find objectName=%s in %s",objectName,identifier));
711     fMessages[msg.Data()]++;
712   }
713   return obj;
714 }
715
716
717 //_____________________________________________________________________________
718 Bool_t AliMergeableCollection::IsEmptyObject(TObject* obj) const
719 {
720   /// Check if object is empty
721   /// (done only for TH1, so far)
722     
723   if ( obj->IsA()->InheritsFrom(TH1::Class()) ) {
724     TH1* histo = static_cast<TH1*> (obj);
725     if ( histo->GetEntries() == 0 ) return kTRUE;
726   }
727
728   return kFALSE;
729
730 }
731
732
733 //_____________________________________________________________________________
734 TMap* AliMergeableCollection::Map() const
735 {
736   /// Wrapper to insure proper key formats (i.e. new vs old)
737   
738   if (!fMap)
739   {
740     fMap = new TMap;
741     fMap->SetOwnerKeyValue(kTRUE,kTRUE);
742     fMapVersion = 1;
743   }
744   else
745   {
746     if ( fMapVersion < 1 ) 
747     {
748       AliInfo("Remapping");
749       // change the keys
750       TIter next(fMap);
751       TObjString* str;
752       
753       while ( ( str = static_cast<TObjString*>(next()) ) )
754       {
755         if ( str->String().Contains("./") )
756         {
757           TString newkey(str->String());
758           
759           newkey.ReplaceAll("./","");
760           
761           TObject* o = fMap->GetValue(str);
762           
763           TPair* p = fMap->RemoveEntry(str);
764           if (!p)
765           {
766             AliError("oups oups oups");
767             return 0x0;
768           }
769           
770           fMap->Add(new TObjString(newkey.Data()),o);
771           
772           delete p;
773         }
774       }
775       
776       fMapVersion = 1;
777     }
778   }
779   
780   return fMap;
781 }
782
783 //_____________________________________________________________________________
784 Long64_t
785 AliMergeableCollection::Merge(TCollection* list)
786 {
787   // Merge a list of AliMergeableCollection objects with this
788   // Returns the number of merged objects (including this).
789   
790   if (!list) return 0;
791   
792   if (list->IsEmpty()) return 1;
793   
794   TIter next(list);
795   TObject* currObj;
796   TList mapList;
797   Int_t count(0);
798   
799   while ( ( currObj = next() ) )
800   {
801     AliMergeableCollection* mergeCol = dynamic_cast<AliMergeableCollection*>(currObj);
802     if (!mergeCol) {
803       AliFatal(Form("object named \"%s\" is a %s instead of an AliMergeableCollection!", currObj->GetName(), currObj->ClassName()));
804       continue;
805     }
806     
807     ++count;
808     
809     if ( mergeCol->fMap ) mergeCol->Map(); // to insure keys in the new format
810     
811     TIter nextIdentifier(mergeCol->fMap);
812     TObjString* identifier;
813
814     while ( ( identifier = static_cast<TObjString*>(nextIdentifier()) ) )
815     {
816       THashList* otherList = static_cast<THashList*>(mergeCol->fMap->GetValue(identifier->String().Data()));
817
818       TIter nextObject(otherList);
819       TObject* obj;
820       
821       while ( ( obj = nextObject() ) )
822       {
823         TString newid(Form("%s%s",identifier->String().Data(),obj->GetName()));
824         
825         TObject* thisObject = GetObject(newid.Data());
826         
827         if (!thisObject)
828         {
829           AliDebug(1,Form("Adopting a new object = %s%s",identifier->String().Data(),obj->GetName()));
830           
831           Bool_t ok = Adopt(identifier->String(), obj->Clone());
832           
833           if (!ok)
834           {
835             AliError(Form("Adoption of object %s failed",obj->GetName()));
836           }
837         }
838         else
839         {
840           // add it...
841           AliDebug(1,Form("Merging object = %s%s",
842                           identifier->String().Data(),
843                           obj->GetName()));
844           
845           MergeObject(thisObject, obj);
846         }
847       } // loop on objects in map
848     } // loop on identifiers
849   } // loop on collections in list
850          
851   AliDebug(1,Form("count=%d",count));
852   
853   return count+1;
854 }
855
856 //_____________________________________________________________________________
857 Bool_t AliMergeableCollection::MergeObject(TObject* baseObject, TObject* objToAdd)
858 {
859   /// Add objToAdd to baseObject
860   
861   if ( baseObject->IsA()->Class() != objToAdd->IsA()->Class() ) {
862     printf("MergeObject: Cannot add %s to %s", objToAdd->ClassName(), baseObject->ClassName());
863     return kFALSE;
864   }
865   if ( ! baseObject->IsA()->InheritsFrom(TObject::Class()) ||
866       ! baseObject->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
867     printf("MergeObject: Objects are not mergeable!");
868     return kFALSE;
869   }  
870   
871   TList list;
872   list.Add(objToAdd);
873   
874   TString listArgs = Form("((TCollection*)0x%lx)", (ULong_t)&list);
875   Int_t error = 0;
876   baseObject->Execute("Merge", listArgs.Data(), &error);
877   return kTRUE;
878 }
879
880 //_____________________________________________________________________________
881 TString AliMergeableCollection::NormalizeName(const char* identifier,const char* action) const
882 {
883   /// Replace / by _ to build a root-compliant histo name
884   TString name(GetName());
885   
886   name += "_";
887   name += identifier;
888   name += "_";
889   name += action;
890   name.ReplaceAll("/","_");
891   name.ReplaceAll("-","_");
892   return name;
893 }
894
895 //_____________________________________________________________________________
896 Int_t 
897 AliMergeableCollection::NumberOfObjects() const
898 {
899   /// Get the number of objects we hold
900   TIter next(CreateIterator());
901   Int_t count(0);
902   while ( next() ) ++count;
903   return count;
904 }
905
906 //_____________________________________________________________________________
907 Int_t 
908 AliMergeableCollection::NumberOfKeys() const
909 {
910   /// Get the number of keys we have
911   return fMap ? fMap->GetSize() : 0;
912 }
913
914 //_____________________________________________________________________________
915 void 
916 AliMergeableCollection::Print(Option_t* option) const
917 {
918   /// Print all the objects we hold, in a hopefully visually pleasing
919   /// way.
920   ///
921   /// Option can be used to select given part only, using the schema :
922   /// /*/*/*/*/*
923   /// Where the stars are wilcards for /key1/key2/.../objectName
924   ///
925   /// if * is used it is assumed to be a wildcard for objectName
926   ///
927   /// For other selections the full syntax /*/*/*/*/* must be used.
928   /// 
929   /// Use "-" as objectName to disable object's name output
930   ///
931   /// One might also use /*/*/*/*/:classname syntax to restrict
932   /// output to only those objects matching a given classname pattern
933   ///
934   
935   cout << Form("AliMergeableCollection(%s,%s) : %d keys and %d objects",
936                GetName(),GetTitle(),
937                NumberOfKeys(), NumberOfObjects()) << endl;
938   
939   if (!strlen(option)) return;
940   
941   TString soption(option);
942   
943   TObjArray* classes = soption.Tokenize(":");
944   
945   TRegexp* classPattern(0x0);
946   
947   if ( classes->GetLast() > 0 )
948   {
949     TString pat = static_cast<TObjString*>(classes->At(1))->String();
950     classPattern = new TRegexp(pat,kTRUE);
951     soption = static_cast<TObjString*>(classes->At(0))->String();
952   }
953   
954   delete classes;
955
956   TObjArray* select = soption.Tokenize("/");
957   
958   TString sreObjectName(select->Last()->GetName());
959   TRegexp reObjectName(sreObjectName.Data(),kTRUE);
960   
961   TObjArray* identifiers = SortAllIdentifiers();
962   
963   printf("identifiers entries %i\n", identifiers->GetEntries());
964     
965   TIter nextIdentifier(identifiers);
966   
967   TObjString* sid(0x0);
968   
969   while ( ( sid = static_cast<TObjString*>(nextIdentifier()) ) )
970   {
971     Bool_t identifierPrinted(kFALSE);
972
973     TString identifier(sid->String());
974     
975     Bool_t matchPattern = kTRUE;
976     for ( Int_t isel=0; isel<select->GetLast(); isel++ ) {
977       if ( ! GetKey(identifier.Data(), isel, kFALSE).Contains(TRegexp(select->At(isel)->GetName(),kTRUE)) ) {
978         matchPattern = kFALSE;
979         break;
980       }
981     }
982     if ( ! matchPattern ) continue;
983     
984     if ( sreObjectName == "*" && !classPattern)
985     {
986       identifierPrinted = kTRUE;
987       cout << identifier.Data() << endl;
988     }
989       
990     THashList * list = static_cast<THashList*>(Map()->GetValue(sid->String().Data()));
991
992     TObjArray names;
993     names.SetOwner(kTRUE);
994     TIter nextUnsortedObj(list);
995     TObject* obj;
996     while ( ( obj = nextUnsortedObj() ) )
997     {
998       TString cname(obj->ClassName());
999       if ( classPattern && !cname.Contains((*classPattern)) )
1000       {
1001         continue;
1002       }
1003       names.Add(new TObjString(obj->GetName()));
1004     }
1005     names.Sort();
1006     TIter nextObjName(&names);
1007     TObjString* oname;
1008     while ( ( oname = static_cast<TObjString*>(nextObjName()) ) )
1009     {
1010       TString objName(oname->String());
1011       if (objName.Contains(reObjectName) )
1012       {
1013         obj = list->FindObject(objName.Data());
1014         if ( IsEmptyObject(obj) && ! fMustShowEmptyObject ) continue;
1015         
1016         if (!identifierPrinted)
1017         {
1018           cout << identifier.Data() << endl;
1019           identifierPrinted = kTRUE;
1020         }
1021         
1022         TString extra;
1023         TString warning("   ");
1024         
1025         if ( obj->IsA()->InheritsFrom(TH1::Class()) )
1026         {
1027           
1028           TH1* histo = static_cast<TH1*> (obj);
1029           extra.Form("%s | Entries=%d Sum=%g",histo->GetTitle(),Int_t(histo->GetEntries()),histo->GetSumOfWeights());
1030         }
1031         else if ( obj->IsA()->InheritsFrom(TGraph::Class()) )
1032         {
1033           TGraph* graph = static_cast<TGraph*> (obj);
1034           if ( ! TMath::Finite(graph->GetMean(2) ) )
1035           {
1036             warning = " ! ";
1037           }
1038           extra.Form("%s | Npts=%d Mean=%g RMS=%g",graph->GetTitle(),graph->GetN(),
1039                        graph->GetMean(2),graph->GetRMS(2));
1040           
1041         }
1042         
1043         std::cout << Form("    (%s) %s %s", obj->ClassName(),
1044                      warning.Data(),
1045                           obj->GetName());
1046         
1047         if ( extra.Length() )
1048         {
1049           std::cout << " | " << extra.Data();
1050         }
1051         std::cout << std::endl;
1052       }
1053     }
1054     if (!identifierPrinted && sreObjectName=="-" )
1055     { 
1056       // to handle the case where we used objectName="-" to disable showing the objectNames,
1057       // but we still want to see the matching keys maybe...
1058       cout << identifier.Data() << endl;
1059     }
1060   }
1061   
1062   delete select;
1063   
1064   delete identifiers;
1065 }
1066
1067 //_____________________________________________________________________________
1068 void 
1069 AliMergeableCollection::PrintMessages(const char* prefix) const
1070 {
1071   /// Print pending messages
1072   
1073   std::map<std::string,int>::const_iterator it;
1074   
1075   for ( it = fMessages.begin(); it != fMessages.end(); ++it ) 
1076   {
1077     cout << Form("%s : message %s appeared %5d times",prefix,it->first.c_str(),it->second) << endl;
1078   }
1079 }
1080
1081
1082 //_____________________________________________________________________________
1083 UInt_t 
1084 AliMergeableCollection::EstimateSize(Bool_t show) const
1085 {
1086   /// Estimate the memory (in kilobytes) used by some objects
1087
1088 //  For TH1:
1089 //  sizeof(TH1) + (nbins+2)*(nbytes_per_bin) +name+title_sizes 
1090 //  if you have errors add (nbins+2)*8 
1091     
1092   TIter next(CreateIterator());
1093   
1094   TObject* obj;
1095   UInt_t size(0);
1096   
1097   while ( ( obj = next() ) )
1098   {
1099     UInt_t thissize=0;
1100     if ( obj->IsA()->InheritsFrom(TH1::Class()) ) {
1101       TH1* histo = static_cast<TH1*> (obj);
1102       Int_t nbins = (histo->GetNbinsX()+2);
1103     
1104       if (histo->GetNbinsY()>1)
1105       {
1106         nbins *= (histo->GetNbinsY()+2);
1107       }
1108     
1109       if (histo->GetNbinsZ()>1)
1110       {
1111         nbins *= (histo->GetNbinsZ()+2);
1112       }
1113       
1114       Bool_t hasErrors = ( histo->GetSumw2N() > 0 );
1115     
1116       TString cname(histo->ClassName());
1117     
1118       Int_t nbytesPerBin(0);
1119     
1120       if (cname.Contains(TRegexp("C$")) ) nbytesPerBin = sizeof(Char_t);
1121       if (cname.Contains(TRegexp("S$")) ) nbytesPerBin = sizeof(Short_t);
1122       if (cname.Contains(TRegexp("I$")) ) nbytesPerBin = sizeof(Int_t);
1123       if (cname.Contains(TRegexp("F$")) ) nbytesPerBin = sizeof(Float_t);
1124       if (cname.Contains(TRegexp("D$")) ) nbytesPerBin = sizeof(Double_t);
1125         
1126       if (!nbytesPerBin)
1127       {
1128         AliError(Form("Could not get the number of bytes per bin for histo %s of class %s. Thus the size estimate will be wrong !",
1129                       histo->GetName(),histo->ClassName()));
1130         continue;
1131       }
1132     
1133       thissize = sizeof(histo) + nbins*(nbytesPerBin) + strlen(histo->GetName())
1134       + strlen(histo->GetTitle());
1135       
1136       if ( hasErrors) thissize += nbins*8;
1137     }
1138     else if ( obj->IsA()->InheritsFrom(THnSparse::Class()) ) {
1139       THnSparse* sparse = static_cast<THnSparse*> (obj);
1140       thissize = sizeof(Float_t) * (UInt_t)sparse->GetNbins();
1141     }
1142 //    else if ( obj->IsA() == AliCFGridSparse::Class() ) {
1143 //      AliCFGridSparse* sparse = static_cast<AliCFGridSparse*> (obj);
1144 //      thissize = sizeof(Float_t) * (UInt_t)sparse->GetNFilledBins();
1145 //    }
1146 //    else if ( obj->IsA() == AliCFContainer::Class() ) {
1147 //      AliCFContainer* cont = static_cast<AliCFContainer*> (obj);
1148 //      for ( Int_t istep=0; istep<cont->GetNStep(); istep++ ) {
1149 //        thissize += sizeof(Float_t) * (UInt_t)cont->GetGrid(istep)->GetNFilledBins();
1150 //      }
1151 //    }
1152     else {
1153       AliWarning(Form("Cannot estimate size of %s\n", obj->ClassName()));
1154       continue;
1155     }
1156
1157     size += thissize;
1158     
1159     if ( show ) 
1160     {
1161       AliInfo(Form("Size of %30s is %20d bytes",obj->GetName(),thissize));
1162     }
1163   } // loop on objects
1164
1165   return size;
1166 }
1167
1168 //_____________________________________________________________________________
1169 Int_t AliMergeableCollection::Prune(const char* identifier)
1170 {
1171   // Delete all objects which match the beginning of the identifier
1172   // returns the number of entries removed from the Map()
1173   // (not to be confused with the number of leaf objects removed)
1174   //
1175   
1176   TIter next(Map());
1177   TObjString* key;
1178   Int_t ndeleted(0);
1179   
1180   while ( ( key = static_cast<TObjString*>(next())) )
1181   {
1182       if (key->String().BeginsWith(identifier))
1183       {
1184         Bool_t ok = Map()->DeleteEntry(key);
1185         if (ok) ++ndeleted;
1186       }
1187   }
1188   
1189   return ndeleted;
1190 }
1191
1192 //_____________________________________________________________________________
1193 void AliMergeableCollection::PruneEmptyObjects()
1194 {
1195   /// Delete the empty objects
1196   /// (Implemented for TH1 only)
1197   TIter next(Map());
1198   TObjString* key;
1199   
1200   TList toBeRemoved;
1201   toBeRemoved.SetOwner(kTRUE);
1202   
1203   while ( ( key = static_cast<TObjString*>(next()) ) )
1204   {
1205     TString identifier(key->String());
1206     THashList* hlist = static_cast<THashList*>(Map()->GetValue(identifier.Data()));
1207     TIter nextObject(hlist);
1208     TObject* obj;
1209     while ( ( obj = nextObject() ) )
1210     {
1211       if ( IsEmptyObject(obj) ) toBeRemoved.Add(new TObjString(Form("%s%s",identifier.Data(),obj->GetName())));
1212     }
1213   }
1214   
1215   TIter nextTBR(&toBeRemoved);
1216   while ( ( key = static_cast<TObjString*>(nextTBR()) ) )
1217   {
1218     Remove(key->GetString().Data());
1219     AliDebug(2,Form("Removing %s", key->GetString().Data()));
1220   }
1221 }
1222
1223 //_____________________________________________________________________________
1224 AliMergeableCollection* 
1225 AliMergeableCollection::Project(const char* identifier) const
1226 {
1227   /// To be implemented : would create a new collection starting at /key1/key2/...
1228   
1229   if (!fMap) return 0x0;
1230   
1231   AliMergeableCollection* mergCol = new AliMergeableCollection(Form("%s %s",GetName(),identifier),
1232                                                                GetTitle());
1233   
1234   TIter next(Map());
1235   TObjString* str;
1236   
1237   while ( ( str = static_cast<TObjString*>(next()) ) )
1238   {
1239     TString currIdentifier = str->String();
1240     if ( ! currIdentifier.Contains(identifier) ) continue;
1241     
1242     THashList* list = static_cast<THashList*>(Map()->GetValue(identifier));
1243     
1244     TIter nextObj(list);
1245     TObject* obj;
1246     
1247     while ( ( obj = nextObj()) )
1248     {
1249       TObject* clone = obj->Clone();
1250
1251       TString newkey(currIdentifier.Data());
1252       newkey.ReplaceAll(identifier,"");
1253
1254       if (newkey=="/") newkey="";
1255       
1256       mergCol->InternalAdopt(newkey.Data(),clone);
1257     }    
1258   }
1259
1260   return mergCol;
1261 }
1262
1263 //_____________________________________________________________________________
1264 TObject* 
1265 AliMergeableCollection::Remove(const char* fullIdentifier)
1266 {
1267   ///
1268   /// Remove a given object (given its fullIdentifier=/key1/key2/.../objectName)
1269   ///
1270   /// Note that we do *not* remove the /key1/key2/... entry even if there's no
1271   /// more object for this triplet.
1272   ///
1273   /// Not very efficient. Could be improved ?
1274   ///
1275   
1276   TString identifier = GetIdentifier(fullIdentifier);
1277   
1278   THashList* hlist = dynamic_cast<THashList*>(Map()->GetValue(identifier.Data()));
1279   
1280   if (!hlist)
1281   {
1282     AliWarning(Form("Could not get hlist for key=%s",identifier.Data()));
1283     return 0x0;
1284   }
1285     
1286   TObject* obj = GetObject(fullIdentifier);
1287   if (!obj)
1288   {
1289     AliError(Form("Could not find object %s",fullIdentifier));
1290     return 0x0;
1291   }
1292   
1293   TObject* rmObj = hlist->Remove(obj);
1294   if (!rmObj)
1295   {
1296     AliError("Remove failed");
1297     return 0x0;
1298   }
1299   
1300   return rmObj;
1301 }
1302
1303 //_____________________________________________________________________________
1304 Int_t AliMergeableCollection::RemoveByType(const char* typeName)
1305 {
1306   /// Remove all the objects in this collection that are of a given type
1307   TIter nextIdentifier(Map());
1308   TObjString* identifier;
1309   Int_t nremoved(0);
1310   
1311   while ( (identifier = static_cast<TObjString*>(nextIdentifier()) ) )
1312   {
1313     THashList* list  = static_cast<THashList*>(Map()->GetValue(identifier->String()));
1314     TIter next(list);
1315     TObject* o;
1316     
1317     while ( ( o = next() ) )
1318     {
1319       if ( strcmp(o->ClassName(),typeName) == 0 )
1320       {
1321         list->Remove(o);
1322         ++nremoved;
1323       }
1324     }
1325   }
1326   return nremoved;
1327 }
1328
1329
1330 //_____________________________________________________________________________
1331 TObjArray*
1332 AliMergeableCollection::SortAllIdentifiers() const
1333 {
1334   /// Sort our internal identifiers. Returned array must be deleted.
1335   TObjArray* identifiers = new TObjArray;
1336   identifiers->SetOwner(kFALSE); 
1337   TIter next(Map());
1338   TObjString* sid;
1339   
1340   while ( ( sid = static_cast<TObjString*>(next()) ) )
1341   {
1342     if ( !identifiers->FindObject(sid->String().Data()) )
1343     {
1344       identifiers->Add(sid);      
1345     }
1346   }
1347   identifiers->Sort();
1348   return identifiers;
1349 }
1350
1351
1352 ///////////////////////////////////////////////////////////////////////////////
1353 //
1354 // AliMergeableCollectionIterator
1355 //
1356 ///////////////////////////////////////////////////////////////////////////////
1357
1358 class AliMergeableCollectionIterator;
1359
1360 //_____________________________________________________________________________
1361 AliMergeableCollectionIterator::AliMergeableCollectionIterator(const AliMergeableCollection* mcol, Bool_t dir)
1362 : fkMergeableCollection(mcol), fMapIterator(0x0), fHashListIterator(0x0), fDirection(dir)
1363 {
1364   /// Default ctor
1365 }
1366
1367 //_____________________________________________________________________________
1368 AliMergeableCollectionIterator&
1369 AliMergeableCollectionIterator::operator=(const TIterator&)
1370 {
1371   /// Overriden operator= (imposed by Root's declaration of TIterator ?)
1372   Fatal("TIterator::operator=","Not implementeable"); // because there's no clone in TIterator :-(
1373   return *this;
1374 }
1375
1376 //_____________________________________________________________________________
1377 AliMergeableCollectionIterator::~AliMergeableCollectionIterator()
1378 {
1379   /// dtor
1380   Reset();
1381 }
1382
1383 //_____________________________________________________________________________
1384 TObject* AliMergeableCollectionIterator::Next()
1385 {
1386   /// Advance to next object in the collection
1387   
1388   if (!fHashListIterator)
1389   {
1390     if ( !fMapIterator ) 
1391     {
1392       fMapIterator = fkMergeableCollection->fMap->MakeIterator(fDirection);
1393     }
1394     TObjString* key = static_cast<TObjString*>(fMapIterator->Next());
1395     if (!key)
1396     {
1397       // we are done
1398       return 0x0;
1399     }      
1400     THashList* list = static_cast<THashList*>(fkMergeableCollection->Map()->GetValue(key->String().Data()));
1401     if (!list) return 0x0;
1402     fHashListIterator = list->MakeIterator(fDirection);
1403   }
1404
1405   TObject* obj = fHashListIterator->Next();
1406   
1407   if (!obj) 
1408   {
1409     delete fHashListIterator;
1410     fHashListIterator = 0x0;
1411     return Next();
1412   }
1413   
1414   return obj;
1415 }
1416
1417 //_____________________________________________________________________________
1418 void AliMergeableCollectionIterator::Reset()
1419 {
1420   /// Reset the iterator
1421   delete fHashListIterator;
1422   delete fMapIterator;
1423 }