]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWG3/muon/AliHistogramCollection.cxx
Macro for QA Monitoring (Cynthia)
[u/mrichter/AliRoot.git] / PWG3 / muon / AliHistogramCollection.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$
17
18 ///
19 /// A (fake) "4D" histogram container. 
20 ///
21 /// For each tuple (keyA,keyB,keyC,keyD) a (hash)list of histogram is associated.
22 /// Note that keyA, keyB (optional), keyC (optional) and keyD (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 "AliHistogramCollection.h"
28
29 #include "AliLog.h"
30 #include "Riostream.h"
31 #include "TError.h"
32 #include "TH1.h"
33 #include "THashList.h"
34 #include "TKey.h"
35 #include "TMap.h"
36 #include "TObjArray.h"
37 #include "TObjString.h"
38 #include "TRegexp.h"
39 #include "TROOT.h"
40 #include "TSystem.h"
41
42 ClassImp(AliHistogramCollection)
43
44 //_____________________________________________________________________________
45 AliHistogramCollection::AliHistogramCollection(const char* name, const char* title) 
46 : TNamed(name,title), fMap(0x0), fMustShowEmptyHistogram(kFALSE)
47 {
48   /// Ctor
49 }
50
51 //_____________________________________________________________________________
52 AliHistogramCollection::~AliHistogramCollection()
53 {
54   /// dtor. Note that the map is owner
55   if ( fMap ) fMap->DeleteAll();
56   delete fMap;
57 }
58
59 //_____________________________________________________________________________
60 Bool_t 
61 AliHistogramCollection::Adopt(const char* key, TH1* histo)
62 {
63   /// Adopt a given histogram, and associate it with pair (keyA)
64   return InternalAdopt(Form("/%s/./././",key),histo);
65 }
66
67 //_____________________________________________________________________________
68 Bool_t 
69 AliHistogramCollection::Adopt(const char* keyA, const char* keyB, TH1* histo)
70 {
71   /// Adopt a given histogram, and associate it with pair (keyA,keyB)
72   return InternalAdopt(Form("/%s/%s/././",keyA,keyB),histo);
73 }
74
75 //_____________________________________________________________________________
76 Bool_t 
77 AliHistogramCollection::Adopt(const char* keyA, const char* keyB, const char* keyC, TH1* histo)
78 {
79   /// Adopt a given histogram, and associate it with pair (keyA,keyB,keyC)
80   return InternalAdopt(Form("/%s/%s/%s/./",keyA,keyB,keyC),histo);
81 }
82
83 //_____________________________________________________________________________
84 Bool_t 
85 AliHistogramCollection::Adopt(const char* keyA, const char* keyB, const char* keyC, const char* keyD, TH1* histo)
86 {
87   /// Adopt a given histogram, and associate it with pair (keyA,keyB,keyC,keyD)
88   return InternalAdopt(Form("/%s/%s/%s/%s/",keyA,keyB,keyC,keyD),histo);
89 }
90
91 //_____________________________________________________________________________
92 TIterator*
93 AliHistogramCollection::CreateIterator(Bool_t direction) const
94 {
95   /// Create an iterator (must be deleted by the client)
96   return fMap ? new AliHistogramCollectionIterator(this,direction) : 0x0;
97 }
98
99 //_____________________________________________________________________________
100 void 
101 AliHistogramCollection::Delete(Option_t*)
102 {
103   /// Delete all the histograms
104   fMap->DeleteAll();
105   delete fMap;
106   fMap=0x0;
107 }
108
109 //_____________________________________________________________________________
110 TObject* 
111 AliHistogramCollection::FindObject(const char* identifier) const
112 {
113   /// Find an object by its full identifier.
114   
115   return Histo(KeyA(identifier),
116                KeyB(identifier),
117                KeyC(identifier),
118                KeyD(identifier),
119                HistoName(identifier));
120 }
121
122 //_____________________________________________________________________________
123 TObject* 
124 AliHistogramCollection::FindObject(const TObject *key) const
125 {
126   /// Find an object 
127   AliWarning("This method is awfully inefficient. Please improve it or use FindObject(const char*)");
128   TIter next(CreateIterator());
129   TObject* o;
130   while ( ( o=next() ) )
131   {
132     if ( o->IsEqual(key) ) return o;
133   }
134   return 0x0;
135 }
136
137 //_____________________________________________________________________________
138 TH1* 
139 AliHistogramCollection::Histo(const char* keyA, 
140                               const char* histoname) const
141 {
142   /// Get histo for (keyA,histoname) triplet
143   
144   return InternalHisto(Form("/%s/./././",keyA),histoname);
145 }
146
147 //_____________________________________________________________________________
148 TH1* 
149 AliHistogramCollection::Histo(const char* keyA, const char* keyB, 
150                               const char* histoname) const
151 {
152   /// Get histo for (keyA,keyB,histoname) triplet
153
154   return InternalHisto(Form("/%s/%s/././",keyA,keyB),histoname);
155 }
156
157 //_____________________________________________________________________________
158 TH1* 
159 AliHistogramCollection::Histo(const char* keyA, const char* keyB, const char* keyC,
160                               const char* histoname) const
161 {
162   /// Get histo for (keyA,keyB,keyC,histoname) quad
163   
164   return InternalHisto(Form("/%s/%s/%s/./",keyA,keyB,keyC),histoname);
165 }
166
167 //_____________________________________________________________________________
168 TH1* 
169 AliHistogramCollection::Histo(const char* keyA, const char* keyB, 
170                               const char* keyC, const char* keyD,
171                               const char* histoname) const
172 {
173   /// Get histo for (keyA,keyB,keyC,histoname) quad
174   
175   return InternalHisto(Form("/%s/%s/%s/%s/",keyA,keyB,keyC,keyD),histoname);
176 }
177
178 //_____________________________________________________________________________
179 TString
180 AliHistogramCollection::HistoName(const char* identifier) const
181 {
182   /// Extract the histogram name from an identifier
183   
184   return InternalDecode(identifier,4);  
185 }
186
187 //_____________________________________________________________________________
188 Bool_t AliHistogramCollection::InternalAdopt(const char* identifier, TH1* histo)
189 {
190   /// Adopt an histogram
191   
192   if (!histo)
193   {
194     Error("Adopt","Cannot adopt a null histogram");
195     return kFALSE;
196   }
197   
198   THashList* hlist = 0x0;
199   
200   if ( !fMap ) 
201   {
202     fMap = new TMap;
203     fMap->SetOwner(kTRUE);
204   }
205   
206   hlist = static_cast<THashList*>(fMap->GetValue(identifier));
207   
208   if (!hlist)
209   {
210     hlist = new THashList;
211     hlist->SetOwner(kTRUE);
212     fMap->Add(new TObjString(identifier),hlist);
213     hlist->SetName(identifier);
214   }
215   
216   TH1* h = static_cast<TH1*>(hlist->FindObject(histo->GetName()));
217   
218   if (h)
219   {
220     AliError(Form("Cannot adopt an already existing histogram : %s -> %s",identifier,h->GetName()));
221     return kFALSE;
222   }
223   
224   
225   histo->SetDirectory(0);  
226   
227   hlist->AddLast(histo);
228   
229   return kTRUE;
230   
231 }
232
233 //_____________________________________________________________________________
234 TH1* 
235 AliHistogramCollection::Histo(const char* identifier) const
236 {
237   /// Get histogram keyA/keyB/keyC/keyD/histoname
238   return Histo(InternalDecode(identifier,0),
239                InternalDecode(identifier,1),
240                InternalDecode(identifier,2),
241                InternalDecode(identifier,3),
242                InternalDecode(identifier,4));
243 }
244
245 //_____________________________________________________________________________
246 TString
247 AliHistogramCollection::InternalDecode(const char* identifier, Int_t index) const
248 {
249   /// Extract the index-th element of the identifier (/keyA/keyB/keyC/keyD/histoname)
250   /// keyA is index=0
251   /// keyB is index=1
252   /// keyC is index=2
253   /// keyD is index=3
254   /// histo is index=4
255   
256   if ( identifier[0] != '/' ) 
257   {    
258     AliError(Form("identifier %s is malformed.",identifier));
259     return "";
260   }
261   
262   TObjArray* array = TString(identifier).Tokenize("/");
263
264   if ( array->GetLast()>5 ) 
265   {
266     AliError(Form("identifier %s is malformed.",identifier));
267     delete array;
268     return "";    
269   }
270
271   TString value("");
272   
273   if ( index <= array->GetLast() ) 
274   {
275     value = static_cast<TObjString*>(array->At(index))->String();
276   }
277   
278   delete array;
279   
280   return value;
281   
282 // "Custom" implementation below is even slower that Tokenize... ??
283 // or at least did not change the timing result enough to be worthwhile (indicating
284 // the cpu time is wasted elsewhere ?)
285 //
286 //  Int_t slashPos[6] = {0};
287 //  Int_t nslashes(0);
288 //  
289 //  for ( Int_t i = 0; i < identifier.Length(); ++i ) 
290 //  {
291 //    if ( identifier[i] == '/' ) 
292 //    {
293 //      slashPos[nslashes++]=i;      
294 //    }
295 //    if ( nslashes > 5 ) 
296 //    {
297 //      AliError(Form("identifier %s is malformed.",identifier.Data()));
298 //      return "";
299 //    }
300 //  }
301 //  
302 //  slashPos[nslashes++]=identifier.Length();
303 //  --nslashes;
304 //
305 //  if ( index < nslashes )
306 //  {
307 //    return TString(identifier(slashPos[index]+1,slashPos[index+1]-slashPos[index]-1));
308 //  }
309 //  return "";
310 }
311
312 //_____________________________________________________________________________
313 TH1* 
314 AliHistogramCollection::InternalHisto(const char* identifier,
315                                       const char* histoname) const
316 {
317   /// Get histo for (identifier,histoname) 
318   
319   if (!fMap) 
320   {
321     return 0x0;
322   }
323   
324   THashList* hlist = static_cast<THashList*>(fMap->GetValue(identifier));
325   if (!hlist) 
326   {
327     return 0x0;
328   }
329   
330   return static_cast<TH1*>(hlist->FindObject(histoname));  
331 }
332
333
334 //_____________________________________________________________________________
335 TString
336 AliHistogramCollection::KeyA(const char* identifier) const
337 {
338   /// Extract the first element of the key pair from an identifier
339   
340   return InternalDecode(identifier,0);
341 }
342
343 //_____________________________________________________________________________
344 TString
345 AliHistogramCollection::KeyB(const char* identifier) const
346 {
347   /// Extract the second element (if present) 
348   return InternalDecode(identifier,1);
349 }
350
351 //_____________________________________________________________________________
352 TString
353 AliHistogramCollection::KeyC(const char* identifier) const
354 {
355   /// Extract the 3rd element (if present) 
356   return InternalDecode(identifier,2);
357 }
358
359 //_____________________________________________________________________________
360 TString
361 AliHistogramCollection::KeyD(const char* identifier) const
362 {
363   /// Extract the 4th element (if present) 
364   return InternalDecode(identifier,3);
365 }
366
367 //_____________________________________________________________________________
368 Long64_t
369 AliHistogramCollection::Merge(TCollection* list)
370 {
371   // Merge a list of AliHistogramCollection objects with this
372   // Returns the number of merged objects (including this).
373   
374   if (!list) return 0;
375   
376   if (list->IsEmpty()) return 1;
377   
378   TIter next(list);
379   TObject* o;
380   TList mapList;
381   Int_t count(0);
382   
383   while ( ( o = next() ) )
384   {
385     AliHistogramCollection* hcol = dynamic_cast<AliHistogramCollection*>(o);
386     if (!hcol) {
387       AliFatal(Form("object named \"%s\" is a %s instead of an AliHistogramCollection!", o->GetName(), o->ClassName()));
388       continue;
389     }
390     
391     ++count;
392     
393     TIter nextIdentifier(hcol->fMap);
394     TObjString* identifier;
395
396     while ( ( identifier = static_cast<TObjString*>(nextIdentifier()) ) )
397     {
398       THashList* otherList = static_cast<THashList*>(hcol->fMap->GetValue(identifier->String().Data()));
399
400       TIter nextHisto(otherList);
401       TH1* h;
402       
403       while ( ( h = static_cast<TH1*>(nextHisto()) ) )
404       {
405         TH1* thisHisto = Histo(KeyA(identifier->String()).Data(),
406                                KeyB(identifier->String()).Data(),
407                                KeyC(identifier->String()).Data(),
408                                KeyD(identifier->String()).Data(),
409                                h->GetName());
410         
411         if (!thisHisto)
412         {
413           AliDebug(1,Form("Adopting a new histo = %s/%s",identifier->String().Data(),h->GetName()));
414           
415           // this is an histogram we don't have yet. Let's add it
416           Adopt(KeyA(identifier->String()),
417                 KeyB(identifier->String()),
418                 KeyC(identifier->String()),
419                 KeyD(identifier->String()),
420                 static_cast<TH1*>(h->Clone()));
421         }
422         else
423         {
424           // add it...
425           AliDebug(1,Form("Merging histo = %s/%s (%g vs %g)",
426                           identifier->String().Data(),
427                           h->GetName(),
428                           h->GetSumOfWeights(),
429                           thisHisto->GetSumOfWeights()));
430           TList l;
431           l.Add(h);
432           
433           thisHisto->Merge(&l);
434         }
435       }
436     }
437   }
438          
439   AliDebug(1,Form("count=%d",count));
440   
441   return count+1;
442 }
443
444 //_____________________________________________________________________________
445 Int_t 
446 AliHistogramCollection::NumberOfHistograms() const
447 {
448   /// Get the number of histograms we hold
449   TIter next(CreateIterator(this));
450   Int_t n(0);
451   while ( next() ) ++n;
452   return n;
453 }
454
455 //_____________________________________________________________________________
456 Int_t 
457 AliHistogramCollection::NumberOfKeys() const
458 {
459   /// Get the number of keys we have
460   return fMap ? fMap->GetSize() : 0;
461 }
462
463 //_____________________________________________________________________________
464 void 
465 AliHistogramCollection::Print(Option_t* option) const
466 {
467   /// Print all the histograms we hold, in a hopefully visually pleasing
468   /// way.
469   ///
470   /// Option can be used to select given part only, using the schema :
471   /// /*/*/*/*/*
472   /// Where the stars are wilcards for /keyA/keyB/keyC/KeyD/histoname
473   ///
474   /// if * is used it is assumed to be a wildcard for histoname
475   ///
476   /// For other selections the full syntax /*/*/*/*/* must be used.
477   /// 
478   /// Use "-" as histoname to disable histogram's name output
479   
480   cout << Form("AliHistogramCollection : %d keys and %d histos",
481                NumberOfKeys(), NumberOfHistograms()) << endl;
482   
483   if (!strlen(option)) return;
484   
485   TString sreKeyA("*");
486   TString sreKeyB("*");
487   TString sreKeyC("*");
488   TString sreKeyD("*");
489   TString sreHistoname("*");
490   
491   TObjArray* select = TString(option).Tokenize("/");
492   Int_t n = select->GetLast();
493   
494   if (n>=0)
495   {
496     sreHistoname = static_cast<TObjString*>(select->At(n))->String();
497   }
498   if (n>=1)
499   {
500     sreKeyD = static_cast<TObjString*>(select->At(n-1))->String();    
501   }
502   if (n>=2)
503   {
504     sreKeyC = static_cast<TObjString*>(select->At(n-2))->String();    
505   }
506   if (n>=3)
507   {
508     sreKeyB = static_cast<TObjString*>(select->At(n-3))->String();    
509   }
510   if (n>=4)
511   {
512     sreKeyA = static_cast<TObjString*>(select->At(n-3))->String();    
513   }
514   
515   TRegexp reKeyA(sreKeyA,kTRUE);
516   TRegexp reKeyB(sreKeyB,kTRUE);
517   TRegexp reKeyC(sreKeyC,kTRUE);
518   TRegexp reKeyD(sreKeyD,kTRUE);
519   TRegexp reHistoname(sreHistoname,kTRUE);
520
521   delete select;
522   
523   TObjArray* identifiers = SortAllIdentifiers();
524   
525   TIter nextIdentifier(identifiers);
526   
527   TObjString* sid(0x0);
528   
529   while ( ( sid = static_cast<TObjString*>(nextIdentifier()) ) )
530   {
531     Bool_t identifierPrinted(kFALSE);
532
533     TString identifier(sid->String());
534     
535     if ( InternalDecode(identifier,0).Contains(reKeyA) &&
536         InternalDecode(identifier,1).Contains(reKeyB) &&
537         InternalDecode(identifier,2).Contains(reKeyC) &&
538         InternalDecode(identifier,3).Contains(reKeyD)
539         )
540     {
541       if ( sreHistoname == "*" ) 
542       {
543         identifierPrinted = kTRUE;
544         cout << identifier.Data() << endl;
545       }
546       
547       THashList * list = static_cast<THashList*>(fMap->GetValue(sid->String().Data()));
548       TObjArray names;
549       names.SetOwner(kTRUE);
550       TIter nextUnsortedHisto(list);
551       TH1* h;
552       while ( ( h = static_cast<TH1*>(nextUnsortedHisto()) ) )
553       {
554         names.Add(new TObjString(h->GetName()));
555       }
556       names.Sort();
557       TIter nextHistoName(&names);
558       TObjString* hname;
559       while ( ( hname = static_cast<TObjString*>(nextHistoName()) ) )
560       {
561         TString histoName(hname->String());
562         if (histoName.Contains(reHistoname) )
563         {
564           h = static_cast<TH1*>(list->FindObject(histoName.Data()));
565           if ( h->GetEntries()==0 && !fMustShowEmptyHistogram ) continue;
566           if (!identifierPrinted)
567           {
568             cout << identifier.Data() << endl;
569             identifierPrinted = kTRUE;
570           }
571           cout << Form("    %s %s Entries=%d Sum=%g",h->GetName(),h->GetTitle(),Int_t(h->GetEntries()),h->GetSumOfWeights()) << endl;
572         }
573       }
574       if (!identifierPrinted && sreHistoname=="-" )
575       { 
576         // to handle the case where we used histoname="-" to disable showing the histonames,
577         // but we still want to see the matching keys maybe...
578         cout << identifier.Data() << endl;
579       }
580     }
581   }
582   
583   delete identifiers;
584 }
585
586 //_____________________________________________________________________________
587 UInt_t 
588 AliHistogramCollection::EstimateSize(Bool_t show) const
589 {
590   /// Estimate the memory (in kilobytes) used by our histograms
591   
592 //  sizeof(TH1) + (nbins+2)*(nbytes_per_bin) +name+title_sizes 
593 //  if you have errors add (nbins+2)*8 
594     
595   TIter next(CreateIterator());
596   TH1* h;
597   UInt_t n(0);
598   
599   while ( ( h = static_cast<TH1*>(next()) ) )
600   {
601     Int_t nbins = (h->GetNbinsX()+2);
602     
603     if (h->GetNbinsY()>1)
604     {
605       nbins *= (h->GetNbinsY()+2);
606     }
607     
608     if (h->GetNbinsZ()>1)
609     {
610       nbins *= (h->GetNbinsZ()+2);
611     }
612       
613     Bool_t hasErrors = ( h->GetSumw2N() > 0 );
614     
615     TString cname(h->ClassName());
616     
617     Int_t nbytesPerBin(0);
618     
619     if (cname.Contains(TRegexp("C$")) ) nbytesPerBin = sizeof(Char_t);
620     if (cname.Contains(TRegexp("S$")) ) nbytesPerBin = sizeof(Short_t);
621     if (cname.Contains(TRegexp("I$")) ) nbytesPerBin = sizeof(Int_t);
622     if (cname.Contains(TRegexp("F$")) ) nbytesPerBin = sizeof(Float_t);
623     if (cname.Contains(TRegexp("D$")) ) nbytesPerBin = sizeof(Double_t);
624         
625     if (!nbytesPerBin)
626     {
627       AliError(Form("Could not get the number of bytes per bin for histo %s of class %s. Thus the size estimate will be wrong !",
628                     h->GetName(),h->ClassName()));
629       continue;
630     }
631     
632     UInt_t thissize = sizeof(h) + nbins*(nbytesPerBin) + strlen(h->GetName())
633     + strlen(h->GetTitle());
634     
635     if ( hasErrors) thissize += nbins*8;
636
637     n += thissize;
638     
639     if ( show ) 
640     {
641       AliInfo(Form("Size of %30s is %20d bytes",h->GetName(),thissize));
642     }
643   }
644
645   return n;
646 }
647
648 //_____________________________________________________________________________
649 void AliHistogramCollection::PruneEmptyHistograms()
650 {
651   /// Delete the empty histograms
652   TIter next(fMap);
653   TObjString* key;
654   
655   TList toBeRemoved;
656   toBeRemoved.SetOwner(kTRUE);
657   
658   while ( ( key = static_cast<TObjString*>(next()) ) )
659   {
660     TString identifier(key->String());
661     THashList* hlist = static_cast<THashList*>(fMap->GetValue(identifier.Data()));
662     TIter nextHisto(hlist);
663     TH1* h;
664     while ( ( h = static_cast<TH1*>(nextHisto())))
665     {
666       if ( h->GetEntries()==0)
667       {
668         toBeRemoved.Add(new TObjString(Form("%s%s",identifier.Data(),h->GetName())));
669       }
670     }
671   }
672   
673   TIter nextTBR(&toBeRemoved);
674   while ( ( key = static_cast<TObjString*>(nextTBR()) ) )
675   {
676     Remove(key);
677   }
678 }
679
680 //_____________________________________________________________________________
681 AliHistogramCollection* 
682 AliHistogramCollection::Project(const char* /*keyA*/, const char* /*keyB*/) const
683 {
684   /// To be implemented : would create a new collection starting at keyA/keyB
685   AliError("Implement me !");
686   return 0x0;
687 }
688
689 //_____________________________________________________________________________
690 TObject* 
691 AliHistogramCollection::Remove(TObject* key)
692 {
693   ///
694   /// Remove a given histogram (given its key=full identifier=/keyA/keyB/keyC/keyD/histoname)
695   ///
696   /// Note that we do *not* remove the /keyA/keyB/keyC/keyD entry even if there's no
697   /// more histogram for this triplet.
698   ///
699   /// Not very efficient. Could be improved ?
700   ///
701   
702   TObjString* str = dynamic_cast<TObjString*>(key);
703   
704   if (!str)
705   {
706     AliError(Form("key is not of the expected TObjString type, but of %s",key->ClassName()));
707     return 0x0;
708   }
709   
710   TString identifier(str->String());
711     
712   TString skey = Form("/%s/%s/%s/%s/",
713                       KeyA(identifier).Data(),
714                       KeyB(identifier).Data(),
715                       KeyC(identifier).Data(),
716                       KeyD(identifier).Data());
717   
718   THashList* hlist = dynamic_cast<THashList*>(fMap->GetValue(skey.Data()));
719   
720   if (!hlist)
721   {
722     AliWarning(Form("Could not get hlist for key=%s",skey.Data()));
723     return 0x0;
724   }
725     
726   TH1* h = InternalHisto(skey,HistoName(identifier.Data()));
727   if (!h)
728   {
729     AliError(Form("Could not find histo %s",identifier.Data()));
730     return 0x0;
731   }
732   
733   TObject* o = hlist->Remove(h);
734   if (!o)
735   {
736     AliError("Remove failed");
737     return 0x0;
738   }
739
740 //  if ( hlist->IsEmpty() ) 
741 //  {
742 //    // we should remove the key as well
743 //    TObject* k = fMap->Remove(key);
744 //    if (!k)
745 //    {
746 //      AliError("Removal of the key failed");
747 //    }
748 //  }
749   
750   return o;
751 }
752
753 //_____________________________________________________________________________
754 TObjArray*
755 AliHistogramCollection::SortAllIdentifiers() const
756 {
757   /// Sort our internal identifiers. Returned array must be deleted.
758   TObjArray* identifiers = new TObjArray;
759   identifiers->SetOwner(kFALSE); 
760   TIter next(fMap);
761   TObjString* sid;
762   
763   while ( ( sid = static_cast<TObjString*>(next()) ) )
764   {
765     if ( !identifiers->FindObject(sid->String().Data()) )
766     {
767       identifiers->Add(sid);      
768     }
769   }
770   identifiers->Sort();
771   return identifiers;
772 }
773
774
775 ///////////////////////////////////////////////////////////////////////////////
776 //
777 // AliHistogramCollectionIterator
778 //
779 ///////////////////////////////////////////////////////////////////////////////
780
781 class AliHistogramCollectionIterator;
782
783 //_____________________________________________________________________________
784 AliHistogramCollectionIterator::AliHistogramCollectionIterator(const AliHistogramCollection* hcol, Bool_t dir)
785 : fkHistogramCollection(hcol), fMapIterator(0x0), fHashListIterator(0x0), fDirection(dir)
786 {
787   /// Default ctor
788 }
789
790 //_____________________________________________________________________________
791 AliHistogramCollectionIterator&
792 AliHistogramCollectionIterator::operator=(const TIterator&)
793 {
794   /// Overriden operator= (imposed by Root's declaration of TIterator ?)
795   Fatal("TIterator::operator=","Not implementeable"); // because there's no clone in TIterator :-(
796   return *this;
797 }
798
799 //_____________________________________________________________________________
800 AliHistogramCollectionIterator::~AliHistogramCollectionIterator()
801 {
802   /// dtor
803   Reset();
804 }
805
806 //_____________________________________________________________________________
807 TObject* AliHistogramCollectionIterator::Next()
808 {
809   /// Advance to next object in the collection
810   
811   if (!fHashListIterator)
812   {
813     if ( !fMapIterator ) 
814     {
815       fMapIterator = fkHistogramCollection->fMap->MakeIterator(fDirection);
816     }
817     TObjString* key = static_cast<TObjString*>(fMapIterator->Next());
818     if (!key)
819     {
820       // we are done
821       return 0x0;
822     }      
823     THashList* list = static_cast<THashList*>(fkHistogramCollection->fMap->GetValue(key->String().Data()));
824     if (!list) return 0x0;
825     fHashListIterator = list->MakeIterator(fDirection);
826   }
827
828   TObject* o = fHashListIterator->Next();
829   
830   if (!o) 
831   {
832     delete fHashListIterator;
833     fHashListIterator = 0x0;
834     return Next();
835   }
836   
837   return o;
838 }
839
840 //_____________________________________________________________________________
841 void AliHistogramCollectionIterator::Reset()
842 {
843   /// Reset the iterator
844   delete fHashListIterator;
845   delete fMapIterator;
846 }