]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWG/muon/AliMergeableCollection.cxx
3daace6c25c03ab4c2b45fefe320439470b7c7e3
[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   /// Get histogram key1/key2/.../objectName:action
417   /// action is used for 2D histograms :
418   /// might be px for projection along x-axis
419   /// py for projection along y-axis
420   /// pfx for profile along x-axis
421   /// pfy for profile along y-axis
422   
423   TObject* o = GetObject(identifier,objectName);
424   
425   TObjArray* arr = TString(objectName).Tokenize(":");
426   TString action;
427   
428   if ( arr->GetLast() > 0 )
429   {
430     action = static_cast<TObjString*>(arr->At(1))->String();
431     action.ToUpper();
432   }
433   
434   delete arr;
435
436   return HistoWithAction(identifier,o,action);
437 }
438
439
440 //_____________________________________________________________________________
441 TH1*
442 AliMergeableCollection::HistoWithAction(const char* identifier, TObject* o, const TString& action) const
443 {
444   /// Convert o to an histogram if possible, applying a given action if there
445
446   if (!o) return 0x0;
447   
448   if (!o->InheritsFrom("TH1"))
449   {
450     AliError(Form("%s is not an histogram",o->GetName()));
451     return 0x0;
452   }
453   
454   TH2* h2 = dynamic_cast<TH2*>(o);
455   
456   if (h2)
457   {
458     if ( action == "PX" )
459     {
460       return h2->ProjectionX(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
461     }
462     if ( action == "PY" )
463     {
464       return h2->ProjectionY(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
465     }
466     if ( action == "PFX" )
467     {
468       return h2->ProfileX(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
469     }
470     if ( action == "PFY" )
471     {
472       return h2->ProfileY(NormalizeName(Form("%s/%s",identifier,o->GetName()),action.Data()).Data());
473     }
474   }
475   
476   return static_cast<TH1*>(o);
477 }
478
479 //_____________________________________________________________________________
480 TH2*
481 AliMergeableCollection::H2(const char* fullIdentifier) const
482 {
483   /// Short-cut method to grab a 2D histogram
484   /// Will return 0x0 if the object if not a TH2xxx
485
486   TObject* o = GetObject(fullIdentifier);
487   
488   if (o->IsA()->InheritsFrom(TH2::Class()))
489   {
490     return static_cast<TH2*>(o);
491   }
492   return 0x0;
493 }
494
495 //_____________________________________________________________________________
496 TH2*
497 AliMergeableCollection::H2(const char* identifier,
498                            const char* objectName) const
499 {
500   /// Short-cut method to grab a 2D histogram
501   /// Will return 0x0 if the object if not a TH2xxx
502   
503   TObject* o = GetObject(identifier,objectName);
504   
505   if (o->IsA()->InheritsFrom(TH2::Class()))
506   {
507     return static_cast<TH2*>(o);
508   }
509   return 0x0;
510 }
511
512 //_____________________________________________________________________________
513 TProfile*
514 AliMergeableCollection::Prof(const char* fullIdentifier) const
515 {
516   /// Short-cut method to grab a TProfile histogram
517   /// Will return 0x0 if the object if not a TProfile
518   
519   TObject* o = GetObject(fullIdentifier);
520   
521   if (o->IsA()->InheritsFrom(TProfile::Class()))
522   {
523     return static_cast<TProfile*>(o);
524   }
525   return 0x0;
526 }
527
528 //_____________________________________________________________________________
529 TProfile*
530 AliMergeableCollection::Prof(const char* identifier,
531                            const char* objectName) const
532 {
533   /// Short-cut method to grab a TProfile histogram
534   /// Will return 0x0 if the object if not a TProfile
535   
536   TObject* o = GetObject(identifier,objectName);
537   
538   if (o->IsA()->InheritsFrom(TProfile::Class()))
539   {
540     return static_cast<TProfile*>(o);
541   }
542   return 0x0;
543 }
544
545 //_____________________________________________________________________________
546 TObject* 
547 AliMergeableCollection::GetObject(const char* fullIdentifier) const
548 {
549   /// Get object key1/key2/.../objectName
550   /// Note that no action is allowed for generic objects (only for histograms,
551   /// see Histo() methods)
552   
553   TString sfullIdentifier(fullIdentifier);
554   
555   Int_t nslashes = sfullIdentifier.CountChar('/');
556   
557   if (!nslashes)
558   {
559     return GetObject("", sfullIdentifier);
560   }
561   else
562   {
563     return GetObject(GetIdentifier(fullIdentifier).Data(), GetObjectName(fullIdentifier));
564   }
565 }
566
567 //_____________________________________________________________________________
568 TObject* 
569 AliMergeableCollection::GetObject(const char* identifier, 
570                                   const char* objectName) const
571 {
572   /// Get object for (identifier,objectName) triplet
573   
574   TString sidentifier(identifier);
575   if ( ! sidentifier.IsNull() ) {
576     if ( ! sidentifier.BeginsWith("/") ) sidentifier.Prepend("/");
577     if ( ! sidentifier.EndsWith("/") ) sidentifier.Append("/");
578   }
579   return InternalObject(sidentifier.Data(),objectName);
580 }
581
582 //_____________________________________________________________________________
583 TObject* AliMergeableCollection::GetSum(const char* idPattern) const
584 {
585   /// Sum objects
586   /// The pattern must be in the form:
587   /// /key1_1,key1_2,.../key2_1,key2_2,.../.../objectName_1,objectName_2...
588   /// The logical or between patterns separated by commas is taken
589   /// Exact match is required for keys and objectNames
590   
591   TObject* sumObject = 0x0;
592   
593   // Build array of lists of pattern
594   TString idPatternString(idPattern);
595   TObjArray* keyList = idPatternString.Tokenize("/");
596   TObjArray keyMatrix(keyList->GetEntries());
597   keyMatrix.SetOwner();
598   for ( Int_t ikey=0; ikey<keyList->GetEntries(); ikey++ ) {
599     TObjArray* subKeyList = ((TObjString*)keyList->At(ikey))->GetString().Tokenize(",");
600     keyMatrix.AddAt(subKeyList, ikey);
601   }
602   delete keyList;
603   
604   TString debugMsg = "Adding objects:";
605   
606   //
607   // First handle the keys
608   //
609   TIter next(Map());
610   TObjString* str;
611   while ( ( str = static_cast<TObjString*>(next()) ) )
612   {
613     TString identifier = str->String();
614     
615     Bool_t listMatchPattern = kTRUE;
616     for ( Int_t ikey=0; ikey<keyMatrix.GetEntries()-1; ikey++ ) {
617       TString currKey = GetKey(identifier, ikey, kFALSE);
618       Bool_t matchKey = kFALSE;
619       TObjArray* subKeyList = static_cast<TObjArray*> ( keyMatrix.At(ikey) );
620       for ( Int_t isub=0; isub<subKeyList->GetEntries(); isub++ ) {
621         TString subKeyString = static_cast<TObjString*> (subKeyList->At(isub))->GetString();
622         if ( currKey == subKeyString ) {
623           matchKey = kTRUE;
624           break;
625         }
626       } // loop on the list of patterns of each key
627       if ( ! matchKey ) {
628         listMatchPattern = kFALSE;
629         break;
630       }
631     } // loop on keys in the idPattern
632     if ( ! listMatchPattern ) continue;
633     
634     
635     //
636     // Then handle the object name
637     //
638     THashList* list = static_cast<THashList*>(Map()->GetValue(identifier.Data()));
639     
640     TIter nextObj(list);
641     TObject* obj;
642     
643     while ( ( obj = nextObj()) )
644     {
645       TString currKey = obj->GetName();
646       Bool_t matchKey = kFALSE;
647       TObjArray* subKeyList = static_cast<TObjArray*> ( keyMatrix.Last() );
648       for ( Int_t isub=0; isub<subKeyList->GetEntries(); isub++ ) {
649         TString subKeyString = static_cast<TObjString*> (subKeyList->At(isub))->GetString();
650         if ( currKey == subKeyString ) {
651           matchKey = kTRUE;
652           break;
653         }
654       }
655       if ( ! matchKey ) continue;
656       if ( ! sumObject ) sumObject = obj->Clone();
657       else MergeObject(sumObject, obj);
658       debugMsg += Form(" %s%s",identifier.Data(),obj->GetName());
659     } // loop on objects in list
660   } // loop on identifiers in map
661   
662   AliDebug(1,debugMsg.Data());
663   
664   return sumObject;
665 }
666
667 //_____________________________________________________________________________
668 Bool_t AliMergeableCollection::InternalAdopt(const char* identifier, TObject* obj)
669 {
670   /// Adopt an obj
671   
672   if (!obj)
673   {
674     Error("Adopt","Cannot adopt a null object");
675     return kFALSE;
676   }
677   
678   if ( ! obj->IsA()->InheritsFrom(TObject::Class()) ||
679         ! obj->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
680     Error("Adopt","Cannot adopt an object which is not mergeable!"); 
681   }
682   
683   THashList* hlist = 0x0;
684   
685   hlist = static_cast<THashList*>(Map()->GetValue(identifier));
686   
687   if (!hlist)
688   {
689     hlist = new THashList;
690     hlist->SetOwner(kTRUE);
691     Map()->Add(new TObjString(identifier),hlist);
692     hlist->SetName(identifier);
693   }
694   
695   TObject* existingObj = hlist->FindObject(obj->GetName());
696   
697   if (existingObj)
698   {
699     AliError(Form("Cannot adopt an already existing object : %s -> %s",identifier,existingObj->GetName()));
700     return kFALSE;
701   }
702   
703   if ( obj->IsA()->InheritsFrom(TH1::Class()) ) (static_cast<TH1*> ( obj ))->SetDirectory(0);  
704   
705   hlist->AddLast(obj);
706     
707   return kTRUE;
708   
709 }
710
711 //_____________________________________________________________________________
712 TString
713 AliMergeableCollection::InternalDecode(const char* identifier, Int_t index) const
714 {
715   /// Extract the index-th element of the identifier (/key1/key2/.../keyN/objectName)
716   /// object is index=-1 (i.e. last)
717   
718   if ( strlen(identifier) > 0 && identifier[0] != '/' ) 
719   {    
720     AliError(Form("identifier %s is malformed (should start with /)",identifier));
721     return "";
722   }
723   
724   std::vector<Int_t> splitIndex;
725   
726   Int_t start(0);
727   TString sidentifier(identifier);
728   
729   while (start < sidentifier.Length())
730   {
731     Int_t pos = sidentifier.Index('/', start);
732     if (pos == kNPOS) break;
733     splitIndex.push_back(pos);
734     start = pos + 1;
735   }
736
737   Int_t nkeys = splitIndex.size() - 1;
738   
739   if ( index >= nkeys )
740   {
741     AliError(Form("Requiring index %i of identifier %s which only have %i",index, identifier, nkeys));
742     return "";
743   }
744
745   if ( index < 0 )
746   {
747     return sidentifier(splitIndex.back()+1,sidentifier.Length()-splitIndex.back()-1);
748   }
749
750   return sidentifier(splitIndex[index]+1,splitIndex[index+1]-splitIndex[index]-1);
751 }
752
753 //_____________________________________________________________________________
754 TObject* 
755 AliMergeableCollection::InternalObject(const char* identifier,
756                                        const char* objectName) const
757 {
758   /// Get object for (identifier,objectName) 
759   
760   if (!fMap) 
761   {
762     return 0x0;
763   }
764   
765   THashList* hlist = static_cast<THashList*>(Map()->GetValue(identifier));
766   if (!hlist) 
767   {
768     TString msg(Form("Did not find hashlist for identifier=%s dir=%s",identifier,gDirectory ? gDirectory->GetName() : "" ));
769     fMessages[msg.Data()]++;
770     return 0x0;
771   }
772   
773   TObject* obj = hlist->FindObject(objectName);
774   if (!obj)
775   {
776     TString msg(Form("Did not find objectName=%s in %s",objectName,identifier));
777     fMessages[msg.Data()]++;
778   }
779   return obj;
780 }
781
782
783 //_____________________________________________________________________________
784 Bool_t AliMergeableCollection::IsEmptyObject(TObject* obj) const
785 {
786   /// Check if object is empty
787   /// (done only for TH1, so far)
788     
789   if ( obj->IsA()->InheritsFrom(TH1::Class()) ) {
790     TH1* histo = static_cast<TH1*> (obj);
791     if ( histo->GetEntries() == 0 ) return kTRUE;
792   }
793
794   return kFALSE;
795
796 }
797
798
799 //_____________________________________________________________________________
800 TMap* AliMergeableCollection::Map() const
801 {
802   /// Wrapper to insure proper key formats (i.e. new vs old)
803   
804   if (!fMap)
805   {
806     fMap = new TMap;
807     fMap->SetOwnerKeyValue(kTRUE,kTRUE);
808     fMapVersion = 1;
809   }
810   else
811   {
812     if ( fMapVersion < 1 ) 
813     {
814       AliInfo("Remapping");
815       // change the keys
816       TIter next(fMap);
817       TObjString* str;
818       
819       while ( ( str = static_cast<TObjString*>(next()) ) )
820       {
821         if ( str->String().Contains("./") )
822         {
823           TString newkey(str->String());
824           
825           newkey.ReplaceAll("./","");
826           
827           TObject* o = fMap->GetValue(str);
828           
829           TPair* p = fMap->RemoveEntry(str);
830           if (!p)
831           {
832             AliError("oups oups oups");
833             return 0x0;
834           }
835           
836           fMap->Add(new TObjString(newkey.Data()),o);
837           
838           delete p;
839         }
840       }
841       
842       fMapVersion = 1;
843     }
844   }
845   
846   return fMap;
847 }
848
849 //_____________________________________________________________________________
850 Long64_t
851 AliMergeableCollection::Merge(TCollection* list)
852 {
853   // Merge a list of AliMergeableCollection objects with this
854   // Returns the number of merged objects (including this).
855   
856   if (!list) return 0;
857   
858   if (list->IsEmpty()) return 1;
859   
860   TIter next(list);
861   TObject* currObj;
862   TList mapList;
863   Int_t count(0);
864   
865   while ( ( currObj = next() ) )
866   {
867     AliMergeableCollection* mergeCol = dynamic_cast<AliMergeableCollection*>(currObj);
868     if (!mergeCol) {
869       AliFatal(Form("object named \"%s\" is a %s instead of an AliMergeableCollection!", currObj->GetName(), currObj->ClassName()));
870       continue;
871     }
872     
873     ++count;
874     
875     if ( mergeCol->fMap ) mergeCol->Map(); // to insure keys in the new format
876     
877     TIter nextIdentifier(mergeCol->fMap);
878     TObjString* identifier;
879
880     while ( ( identifier = static_cast<TObjString*>(nextIdentifier()) ) )
881     {
882       THashList* otherList = static_cast<THashList*>(mergeCol->fMap->GetValue(identifier->String().Data()));
883
884       TIter nextObject(otherList);
885       TObject* obj;
886       
887       while ( ( obj = nextObject() ) )
888       {
889         TString newid(Form("%s%s",identifier->String().Data(),obj->GetName()));
890         
891         TObject* thisObject = GetObject(newid.Data());
892         
893         if (!thisObject)
894         {
895           AliDebug(1,Form("Adopting a new object = %s%s",identifier->String().Data(),obj->GetName()));
896           
897           Bool_t ok = Adopt(identifier->String(), obj->Clone());
898           
899           if (!ok)
900           {
901             AliError(Form("Adoption of object %s failed",obj->GetName()));
902           }
903         }
904         else
905         {
906           // add it...
907           AliDebug(1,Form("Merging object = %s%s",
908                           identifier->String().Data(),
909                           obj->GetName()));
910           
911           MergeObject(thisObject, obj);
912         }
913       } // loop on objects in map
914     } // loop on identifiers
915   } // loop on collections in list
916          
917   AliDebug(1,Form("count=%d",count));
918   
919   return count+1;
920 }
921
922 //_____________________________________________________________________________
923 Bool_t AliMergeableCollection::MergeObject(TObject* baseObject, TObject* objToAdd)
924 {
925   /// Add objToAdd to baseObject
926   
927   if ( baseObject->IsA()->Class() != objToAdd->IsA()->Class() ) {
928     printf("MergeObject: Cannot add %s to %s", objToAdd->ClassName(), baseObject->ClassName());
929     return kFALSE;
930   }
931   if ( ! baseObject->IsA()->InheritsFrom(TObject::Class()) ||
932       ! baseObject->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
933     printf("MergeObject: Objects are not mergeable!");
934     return kFALSE;
935   }  
936   
937   TList list;
938   list.Add(objToAdd);
939   
940   TString listArgs = Form("((TCollection*)0x%lx)", (ULong_t)&list);
941   Int_t error = 0;
942   baseObject->Execute("Merge", listArgs.Data(), &error);
943   return kTRUE;
944 }
945
946 //_____________________________________________________________________________
947 TString AliMergeableCollection::NormalizeName(const char* identifier,const char* action) const
948 {
949   /// Replace / by _ to build a root-compliant histo name
950   TString name(GetName());
951   
952   name += "_";
953   name += identifier;
954   name += "_";
955   name += action;
956   name.ReplaceAll("/","_");
957   name.ReplaceAll("-","_");
958   return name;
959 }
960
961 //_____________________________________________________________________________
962 Int_t 
963 AliMergeableCollection::NumberOfObjects() const
964 {
965   /// Get the number of objects we hold
966   TIter next(CreateIterator());
967   Int_t count(0);
968   while ( next() ) ++count;
969   return count;
970 }
971
972 //_____________________________________________________________________________
973 Int_t 
974 AliMergeableCollection::NumberOfKeys() const
975 {
976   /// Get the number of keys we have
977   return fMap ? fMap->GetSize() : 0;
978 }
979
980 //_____________________________________________________________________________
981 void 
982 AliMergeableCollection::Print(Option_t* option) const
983 {
984   /// Print all the objects we hold, in a hopefully visually pleasing
985   /// way.
986   ///
987   /// Option can be used to select given part only, using the schema :
988   /// /*/*/*/*/*
989   /// Where the stars are wilcards for /key1/key2/.../objectName
990   ///
991   /// if * is used it is assumed to be a wildcard for objectName
992   ///
993   /// For other selections the full syntax /*/*/*/*/* must be used.
994   /// 
995   /// Use "-" as objectName to disable object's name output
996   ///
997   /// One might also use /*/*/*/*/:classname syntax to restrict
998   /// output to only those objects matching a given classname pattern
999   ///
1000   
1001   cout << Form("AliMergeableCollection(%s,%s)[%p] : %d keys and %d objects",
1002                GetName(),GetTitle(),this,
1003                NumberOfKeys(), NumberOfObjects()) << endl;
1004   
1005   if (!strlen(option)) return;
1006   
1007   TString soption(option);
1008   
1009   TObjArray* classes = soption.Tokenize(":");
1010   
1011   TRegexp* classPattern(0x0);
1012   
1013   if ( classes->GetLast() > 0 )
1014   {
1015     TString pat = static_cast<TObjString*>(classes->At(1))->String();
1016     classPattern = new TRegexp(pat,kTRUE);
1017     soption = static_cast<TObjString*>(classes->At(0))->String();
1018   }
1019   
1020   delete classes;
1021
1022   TObjArray* select = soption.Tokenize("/");
1023   
1024   TString sreObjectName(select->Last()->GetName());
1025   TRegexp reObjectName(sreObjectName.Data(),kTRUE);
1026   
1027   TObjArray* identifiers = SortAllIdentifiers();
1028   
1029   std::cout << Form("Number of identifiers %d", identifiers->GetEntries()) << std::endl;
1030     
1031   TIter nextIdentifier(identifiers);
1032   
1033   TObjString* sid(0x0);
1034   
1035   while ( ( sid = static_cast<TObjString*>(nextIdentifier()) ) )
1036   {
1037     Bool_t identifierPrinted(kFALSE);
1038
1039     TString identifier(sid->String());
1040     
1041     Bool_t matchPattern = kTRUE;
1042     for ( Int_t isel=0; isel<select->GetLast(); isel++ ) {
1043       if ( ! GetKey(identifier.Data(), isel, kFALSE).Contains(TRegexp(select->At(isel)->GetName(),kTRUE)) ) {
1044         matchPattern = kFALSE;
1045         break;
1046       }
1047     }
1048     if ( ! matchPattern ) continue;
1049     
1050     if ( sreObjectName == "*" && !classPattern)
1051     {
1052       identifierPrinted = kTRUE;
1053       cout << identifier.Data() << endl;
1054     }
1055       
1056     THashList * list = static_cast<THashList*>(Map()->GetValue(sid->String().Data()));
1057
1058     TObjArray names;
1059     names.SetOwner(kTRUE);
1060     TIter nextUnsortedObj(list);
1061     TObject* obj;
1062     while ( ( obj = nextUnsortedObj() ) )
1063     {
1064       TString cname(obj->ClassName());
1065       if ( classPattern && !cname.Contains((*classPattern)) )
1066       {
1067         continue;
1068       }
1069       names.Add(new TObjString(obj->GetName()));
1070     }
1071     names.Sort();
1072     TIter nextObjName(&names);
1073     TObjString* oname;
1074     while ( ( oname = static_cast<TObjString*>(nextObjName()) ) )
1075     {
1076       TString objName(oname->String());
1077       if (objName.Contains(reObjectName) )
1078       {
1079         obj = list->FindObject(objName.Data());
1080         if ( IsEmptyObject(obj) && ! fMustShowEmptyObject ) continue;
1081         
1082         if (!identifierPrinted)
1083         {
1084           cout << identifier.Data() << endl;
1085           identifierPrinted = kTRUE;
1086         }
1087         
1088         TString extra;
1089         TString warning("   ");
1090         
1091         if ( obj->IsA()->InheritsFrom(TH1::Class()) )
1092         {
1093           
1094           TH1* histo = static_cast<TH1*> (obj);
1095           extra.Form("%s | Entries=%d Sum=%g",histo->GetTitle(),Int_t(histo->GetEntries()),histo->GetSumOfWeights());
1096         }
1097         else if ( obj->IsA()->InheritsFrom(TGraph::Class()) )
1098         {
1099           TGraph* graph = static_cast<TGraph*> (obj);
1100           if ( ! TMath::Finite(graph->GetMean(2) ) )
1101           {
1102             warning = " ! ";
1103           }
1104           extra.Form("%s | Npts=%d Mean=%g RMS=%g",graph->GetTitle(),graph->GetN(),
1105                        graph->GetMean(2),graph->GetRMS(2));
1106           
1107         }
1108         
1109         std::cout << Form("    (%s) %s %s", obj->ClassName(),
1110                      warning.Data(),
1111                           obj->GetName());
1112         
1113         if ( extra.Length() )
1114         {
1115           std::cout << " | " << extra.Data();
1116         }
1117         std::cout << std::endl;
1118       }
1119     }
1120     if (!identifierPrinted && sreObjectName=="-" )
1121     { 
1122       // to handle the case where we used objectName="-" to disable showing the objectNames,
1123       // but we still want to see the matching keys maybe...
1124       cout << identifier.Data() << endl;
1125     }
1126   }
1127   
1128   delete select;
1129   
1130   delete identifiers;
1131 }
1132
1133 //_____________________________________________________________________________
1134 void 
1135 AliMergeableCollection::PrintMessages(const char* prefix) const
1136 {
1137   /// Print pending messages
1138   
1139   std::map<std::string,int>::const_iterator it;
1140   
1141   for ( it = fMessages.begin(); it != fMessages.end(); ++it ) 
1142   {
1143     cout << Form("%s : message %s appeared %5d times",prefix,it->first.c_str(),it->second) << endl;
1144   }
1145 }
1146
1147
1148 //_____________________________________________________________________________
1149 UInt_t 
1150 AliMergeableCollection::EstimateSize(Bool_t show) const
1151 {
1152   /// Estimate the memory (in kilobytes) used by some objects
1153
1154 //  For TH1:
1155 //  sizeof(TH1) + (nbins+2)*(nbytes_per_bin) +name+title_sizes 
1156 //  if you have errors add (nbins+2)*8 
1157     
1158   TIter next(CreateIterator());
1159   
1160   TObject* obj;
1161   UInt_t size(0);
1162   
1163   while ( ( obj = next() ) )
1164   {
1165     UInt_t thissize=0;
1166     if ( obj->IsA()->InheritsFrom(TH1::Class()) || obj->IsA()->InheritsFrom(TProfile::Class()) )
1167     {
1168       TH1* histo = static_cast<TH1*> (obj);
1169       Int_t nbins = (histo->GetNbinsX()+2);
1170     
1171       if (histo->GetNbinsY()>1)
1172       {
1173         nbins *= (histo->GetNbinsY()+2);
1174       }
1175     
1176       if (histo->GetNbinsZ()>1)
1177       {
1178         nbins *= (histo->GetNbinsZ()+2);
1179       }
1180       
1181       Bool_t hasErrors = ( histo->GetSumw2N() > 0 );
1182     
1183       TString cname(histo->ClassName());
1184     
1185       Int_t nbytesPerBin(0);
1186     
1187       if (cname.Contains(TRegexp("C$")) ) nbytesPerBin = sizeof(Char_t);
1188       if (cname.Contains(TRegexp("S$")) ) nbytesPerBin = sizeof(Short_t);
1189       if (cname.Contains(TRegexp("I$")) ) nbytesPerBin = sizeof(Int_t);
1190       if (cname.Contains(TRegexp("F$")) ) nbytesPerBin = sizeof(Float_t);
1191       if (cname.Contains(TRegexp("D$")) ) nbytesPerBin = sizeof(Double_t);
1192       if (cname=="TProfile") nbytesPerBin = sizeof(Double_t);
1193         
1194       if (!nbytesPerBin)
1195       {
1196         AliError(Form("Could not get the number of bytes per bin for histo %s of class %s. Thus the size estimate will be wrong !",
1197                       histo->GetName(),histo->ClassName()));
1198         continue;
1199       }
1200     
1201       thissize = sizeof(histo) + nbins*(nbytesPerBin) + strlen(histo->GetName())
1202       + strlen(histo->GetTitle());
1203       
1204       if ( hasErrors) thissize += nbins*8;
1205       
1206       if ( obj->IsA()->InheritsFrom(TProfile::Class()) )
1207       {
1208         TProfile* prof = static_cast<TProfile*>(obj);
1209         TArrayD* d = prof->GetBinSumw2();
1210         thissize += d->GetSize()*8*2; // 2 TArrayD
1211         thissize += sizeof(prof) - sizeof(histo);
1212       }
1213     }
1214     else if ( obj->IsA()->InheritsFrom(THnSparse::Class()) ) {
1215       THnSparse* sparse = static_cast<THnSparse*> (obj);
1216       thissize = sizeof(Float_t) * (UInt_t)sparse->GetNbins();
1217     }
1218 //    else if ( obj->IsA() == AliCFGridSparse::Class() ) {
1219 //      AliCFGridSparse* sparse = static_cast<AliCFGridSparse*> (obj);
1220 //      thissize = sizeof(Float_t) * (UInt_t)sparse->GetNFilledBins();
1221 //    }
1222 //    else if ( obj->IsA() == AliCFContainer::Class() ) {
1223 //      AliCFContainer* cont = static_cast<AliCFContainer*> (obj);
1224 //      for ( Int_t istep=0; istep<cont->GetNStep(); istep++ ) {
1225 //        thissize += sizeof(Float_t) * (UInt_t)cont->GetGrid(istep)->GetNFilledBins();
1226 //      }
1227 //    }
1228     else {
1229       AliWarning(Form("Cannot estimate size of %s\n", obj->ClassName()));
1230       continue;
1231     }
1232
1233     size += thissize;
1234     
1235     if ( show ) 
1236     {
1237       AliInfo(Form("Size of %30s is %20d bytes",obj->GetName(),thissize));
1238     }
1239   } // loop on objects
1240
1241   return size;
1242 }
1243
1244 //_____________________________________________________________________________
1245 Int_t AliMergeableCollection::Prune(const char* identifier)
1246 {
1247   // Delete all objects which match the beginning of the identifier
1248   // returns the number of entries removed from the Map()
1249   // (not to be confused with the number of leaf objects removed)
1250   //
1251   
1252   TIter next(Map());
1253   TObjString* key;
1254   Int_t ndeleted(0);
1255   
1256   while ( ( key = static_cast<TObjString*>(next())) )
1257   {
1258       if (key->String().BeginsWith(identifier))
1259       {
1260         Bool_t ok = Map()->DeleteEntry(key);
1261         if (ok) ++ndeleted;
1262       }
1263   }
1264   
1265   return ndeleted;
1266 }
1267
1268 //_____________________________________________________________________________
1269 void AliMergeableCollection::PruneEmptyObjects()
1270 {
1271   /// Delete the empty objects
1272   /// (Implemented for TH1 only)
1273   TIter next(Map());
1274   TObjString* key;
1275   
1276   TList toBeRemoved;
1277   toBeRemoved.SetOwner(kTRUE);
1278   
1279   while ( ( key = static_cast<TObjString*>(next()) ) )
1280   {
1281     TString identifier(key->String());
1282     THashList* hlist = static_cast<THashList*>(Map()->GetValue(identifier.Data()));
1283     TIter nextObject(hlist);
1284     TObject* obj;
1285     while ( ( obj = nextObject() ) )
1286     {
1287       if ( IsEmptyObject(obj) ) toBeRemoved.Add(new TObjString(Form("%s%s",identifier.Data(),obj->GetName())));
1288     }
1289   }
1290   
1291   TIter nextTBR(&toBeRemoved);
1292   while ( ( key = static_cast<TObjString*>(nextTBR()) ) )
1293   {
1294     Remove(key->GetString().Data());
1295     AliDebug(2,Form("Removing %s", key->GetString().Data()));
1296   }
1297 }
1298
1299 //_____________________________________________________________________________
1300 AliMergeableCollection* 
1301 AliMergeableCollection::Project(const char* identifier) const
1302 {
1303   /// To be implemented : would create a new collection starting at /key1/key2/...
1304   
1305   if (!fMap) return 0x0;
1306   
1307   AliMergeableCollection* mergCol = new AliMergeableCollection(Form("%s %s",GetName(),identifier),
1308                                                                GetTitle());
1309   
1310   TIter next(Map());
1311   TObjString* str;
1312   
1313   while ( ( str = static_cast<TObjString*>(next()) ) )
1314   {
1315     TString currIdentifier = str->String();
1316     if ( ! currIdentifier.Contains(identifier) ) continue;
1317     
1318     THashList* list = static_cast<THashList*>(Map()->GetValue(identifier));
1319     
1320     TIter nextObj(list);
1321     TObject* obj;
1322     
1323     while ( ( obj = nextObj()) )
1324     {
1325       TObject* clone = obj->Clone();
1326
1327       TString newkey(currIdentifier.Data());
1328       newkey.ReplaceAll(identifier,"");
1329
1330       if (newkey=="/") newkey="";
1331       
1332       mergCol->InternalAdopt(newkey.Data(),clone);
1333     }    
1334   }
1335
1336   return mergCol;
1337 }
1338
1339 //_____________________________________________________________________________
1340 TObject* 
1341 AliMergeableCollection::Remove(const char* fullIdentifier)
1342 {
1343   ///
1344   /// Remove a given object (given its fullIdentifier=/key1/key2/.../objectName)
1345   ///
1346   /// Note that we do *not* remove the /key1/key2/... entry even if there's no
1347   /// more object for this triplet.
1348   ///
1349   /// Not very efficient. Could be improved ?
1350   ///
1351   
1352   TString identifier = GetIdentifier(fullIdentifier);
1353   
1354   THashList* hlist = dynamic_cast<THashList*>(Map()->GetValue(identifier.Data()));
1355   
1356   if (!hlist)
1357   {
1358     AliWarning(Form("Could not get hlist for key=%s",identifier.Data()));
1359     return 0x0;
1360   }
1361     
1362   TObject* obj = GetObject(fullIdentifier);
1363   if (!obj)
1364   {
1365     AliError(Form("Could not find object %s",fullIdentifier));
1366     return 0x0;
1367   }
1368   
1369   TObject* rmObj = hlist->Remove(obj);
1370   if (!rmObj)
1371   {
1372     AliError("Remove failed");
1373     return 0x0;
1374   }
1375   
1376   return rmObj;
1377 }
1378
1379 //_____________________________________________________________________________
1380 Int_t AliMergeableCollection::RemoveByType(const char* typeName)
1381 {
1382   /// Remove all the objects in this collection that are of a given type
1383   TIter nextIdentifier(Map());
1384   TObjString* identifier;
1385   Int_t nremoved(0);
1386   
1387   while ( (identifier = static_cast<TObjString*>(nextIdentifier()) ) )
1388   {
1389     THashList* list  = static_cast<THashList*>(Map()->GetValue(identifier->String()));
1390     TIter next(list);
1391     TObject* o;
1392     
1393     while ( ( o = next() ) )
1394     {
1395       if ( strcmp(o->ClassName(),typeName) == 0 )
1396       {
1397         list->Remove(o);
1398         ++nremoved;
1399       }
1400     }
1401   }
1402   return nremoved;
1403 }
1404
1405
1406 //_____________________________________________________________________________
1407 TObjArray*
1408 AliMergeableCollection::SortAllIdentifiers() const
1409 {
1410   /// Sort our internal identifiers. Returned array must be deleted.
1411   TObjArray* identifiers = new TObjArray;
1412   identifiers->SetOwner(kFALSE); 
1413   TIter next(Map());
1414   TObjString* sid;
1415   
1416   while ( ( sid = static_cast<TObjString*>(next()) ) )
1417   {
1418     if ( !identifiers->FindObject(sid->String().Data()) )
1419     {
1420       identifiers->Add(sid);      
1421     }
1422   }
1423   identifiers->Sort();
1424   return identifiers;
1425 }
1426
1427
1428 ///////////////////////////////////////////////////////////////////////////////
1429 //
1430 // AliMergeableCollectionIterator
1431 //
1432 ///////////////////////////////////////////////////////////////////////////////
1433
1434 class AliMergeableCollectionIterator;
1435
1436 //_____________________________________________________________________________
1437 AliMergeableCollectionIterator::AliMergeableCollectionIterator(const AliMergeableCollection* mcol, Bool_t dir)
1438 : fkMergeableCollection(mcol), fMapIterator(0x0), fHashListIterator(0x0), fDirection(dir)
1439 {
1440   /// Default ctor
1441 }
1442
1443 //_____________________________________________________________________________
1444 AliMergeableCollectionIterator&
1445 AliMergeableCollectionIterator::operator=(const TIterator&)
1446 {
1447   /// Overriden operator= (imposed by Root's declaration of TIterator ?)
1448   Fatal("TIterator::operator=","Not implementeable"); // because there's no clone in TIterator :-(
1449   return *this;
1450 }
1451
1452 //_____________________________________________________________________________
1453 AliMergeableCollectionIterator::~AliMergeableCollectionIterator()
1454 {
1455   /// dtor
1456   Reset();
1457 }
1458
1459 //_____________________________________________________________________________
1460 TObject* AliMergeableCollectionIterator::Next()
1461 {
1462   /// Advance to next object in the collection
1463   
1464   if (!fHashListIterator)
1465   {
1466     if ( !fMapIterator ) 
1467     {
1468       fMapIterator = fkMergeableCollection->fMap->MakeIterator(fDirection);
1469     }
1470     TObjString* key = static_cast<TObjString*>(fMapIterator->Next());
1471     if (!key)
1472     {
1473       // we are done
1474       return 0x0;
1475     }      
1476     THashList* list = static_cast<THashList*>(fkMergeableCollection->Map()->GetValue(key->String().Data()));
1477     if (!list) return 0x0;
1478     fHashListIterator = list->MakeIterator(fDirection);
1479   }
1480
1481   TObject* obj = fHashListIterator->Next();
1482   
1483   if (!obj) 
1484   {
1485     delete fHashListIterator;
1486     fHashListIterator = 0x0;
1487     return Next();
1488   }
1489   
1490   return obj;
1491 }
1492
1493 //_____________________________________________________________________________
1494 void AliMergeableCollectionIterator::Reset()
1495 {
1496   /// Reset the iterator
1497   delete fHashListIterator;
1498   delete fMapIterator;
1499 }