    // @(#)root/meta:$Name:  $:$Id: TMethod.cxx,v 1.6 2004/03/12 08:53:04 brun Exp $
    // Author: Rene Brun   09/02/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.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    //  Each ROOT class (see TClass) has a linked list of methods.          //
    //  This class describes one single method (member function).           //
    //  The method info is obtained via the CINT api. See class TCint.      //
    //                                                                      //
    //  The method information is used a.o. by the THml class and by the    //
    //  TTree class.                                                        //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TClass.h"
    #include "TMethod.h"
    #include "TMethodArg.h"
    #include "TMethodCall.h"
    #include "TROOT.h"
    #include "TApplication.h"
    #include "TInterpreter.h"
    #include "Strlen.h"
    #include "Api.h"
    #include "TDataMember.h"

    ClassImp(TMethod)

    //______________________________________________________________________________
     TMethod::TMethod(G__MethodInfo *info, TClass *cl) : TFunction(info)
    {
       // Default TMethod ctor. TMethods are constructed in TClass.
       // Comment strings are pre-parsed to find out whether the method is
       // a context-menu item.

       fClass        = cl;
       fGetterMethod = 0;
       fSetterMethod = 0;

       if (fInfo) {
          const char *t = fInfo->Title();

          if (t && strstr(t, "*TOGGLE")) {
             fMenuItem = kMenuToggle;
             const char *s;
             if ((s = strstr(t, "*GETTER="))) {
                fGetter = s+8;
                fGetter = fGetter.Strip(TString::kBoth);
             }
          } else
          if (t && strstr(t, "*MENU"))
             fMenuItem = kMenuDialog;
          else
          if (t && strstr(t, "*SUBMENU"))
             {
             fMenuItem = kMenuSubMenu;
             }
          else
             fMenuItem = kMenuNoMenu;
       }
    }

    //______________________________________________________________________________
     TMethod::TMethod(const TMethod& orig) : TFunction(orig)
    {
       // Copy ctor.

       fClass        = orig.fClass;
       fMenuItem     = orig.fMenuItem;
       fGetter       = orig.fGetter;
       fGetterMethod = 0;
       fSetterMethod = 0;
    }

    //______________________________________________________________________________
    TMethod& TMethod::operator=(const TMethod& rhs)
    {
       // Assignment operator.

       if (this != &rhs) {
          TFunction::operator=(rhs);
          fClass        = rhs.fClass;
          fMenuItem     = rhs.fMenuItem;
          fGetter       = rhs.fGetter;
          if (fGetterMethod)
             delete fGetterMethod;
          fGetterMethod = 0;
          if (fSetterMethod)
             delete fSetterMethod;
          fSetterMethod = 0;
       }
       return *this;
    }

    //______________________________________________________________________________
     TMethod::~TMethod()
    {
       // Cleanup.

       delete fGetterMethod;
       delete fSetterMethod;
    }

    //______________________________________________________________________________
     TObject *TMethod::Clone(const char *newname) const
    {
       // Clone method.

       TNamed *newobj = new TMethod(*this);
       if (newname && strlen(newname)) newobj->SetName(newname);
       return newobj;
    }

    //______________________________________________________________________________
     const char *TMethod::GetCommentString()
    {
        // Returns a comment string from the class declaration.

        return fInfo->Title();
    }

    //______________________________________________________________________________
     void TMethod::CreateSignature()
    {
       // Using the CINT method arg information create a complete signature string.

       TFunction::CreateSignature();

       if (Property() & kIsConstant) fSignature += " const";
    }

    //______________________________________________________________________________
     TDataMember *TMethod::FindDataMember()
    {
       // Tries to guess DataMember from comment string
       // and Method's name <==(only if 1 Argument!).
       // If more then one argument=> returns pointer to the last argument.
       // It also sets MethodArgs' pointers to point to specified data members.
       //
       // The form of comment string defining arguments is:
       // void XXX(Int_t x1, Float_t y2) //*ARGS={x1=>fX1,y2=>fY2}
       // where fX1, fY2 are data fields in the same class.
       // ("pointers" to data members)

       Char_t *argstring = (char*)strstr(GetCommentString(),"*ARGS={");

       // the following statement has been commented (Rene). Not needed
       // it was making troubles in BuildRealData for classes with protected
       // default constructors.
       // if (!(GetClass()->GetListOfRealData())) GetClass()->BuildRealData();

       if (argstring) {

          // if we found any argument-specifying hints  - parse it

          if (!fMethodArgs) return 0;

          char argstr[2048];    // workspace...
          char *ptr1 = 0;
          char *tok  = 0;
          char *ptr2 = 0;
          Int_t i;

          strcpy(argstr,argstring);       //let's move it to "worksapce"  copy

          ptr2 = strtok(argstr,"{}");     //extract the data!
          ptr2 = strtok((char*)0,"{}");

          //extract argument tokens//
          char *tokens[20];
          Int_t cnt       = 0;
          Int_t token_cnt = 0;
          do {
              ptr1 = strtok((char*) (cnt++ ? 0:ptr2),",;"); //extract tokens
                                                            // separated by , or ;
              if (ptr1) {
                 tok = new char[strlen(ptr1)+1];
                 strcpy(tok,ptr1);
                 tokens[token_cnt] = tok;            //store this token.
                 token_cnt++;
              }
          } while (ptr1);

          //now let's  parse all argument tokens...
          TClass     *cl = 0;
          TMethodArg *a  = 0;
          TMethodArg *ar = 0;
          TDataMember *member = 0;

          for (i=0; i<token_cnt;i++) {
             cnt = 0;
             ptr1 = strtok(tokens[i],"=>");  //LeftHandedSide=methodarg
             ptr2 = strtok((char*)0,"=>"); //RightHandedSide-points to datamember

             //find the MethodArg
             a      = 0;
             ar     = 0;
             member = 0;
             TIter nextarg(fMethodArgs);     // iterate through all arguments.
             while ((ar = (TMethodArg*)nextarg())) {
                if (!strcmp(ptr1,ar->GetName())) {
                    a = ar;
                   break;
                }
             }

             //now find the data member
             cl = GetClass()->GetBaseDataMember(ptr2);
             if (cl) {
                member = cl->GetDataMember(ptr2);
                if (a) a->fDataMember = member; //SET THE APROPRIATE FIELD !!!
                                         //We can do it - friend decl. in MethodArg
             }
             delete tokens[i];
          }
          return member; // nothing else to do! We return a pointer to the last
                         // found data member

       // if not found in comment string - try to guess it from name!
       } else {
          if (fMethodArgs)
            if (fMethodArgs->GetSize() != 1) return 0;

          TMethodArg *a = 0;
          if (fMethodArgs) a = (TMethodArg*)(fMethodArgs->First());

          char dataname[64]    = "";
          char basename[64]    = "";
          const char *funcname = GetName();
          if ( strncmp(funcname,"Get",3) == 0 || strncmp(funcname,"Set",3) == 0 )
             sprintf(basename,"%s",funcname+3);
          else if ( strncmp(funcname,"Is",2) == 0 )
             sprintf(basename,"%s",funcname+2);
          else if (strncmp(funcname, "Has", 3) == 0)
             sprintf(basename, "%s", funcname+3);
          else
             return 0;

          sprintf(dataname,"f%s",basename);

          TClass *cl = GetClass()->GetBaseDataMember(dataname);
          if (cl) {
             TDataMember *member   = cl->GetDataMember(dataname);
             if (a) a->fDataMember = member;
             return member;
          } else {
             sprintf(dataname,"fIs%s",basename);  //in case of IsEditable()
                                                            //and fIsEditable
             cl = GetClass()->GetBaseDataMember(dataname);
             if (cl) {
               TDataMember *member = cl->GetDataMember(dataname);
               if (a) a->fDataMember = member;
               return member;
             }
          }
       }

        //if nothing found - return null -pointer:
        return 0;
    }

    //______________________________________________________________________________
     TMethodCall *TMethod::GetterMethod()
    {
       // Return call environment for the getter method in case this is a
       // *TOGGLE method (for the context menu).

       if (!fGetterMethod && fMenuItem == kMenuToggle && fGetter != "" && fClass) {
          fGetterMethod = new TMethodCall(fClass, Getter(), "");
       }
       return fGetterMethod;
    }

    //______________________________________________________________________________
     TMethodCall *TMethod::SetterMethod()
    {
       // Return call environment for this method in case this is a
       // *TOGGLE method which takes a single boolean or integer argument.

       if (!fSetterMethod && fMenuItem == kMenuToggle && fClass) {
          fSetterMethod = new TMethodCall(fClass, GetName(), "1");
       }
       return fSetterMethod;
    }

    //______________________________________________________________________________
     TList *TMethod::GetListOfMethodArgs()
    {
       // Returns methodarg list and additionally updates fDataMember in TMethod by
       // calling FindDataMember();

       if (!fMethodArgs){
          TFunction::GetListOfMethodArgs();
          FindDataMember();
       }
       return fMethodArgs;
    }