// @(#)root/cont:$Name:  $:$Id: TClassTable.cxx,v 1.28 2004/07/08 00:28:39 rdm Exp $
// Author: Fons Rademakers   11/08/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// This class registers for all classes their name, id and dictionary   //
// function in a hash table. Classes are automatically added by the     //
// ctor of a special init class when a global of this init class is     //
// initialized when the program starts (see the ClassImp macro).        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <string>
#include <map>
#include "Riostream.h"

#include "TClassTable.h"
#include "TClass.h"
#include "TClassEdit.h"
#include "TROOT.h"
#include "TMath.h"
#include "TString.h"
#include "TError.h"
#include "TRegexp.h"

#include "TObjString.h"
#include "TMap.h"

TClassTable *gClassTable;

ClassRec_t **TClassTable::fgTable;
ClassRec_t **TClassTable::fgSortedTable;
int          TClassTable::fgSize;
int          TClassTable::fgTally;
Bool_t       TClassTable::fgSorted;
int          TClassTable::fgCursor;
TClassTable::IdMap_t *TClassTable::fgIdMap;

ClassImp(TClassTable)

//______________________________________________________________________________
namespace ROOT {

   class TMapTypeToClassRec {
#if defined R__USE_STD_MAP
     // This wrapper class allow to avoid putting #include <map> in the
     // TROOT.h header file.
   public:
#ifdef R__GLOBALSTL
      typedef map<string,ClassRec_t*>           IdMap_t;
#else
      typedef std::map<std::string,ClassRec_t*> IdMap_t;
#endif
      typedef IdMap_t::key_type                 key_type;
      typedef IdMap_t::const_iterator           const_iterator;
      typedef IdMap_t::size_type                size_type;
#ifdef R__WIN32
      // Window's std::map does NOT defined mapped_type
      typedef ClassRec_t*                       mapped_type;
#else
      typedef IdMap_t::mapped_type              mapped_type;
#endif

   private:
      IdMap_t fMap;

   public:
      void Add(const key_type &key, mapped_type &obj) {
         fMap[key] = obj;
      }

      mapped_type Find(const key_type &key) const {

         IdMap_t::const_iterator iter = fMap.find(key);
         mapped_type cl = 0;
         if (iter != fMap.end()) cl = iter->second;
         return cl;
      }

      void Remove(const key_type &key) { fMap.erase(key); }

      void Print() {
         Info("TMapTypeToClassRec::Print", "printing the typeinfo map in TClassTable");
         for (const_iterator iter = fMap.begin(); iter != fMap.end(); iter++) {
            printf("Key: %40s 0x%x\n", iter->first.c_str(), iter->second);
         }
      }
#else
   private:
      TMap fMap;
   public:
      void Add(const char *key, ClassRec_t *&obj) {
         TObjString *realkey = new TObjString(key);
         fMap.Add(realkey, (TObject*)obj);
      }

      ClassRec_t *Find(const char *key) const {
         const TPair *a = (const TPair *)fMap.FindObject(key);
         if (a) return (ClassRec_t*) a->Value();
         return 0;
      }

      void Remove(const char *key) {
         TObjString realkey(key);
         TObject *actual = fMap.Remove(&realkey);
         delete actual;
      }

      void Print() {
         Info("TMapTypeToClassRec::Print", "printing the typeinfo map in TClassTable");
         TIter next(&fMap);
         TObjString *key;
         while((key = (TObjString*)next())) {
            printf("Key: %s\n",key->String().Data());
            ClassRec_t *data = (ClassRec_t*)fMap.GetValue(key);
            if (data) {
               printf("  class: %s %d\n",data->name,data->id);
            } else {
               printf("  no class: \n");
            }
         }
      }
#endif
   };
}

//______________________________________________________________________________
TClassTable::TClassTable()
{
   // TClassTable is a singleton (i.e. only one can exist per application).

   if (gClassTable) return;

   fgSize  = (int)TMath::NextPrime(1000);
   fgTable = new ClassRec_t* [fgSize];
   fgIdMap = new IdMap_t;
   memset(fgTable, 0, fgSize*sizeof(ClassRec_t*));
   gClassTable = this;
}

//______________________________________________________________________________
TClassTable::~TClassTable()
{
   // TClassTable singleton is deleted in Terminate().

   // Try to avoid spurrious warning from memory leak checkers.
   if (gClassTable != this) return;

   for (Int_t i = 0; i < fgSize; i++) {
      ClassRec_t *r = fgTable[i];
      while (r) {
         delete [] r->name;
         ClassRec_t *next = r->next;
         delete r;
         r = next;
      }
   }
   delete [] fgTable; fgTable = 0;
   delete [] fgSortedTable; fgSortedTable = 0;
   delete fgIdMap; fgIdMap = 0;
}

