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