//______________________________________________________________________________
void TClassTable::Print(Option_t *option) const
{
   // Print the class table. Before printing the table is sorted
   // alphabetically. Only classes specified in option are listed.
   // The default is to list all classes.
   // Standard wilcarding notation supported.

   if (fgTally == 0 || !fgTable)
      return;

   SortTable();

   int n = 0, ninit = 0, nl = 0;

   int nch = strlen(option);
   TRegexp re(option, kTRUE);

   Printf("");
   Printf("Defined classes");
   Printf("class                                 version  bits  initialized");
   Printf("================================================================");
   for (int i = 0; i < fgTally; i++) {
      ClassRec_t *r = fgSortedTable[i];
      n++;
      TString s = r->name;
      if (nch && strcmp(option,r->name) && s.Index(re) == kNPOS) continue;
      nl++;
      if (gROOT->GetClass(r->name, kFALSE)) {
         ninit++;
         Printf("%-35s %6d %7d       Yes", r->name, r->id, r->bits);
      } else
         Printf("%-35s %6d %7d       No",  r->name, r->id, r->bits);
   }
   Printf("----------------------------------------------------------------");
   Printf("Listed Classes: %4d  Total classes: %4d   initialized: %4d",nl, n, ninit);
   Printf("================================================================");

   Printf("");
}

//---- static members --------------------------------------------------------

//______________________________________________________________________________
int   TClassTable::Classes() { return fgTally; }
//______________________________________________________________________________
void  TClassTable::Init() { fgCursor = 0; SortTable(); }


//______________________________________________________________________________
void TClassTable::Add(const char *cname, Version_t id,  const type_info &info,
                      VoidFuncPtr_t dict, Int_t pragmabits)
{
   // Add a class to the class table (this is a static function).

   if (!gClassTable)
      new TClassTable;

  // check if already in table, if so return
   ClassRec_t *r = FindElement(cname, kTRUE);
   if (r->name) {
      if (TClassEdit::IsSTLCont(cname)==0) {
         // Warn only for class that are not STL containers.
         ::Warning("TClassTable::Add", "class %s already in TClassTable", cname);
      }
      return;
   }

   r->name = StrDup(cname);
   r->id   = id;
   r->bits = pragmabits;
   r->dict = dict;
   r->info = &info;

   fgIdMap->Add(info.name(),r);

   fgTally++;
   fgSorted = kFALSE;
}

//______________________________________________________________________________
void TClassTable::Remove(const char *cname)
{
   // Remove a class from the class table. This happens when a shared library
   // is unloaded (i.e. the dtor's of the global init objects are called).

   if (!gClassTable || !fgTable) return;

   int slot = 0;
   const char *p = cname;

   while (*p) slot = slot<<1 ^ *p++;
   if (slot < 0) slot = -slot;
   slot %= fgSize;

   ClassRec_t *r;
   ClassRec_t *prev = 0;
   for (r = fgTable[slot]; r; r = r->next) {
      if (!strcmp(r->name, cname)) {
         if (prev)
            prev->next = r->next;
         else
            fgTable[slot] = r->next;
         fgIdMap->Remove(r->info->name());
         delete [] r->name;
         delete r;
         fgTally--;
         fgSorted = kFALSE;
         break;
      }
      prev = r;
   }
}

//______________________________________________________________________________
ClassRec_t *TClassTable::FindElement(const char *cname, Bool_t insert)
{
   // Find a class by name in the class table (using hash of name). Returns
   // 0 if the class is not in the table. Unless arguments insert is true in
   // which case a new entry is created and returned.

   if (!fgTable) return 0;

   int slot = 0;
   const char *p = cname;

   while (*p) slot = slot<<1 ^ *p++;
   if (slot < 0) slot = -slot;
   slot %= fgSize;

   ClassRec_t *r;

   for (r = fgTable[slot]; r; r = r->next)
      if (!strcmp(r->name, cname)) return r;

   if (!insert) return 0;

   r = new ClassRec_t;
   r->name = 0;
   r->id   = 0;
   r->dict = 0;
   r->info = 0;
   r->next = fgTable[slot];
   fgTable[slot] = r;

   return r;
}

//______________________________________________________________________________
Version_t TClassTable::GetID(const char *cname)
{
   // Returns the ID of a class.

   ClassRec_t *r = FindElement(cname);
   if (r) return r->id;
   return -1;
}

//______________________________________________________________________________
Int_t TClassTable::GetPragmaBits(const char *cname)
{
   // Returns the pragma bits as specified in the LinkDef.h file.

   ClassRec_t *r = FindElement(cname);
   if (r) return r->bits;
   return 0;
}

//______________________________________________________________________________
VoidFuncPtr_t TClassTable::GetDict(const char *cname)
{
   // Given the class name returns the Dictionary() function of a class
   // (uses hash of name).

   if (gDebug > 9) {
      ::Info("GetDict", "searches for %s", cname);
      fgIdMap->Print();
   }

   ClassRec_t *r = FindElement(cname);
   if (r) return r->dict;
   return 0;
}

//______________________________________________________________________________
VoidFuncPtr_t TClassTable::GetDict(const type_info& info)
{
   // Given the type_info returns the Dictionary() function of a class
   // (uses hash of type_info::name()).

   if (gDebug > 9) {
      ::Info("GetDict", "searches for %s at 0x%x", info.name(), &info);
      fgIdMap->Print();
   }

   ClassRec_t *r = fgIdMap->Find(info.name());
   if (r) return r->dict;
   return 0;
}


//______________________________________________________________________________
extern "C" {
   static int ClassComp(const void *a, const void *b)
   {
      // Function used for sorting classes alphabetically.

      return strcmp((*(ClassRec_t **)a)->name, (*(ClassRec_t **)b)->name);
   }
}

//______________________________________________________________________________
char *TClassTable::Next()
{
    // Returns next class from sorted class table.

    if (fgCursor < fgTally) {
       ClassRec_t *r = fgSortedTable[fgCursor++];
       return r->name;
    } else
       return 0;
}

//______________________________________________________________________________
void TClassTable::PrintTable()
{
   // Print the class table. Before printing the table is sorted
   // alphabetically.

   if (fgTally == 0 || !fgTable)
      return;

   SortTable();

   int n = 0, ninit = 0;

   Printf("");
   Printf("Defined classes");
   Printf("class                                 version  bits  initialized");
   Printf("================================================================");
   for (int i = 0; i < fgTally; i++) {
      ClassRec_t *r = fgSortedTable[i];
      n++;
      if (gROOT->GetClass(r->name, kFALSE)) {
         ninit++;
         Printf("%-35s %6d %7d       Yes", r->name, r->id, r->bits);
      } else
         Printf("%-35s %6d %7d       No",  r->name, r->id, r->bits);
   }
   Printf("----------------------------------------------------------------");
   Printf("Total classes: %4d   initialized: %4d", n, ninit);
   Printf("================================================================");

   Printf("");
}

//______________________________________________________________________________
void TClassTable::SortTable()
{
   // Sort the class table by ascending class ID's.

   if (!fgSorted) {
      delete [] fgSortedTable;
      fgSortedTable = new ClassRec_t* [fgTally];

      int j = 0;
      for (int i = 0; i < fgSize; i++)
         for (ClassRec_t *r = fgTable[i]; r; r = r->next)
            fgSortedTable[j++] = r;

      ::qsort(fgSortedTable, fgTally, sizeof(ClassRec_t *), ::ClassComp);
      fgSorted = kTRUE;
   }
}

//______________________________________________________________________________
void TClassTable::Terminate()
{
   // Deletes the class table (this static class function calls the dtor).

   if (gClassTable) {
      for (int i = 0; i < fgSize; i++)
         for (ClassRec_t *r = fgTable[i]; r; ) {
            ClassRec_t *t = r;
            r = r->next;
            fgIdMap->Remove(r->info->name());
            delete [] t->name;
            delete t;
         }
      delete [] fgTable; fgTable = 0;
      delete [] fgSortedTable; fgSortedTable = 0;
      delete fgIdMap; fgIdMap = 0;
      fgSize = 0;
      SafeDelete(gClassTable);
   }
}

//______________________________________________________________________________
void ROOT::AddClass(const char *cname, Version_t id,
                    const type_info& info,
                    VoidFuncPtr_t dict,
                    Int_t pragmabits)
{
   // Global function called by the ctor of a class's init class
   // (see the ClassImp macro).

   TClassTable::Add(cname, id, info, dict, pragmabits);
}

//______________________________________________________________________________
void ROOT::ResetClassVersion(TClass *cl, const char *cname, Short_t newid)
{
   // Global function to update the version number.
   // This is called via the RootClassVersion macro.

   if (cname) {
      ClassRec_t *r = TClassTable::FindElement(cname,kFALSE);
      if (r) r->id = newid;
   }
   if (cl) {
      if (cl->fVersionUsed) {
         // Problem, the reset is called after the first usage!
         Error("ResetClassVersion","Version number of %s can not be changed after first usage!",
               cl->GetName());
      } else {
         cl->SetClassVersion(newid);
      }
   }
}


//______________________________________________________________________________
void ROOT::RemoveClass(const char *cname)
{
   // Global function called by the dtor of a class's init class
   // (see the ClassImp macro).

   // don't delete class information since it is needed by the I/O system
   // to write the StreamerInfo to file
   if (cname) {
     // Let's still remove this information to allow reloading later.
     // Anyway since the shared library has been unloaded, the dictionary
     // pointer is now invalid ....
     // We still keep the TClass object around because TFile needs to
     // get to the TStreamerInfo.
     if (gROOT && gROOT->GetListOfClasses()) {
        TClass *cl = dynamic_cast<TClass*>(gROOT->GetListOfClasses()->FindObject(cname));
        if (cl) cl->SetUnloaded();
     }
     TClassTable::Remove(cname);
   }
}

//______________________________________________________________________________
TNamed *ROOT::RegisterClassTemplate(const char *name, const char *file,
                                    Int_t line)
{
   // Global function to register the implementation file and line of
   // a class template (i.e. NOT a concrete class).

   static TList table;

   TString classname(name);
   Ssiz_t loc = classname.Index("<");
   if (loc >= 1) classname.Remove(loc);
   if (file) {
      TNamed *obj = new TNamed((const char*)classname, file);
      obj->SetUniqueID(line);
      table.Add(obj);
      return obj;
   } else {
      return (TNamed*)table.FindObject(classname);
   }
}

