TDocParser.cxx

Go to the documentation of this file.
00001 // @(#)root/html:$Id: TDocParser.cxx 35733 2010-09-25 12:51:30Z axel $
00002 // Author: Axel Naumann 2007-01-09
00003 
00004 /*************************************************************************
00005  * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers.               *
00006  * All rights reserved.                                                  *
00007  *                                                                       *
00008  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010  *************************************************************************/
00011 
00012 #include "TDocParser.h"
00013 
00014 #include "Riostream.h"
00015 #include "TBaseClass.h"
00016 #include "TClass.h"
00017 #include "TClassDocOutput.h"
00018 #include "TDataMember.h"
00019 #include "TDataType.h"
00020 #include "TDatime.h"
00021 #include "TDocDirective.h"
00022 #include "TEnv.h"
00023 #include "TGlobal.h"
00024 #include "THtml.h"
00025 #include "TInterpreter.h"
00026 #include "TMethod.h"
00027 #include "TMethodArg.h"
00028 #include "TPRegexp.h"
00029 #include "TROOT.h"
00030 #include "TSystem.h"
00031 #include "TVirtualMutex.h"
00032 #include <string>
00033 
00034 namespace {
00035 
00036    class TMethodWrapperImpl: public TDocMethodWrapper {
00037    public:
00038       TMethodWrapperImpl(TMethod* m, int overloadIdx):
00039          fMeth(m), fOverloadIdx(overloadIdx) {}
00040 
00041       static void SetClass(const TClass* cl) { fgClass = cl; }
00042 
00043       const char* GetName() const { return fMeth->GetName(); }
00044       ULong_t Hash() const { return fMeth->Hash();}
00045       Int_t GetNargs() const { return fMeth->GetNargs(); }
00046       virtual TMethod* GetMethod() const { return fMeth; }
00047       Bool_t IsSortable() const { return kTRUE; }
00048 
00049       Int_t GetOverloadIdx() const { return fOverloadIdx; }
00050 
00051       Int_t Compare(const TObject *obj) const {
00052          const TMethodWrapperImpl* m = dynamic_cast<const TMethodWrapperImpl*>(obj);
00053          if (!m) return 1;
00054 
00055          Int_t ret = strcasecmp(GetName(), m->GetName());
00056          if (ret == 0) {
00057             if (GetNargs() < m->GetNargs()) return -1;
00058             else if (GetNargs() > m->GetNargs()) return 1;
00059             if (GetMethod()->GetClass()->InheritsFrom(m->GetMethod()->GetClass()))
00060                return -1;
00061             else 
00062                return 1;
00063          }
00064 
00065          const char* l(GetName());
00066          const char* r(m->GetName());
00067          if (l[0] == '~' && r[0] == '~') {
00068             ++l;
00069             ++r;
00070          }
00071          if (fgClass->InheritsFrom(l)) {
00072             if (fgClass->InheritsFrom(r)) {
00073                if (gROOT->GetClass(l)->InheritsFrom(r))
00074                   return -1;
00075                else return 1;
00076             } else return -1;
00077          } else if (fgClass->InheritsFrom(r))
00078             return 1;
00079 
00080          if (l[0] == '~') return -1;
00081          if (r[0] == '~') return 1;
00082          return (ret < 0) ? -1 : 1;
00083       }
00084 
00085    private:
00086       static const TClass* fgClass; // current class, defining inheritance sort order
00087       TMethod* fMeth; // my method
00088       Int_t fOverloadIdx; // this is the n-th overload
00089    };
00090 
00091    const TClass* TMethodWrapperImpl::fgClass = 0;
00092 }
00093 
00094 
00095 //______________________________________________________________________________
00096 ////////////////////////////////////////////////////////////////////////////////
00097 // 
00098 // Parse C++ source or header, and extract documentation.
00099 //
00100 // Also handles special macros like
00101 /* Begin_Macro(GUI, source)
00102 {
00103    TGMainFrame* f = new TGMainFrame(0, 100, 100);
00104    f->SetName("testMainFrame"); // that's part of the name of the image
00105    TGButton* b = new TGTextButton(f, "Test Button");
00106    f->AddFrame(b);
00107    f->MapSubwindows();
00108    f->Resize(f->GetDefaultSize());
00109 
00110    f->MapWindow();
00111    return f; // *HIDE*
00112 }
00113 End_Macro */
00114 // or multiline Latex aligned at =:
00115 /* Begin_Latex(separator='=',align=rcl) C = d #sqrt{#frac{2}{#lambdaD}} #int^{x}_{0}cos(#frac{#pi}{2}t^{2})dt 
00116  D(x) = d End_Latex */
00117 // even without alignment: Begin_Latex
00118 // x=sin^2(y)
00119 // y = #sqrt{sin(x)}
00120 // End_Latex and what about running an external macro?
00121 /* BEGIN_MACRO(source)
00122 
00123 
00124 testmacro.C END_MACRO
00125 
00126 
00127 and some nested stuff which doesn't work yet: */
00128 // BEGIN_HTML
00129 /* BEGIN_LATEX Wow,^{an}_{image}^{inside}_{a}^{html}_{block}
00130    END_LATEX
00131 */
00132 // END_HTML
00133 ////////////////////////////////////////////////////////////////////////////////
00134 
00135 ClassImp(TDocParser);
00136 
00137 std::set<std::string>  TDocParser::fgKeywords;
00138 
00139 //______________________________________________________________________________
00140 TDocParser::TDocParser(TClassDocOutput& docOutput, TClass* cl):
00141    fHtml(docOutput.GetHtml()), fDocOutput(&docOutput), fLineNo(0),
00142    fCurrentClass(cl), fRecentClass(0), fCurrentModule(0),
00143    fDirectiveCount(0), fLineNumber(0), fDocContext(kIgnore), 
00144    fCheckForMethod(kFALSE), fClassDocState(kClassDoc_Uninitialized), 
00145    fCommentAtBOL(kFALSE), fAllowDirectives(kTRUE)
00146 {
00147    // Constructor called for parsing class sources
00148 
00149    InitKeywords();
00150 
00151    fSourceInfoTags[kInfoLastUpdate] = fHtml->GetLastUpdateTag();
00152    fSourceInfoTags[kInfoAuthor]     = fHtml->GetAuthorTag();
00153    fSourceInfoTags[kInfoCopyright]  = fHtml->GetCopyrightTag();
00154 
00155    fClassDescrTag = fHtml->GetClassDocTag();
00156 
00157    TMethodWrapperImpl::SetClass(cl);
00158 
00159    for (int ia = 0; ia < 3; ++ia) {
00160       fMethods[ia].Rehash(101);
00161    }
00162 
00163    AddClassMethodsRecursively(0);
00164    AddClassDataMembersRecursively(0);
00165 
00166    // needed for list of methods,...
00167    fParseContext.push_back(kCode);
00168 
00169    // create an array of method names
00170    TMethod *method;
00171    TIter nextMethod(fCurrentClass->GetListOfMethods());
00172    fMethodCounts.clear();
00173    while ((method = (TMethod *) nextMethod())) {
00174       ++fMethodCounts[method->GetName()];
00175    }
00176 
00177 }
00178 
00179 //______________________________________________________________________________
00180 TDocParser::TDocParser(TDocOutput& docOutput):
00181    fHtml(docOutput.GetHtml()), fDocOutput(&docOutput), fLineNo(0),
00182    fCurrentClass(0), fRecentClass(0), fDirectiveCount(0),
00183    fLineNumber(0), fDocContext(kIgnore), 
00184    fCheckForMethod(kFALSE), fClassDocState(kClassDoc_Uninitialized),
00185    fCommentAtBOL(kFALSE), fAllowDirectives(kFALSE)
00186 {
00187    // constructor called for parsing text files with Convert()
00188    InitKeywords();
00189 
00190    fSourceInfoTags[kInfoLastUpdate] = fHtml->GetLastUpdateTag();
00191    fSourceInfoTags[kInfoAuthor]     = fHtml->GetAuthorTag();
00192    fSourceInfoTags[kInfoCopyright]  = fHtml->GetCopyrightTag();
00193 
00194    fClassDescrTag = fHtml->GetClassDocTag();
00195 
00196    TMethodWrapperImpl::SetClass(0);
00197 }
00198 
00199 //______________________________________________________________________________
00200 TDocParser::~TDocParser()
00201 {
00202    // destructor, checking whether all methods have been found for gDebug > 3
00203    if (gDebug > 3) {
00204       for (std::map<std::string, Int_t>::const_iterator iMethod = fMethodCounts.begin();
00205          iMethod != fMethodCounts.end(); ++iMethod)
00206          if (iMethod->second)
00207             Info("~TDocParser", "Implementation of method %s::%s could not be found.",
00208             fCurrentClass ? fCurrentClass->GetName() : "",
00209             iMethod->first.c_str());
00210       TIter iDirective(&fDirectiveHandlers);
00211       TDocDirective* directive = 0;
00212       while ((directive = (TDocDirective*) iDirective())) {
00213          TString directiveName;
00214          directive->GetName(directiveName);
00215          Warning("~TDocParser", "Missing \"%s\" for macro %s", directive->GetEndTag(), directiveName.Data());
00216       }
00217    }
00218 }
00219 
00220 //______________________________________________________________________________
00221 void TDocParser::AddClassMethodsRecursively(TBaseClass* bc)
00222 {
00223    // Add accessible (i.e. non-private) methods of base class bc 
00224    // and its base classes' methods to methodNames.
00225    // If bc==0, we add fCurrentClass's methods (and also private functions).
00226 
00227    // make a loop on member functions
00228    TClass *cl = fCurrentClass;
00229    if (bc) 
00230       cl = bc->GetClassPointer(kFALSE);
00231    if (!cl) return;
00232 
00233    TMethod *method;
00234    TIter nextMethod(cl->GetListOfMethods());
00235    std::map<std::string, int> methOverloads;
00236 
00237    while ((method = (TMethod *) nextMethod())) {
00238 
00239       if (!strcmp(method->GetName(), "Dictionary") ||
00240           !strcmp(method->GetName(), "Class_Version") ||
00241           !strcmp(method->GetName(), "Class_Name") ||
00242           !strcmp(method->GetName(), "DeclFileName") ||
00243           !strcmp(method->GetName(), "DeclFileLine") ||
00244           !strcmp(method->GetName(), "ImplFileName") ||
00245           !strcmp(method->GetName(), "ImplFileLine") ||
00246           (bc && (method->GetName()[0] == '~' // d'tor
00247              || !strcmp(method->GetName(), method->GetReturnTypeName()))) // c'tor
00248           )
00249          continue;
00250 
00251 
00252       Int_t mtype = 0;
00253       if (kIsPrivate & method->Property())
00254          mtype = 0;
00255       else if (kIsProtected & method->Property())
00256          mtype = 1;
00257       else if (kIsPublic & method->Property())
00258          mtype = 2;
00259 
00260       if (bc) {
00261          if (mtype == 0) continue;
00262          if (bc->Property() & kIsPrivate)
00263             mtype = 0;
00264          else if ((bc->Property() & kIsProtected) && mtype == 2)
00265             mtype = 1;
00266       }
00267 
00268       Bool_t hidden = kFALSE;
00269       for (Int_t access = 0; !hidden && access < 3; ++access) {
00270          TMethodWrapperImpl* other = (TMethodWrapperImpl*) fMethods[access].FindObject(method->GetName());
00271          hidden |= (other) && (other->GetMethod()->GetClass() != method->GetClass());
00272       }
00273       if (!hidden) {
00274          fMethods[mtype].Add(new TMethodWrapperImpl(method, methOverloads[method->GetName()]));
00275          ++methOverloads[method->GetName()];
00276       }
00277    }
00278 
00279    TIter iBase(cl->GetListOfBases());
00280    TBaseClass* base = 0;
00281    while ((base = (TBaseClass*)iBase()))
00282       AddClassMethodsRecursively(base);
00283 
00284    if (!bc)
00285       for (Int_t access = 0; access < 3; ++access) {
00286          fMethods[access].SetOwner();
00287          fMethods[access].Sort();
00288       }
00289 }
00290 
00291 //______________________________________________________________________________
00292 void TDocParser::AddClassDataMembersRecursively(TBaseClass* bc) {
00293    // Add data members of fCurrentClass and of bc to datamembers, recursively.
00294    // Real data members are in idx 0..2 (public, protected, private access),
00295    // enum constants in idx 3..5.
00296 
00297    // make a loop on member functions
00298    TClass *cl = fCurrentClass;
00299    if (bc) 
00300       cl = bc->GetClassPointer(kFALSE);
00301    if (!cl) return;
00302 
00303    TDataMember *dm;
00304    TIter nextDM(cl->GetListOfDataMembers());
00305 
00306    while ((dm = (TDataMember *) nextDM())) {
00307       if (!strcmp(dm->GetName(), "fgIsA"))
00308          continue;
00309       Int_t mtype = 0;
00310       if (kIsPrivate & dm->Property())
00311          mtype = 0;
00312       else if (kIsProtected & dm->Property())
00313          mtype = 1;
00314       else if (kIsPublic & dm->Property())
00315          mtype = 2;
00316 
00317       if (bc) {
00318          if (mtype == 0) continue;
00319          if (bc->Property() & kIsPrivate)
00320             mtype = 0;
00321          else if ((bc->Property() & kIsProtected) && mtype == 2)
00322             mtype = 1;
00323       }
00324 
00325       const Int_t flagEnumConst = G__BIT_ISENUM | G__BIT_ISCONSTANT | G__BIT_ISSTATIC;
00326       if ((dm->Property() & flagEnumConst) == flagEnumConst
00327           && dm->GetDataType() && dm->GetDataType()->GetType() == kInt_t) {
00328          mtype = 5;
00329          // The access of the enum constant is defined by the access of the enum:
00330          // for CINT, all enum constants are public.
00331          // There is no TClass or TDataType for enum types; instead, use CINT:
00332          /*
00333            No - CINT does not know their access restriction.
00334            With CINT5 we have no way of determining it...
00335 
00336          ClassInfo_t* enumCI = gInterpreter->ClassInfo_Factory(dm->GetTypeName());
00337          if (enumCI) {
00338             Long_t prop = gInterpreter->ClassInfo_Property(enumCI);
00339             if (kIsPrivate & prop)
00340                mtype = 3;
00341             else if (kIsProtected & prop)
00342                mtype = 4;
00343             else if (kIsPublic & prop)
00344                mtype = 5;
00345             gInterpreter->ClassInfo_Delete(enumCI);
00346          }
00347          */
00348       }
00349 
00350       fDataMembers[mtype].Add(dm);
00351    }
00352 
00353    TIter iBase(cl->GetListOfBases());
00354    TBaseClass* base = 0;
00355    while ((base = (TBaseClass*)iBase()))
00356       AddClassDataMembersRecursively(base);
00357 
00358    if (!bc)
00359       for (Int_t access = 0; access < 6; ++access) {
00360          fDataMembers[access].SetOwner(kFALSE);
00361          if (access < 3) // don't sort enums; we keep them in enum tag order
00362             fDataMembers[access].Sort();
00363       }
00364 }
00365 
00366 
00367 //______________________________________________________________________________
00368 void TDocParser::AnchorFromLine(const TString& line, TString& anchor) {
00369    // Create an anchor from the given line, by hashing it and
00370    // convertig the hash into a custom base64 string.
00371 
00372    const char base64String[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.";
00373 
00374    // use hash of line instead of e.g. line number.
00375    // advantages: more stable (lines can move around, we still find them back),
00376    // no need for keeping a line number context
00377    UInt_t hash = ::Hash(line);
00378    anchor.Remove(0);
00379    // force first letter to be [A-Za-z], to be id compatible
00380    anchor += base64String[hash % 52];
00381    hash /= 52;
00382    while (hash) {
00383       anchor += base64String[hash % 64];
00384       hash /= 64;
00385    }
00386 }
00387 
00388 //______________________________________________________________________________
00389 void TDocParser::Convert(std::ostream& out, std::istream& in, const char* relpath,
00390                          Bool_t isCode, Bool_t interpretDirectives)
00391 {
00392    // Parse text file "in", add links etc, and write output to "out".
00393    // If "isCode", "in" is assumed to be C++ code.
00394    fLineNumber = 0;
00395    fParseContext.clear();
00396    if (isCode) fParseContext.push_back(kCode);
00397    else        fParseContext.push_back(kComment); // so we can find "BEGIN_HTML"/"END_HTML" in plain text
00398 
00399    while (!in.eof()) {
00400       fLineRaw.ReadLine(in, kFALSE);
00401       ++fLineNumber;
00402       if (in.eof())
00403          break;
00404 
00405       // remove leading spaces
00406       fLineComment = "";
00407       fLineSource = fLineRaw;
00408       fLineStripped = fLineRaw;
00409       Strip(fLineStripped);
00410 
00411       DecorateKeywords(fLineSource);
00412       ProcessComment();
00413 
00414       // Changes in this bit of code have consequences for:
00415       // * module index,
00416       // * source files,
00417       // * THtml::Convert() e.g. in tutorials/html/MakeTutorials.C
00418       if (!interpretDirectives) {
00419          // Only write the raw, uninterpreted directive code:
00420          if (!InContext(kDirective)) {
00421             GetDocOutput()->AdjustSourcePath(fLineSource, relpath);
00422             out << fLineSource << endl;
00423          }
00424       } else {
00425          // Write source for source and interpreted directives if they exist.
00426          if (fLineComment.Length() ) {   
00427             GetDocOutput()->AdjustSourcePath(fLineComment, relpath);     
00428             out << fLineComment << endl;         
00429          } else if (!InContext(kDirective)) {
00430             GetDocOutput()->AdjustSourcePath(fLineSource, relpath);
00431             out << fLineSource << endl;
00432          }
00433       }
00434    }
00435 }
00436 
00437 //______________________________________________________________________________
00438 void TDocParser::DecorateKeywords(std::ostream& out, const char *text)
00439 {
00440    // Expand keywords in text, writing to out.
00441    TString str(text);
00442    DecorateKeywords(str);
00443    out << str;
00444 }
00445 
00446 //______________________________________________________________________________
00447 void TDocParser::DecorateKeywords(TString& line)
00448 {
00449    // Find keywords in line and create URLs around them. Escape characters with a 
00450    // special meaning for HTML. Protect "Begin_Html"/"End_Html" pairs, and set the
00451    // parsing context. Evaluate sequences like a::b->c.
00452    // Skip regions where directives are active.
00453 
00454    std::list<TClass*> currentType;
00455 
00456    enum {
00457       kNada,
00458       kMember,
00459       kScope,
00460       kNumAccesses
00461    } scoping = kNada;
00462 
00463    currentType.push_back(0);
00464 
00465    Ssiz_t i = 0;
00466    while (isspace((UChar_t)line[i]))
00467       ++i;
00468 
00469    Ssiz_t startOfLine = i;
00470 
00471    // changed when the end of a directive is encountered, i.e. 
00472    // from where fLineSource needs to be appended to fLineComment
00473    Ssiz_t copiedToCommentUpTo = 0;
00474 
00475    if (InContext(kDirective) && fDirectiveHandlers.Last()) {
00476       // we're only waiting for an "End_Whatever" and ignoring everything else
00477       TDocDirective* directive = (TDocDirective*)fDirectiveHandlers.Last();
00478       const char* endTag = directive->GetEndTag();
00479       Ssiz_t posEndTag = i;
00480       while (kNPOS != (posEndTag = line.Index(endTag, posEndTag, TString::kIgnoreCase)))
00481          if (posEndTag == 0 || line[posEndTag - 1] != '"') // escaping '"'
00482             break;
00483       if (posEndTag != kNPOS)
00484          i = posEndTag;
00485       else {
00486          Ssiz_t start = 0;
00487          if (!InContext(kComment) || (InContext(kComment) & kCXXComment)) {
00488             // means we are in a C++ comment
00489             while (isspace((UChar_t)fLineRaw[start])) ++start;
00490             if (fLineRaw[start] == '/' && fLineRaw[start + 1] == '/')
00491                start += 2;
00492             else start = 0;
00493          }
00494          directive->AddLine(fLineRaw(start, fLineRaw.Length()));
00495          while(i < line.Length())
00496             fDocOutput->ReplaceSpecialChars(line, i);
00497          copiedToCommentUpTo = i;
00498       }
00499    }
00500 
00501    for (; i < line.Length(); ++i) {
00502 
00503       if (!currentType.back())
00504          scoping = kNada;
00505 
00506       // evaluate scope relation
00507       if (Context() == kCode 
00508          || Context() == kComment) {
00509          if (currentType.back())
00510             switch (line[i]) {
00511                case ':':
00512                   if (line[i + 1] == ':') {
00513                      scoping = kScope;
00514                      i += 1;
00515                      continue;
00516                   }
00517                   break;
00518                case '-':
00519                   if (line[i + 1] == '>') {
00520                      scoping = kMember;
00521                      i += 1;
00522                      continue;
00523                   }
00524                   break;
00525                case '.':
00526                   if (line[i + 1] != '.') {
00527                      // prevent "..."
00528                      scoping = kMember;
00529                      continue;
00530                   }
00531                   break;
00532             }
00533          switch (line[i]) {
00534             case '(':
00535                currentType.push_back(0);
00536                scoping = kNada;
00537                continue;
00538                break;
00539             case ')':
00540                if (currentType.size() > 1)
00541                   currentType.pop_back();
00542                scoping = kMember;
00543                continue;
00544                break;
00545          }
00546          if (i >= line.Length()) 
00547             break;
00548       } else // code or comment
00549          currentType.back() = 0;
00550 
00551 
00552       if (!IsWord(line[i])){
00553 
00554          Bool_t haveHtmlEscapedChar = Context() == kString 
00555             && i > 2 && line[i] == '\'' && line[i-1] == ';';
00556          if (haveHtmlEscapedChar) {
00557             Ssiz_t posBegin = i - 2;
00558             while (posBegin > 0 && IsWord(line[posBegin])) 
00559                --posBegin;
00560             haveHtmlEscapedChar = posBegin > 0 && 
00561                line[posBegin] == '&' && line[posBegin - 1] == '\'';
00562          }
00563          EParseContext context = Context();
00564          Bool_t closeString = context == kString
00565             && (  line[i] == '"' 
00566                || (line[i] == '\'' 
00567                    && (  (i > 1 && line[i - 2] == '\'') 
00568                       || (i > 3 && line[i - 2] == '\\' && line[i - 3] == '\'')))
00569                || haveHtmlEscapedChar)
00570             && (i == 0 || line[i - 1] != '\\'); // but not "foo \"str...
00571          if (context == kCode || context == kComment) {
00572             if (line[i] == '"' || (line[i] == '\'' && (
00573                   // 'a'
00574                   (line.Length() > i + 2 && line[i + 2] == '\'') ||
00575                   // '\a'
00576                   (line.Length() > i + 3 && line[i + 1] == '\'' && line[i + 3] == '\'')))) {
00577 
00578                fDocOutput->DecorateEntityBegin(line, i, kString);
00579                fParseContext.push_back(kString);
00580                currentType.back() = 0;
00581                closeString = kFALSE;
00582             } else if (context == kCode
00583                && line[i] == '/' && (line[i+1] == '/' || line[i+1] == '*')) {
00584                fParseContext.push_back(kComment);
00585                if (line[i+1] == '/')
00586                   fParseContext.back() |= kCXXComment;
00587                currentType.back() = 0;
00588                fDocOutput->DecorateEntityBegin(line, i, kComment);
00589                ++i;
00590             } else if (context == kComment 
00591                && !(fParseContext.back() & kCXXComment)
00592                && line.Length() > i + 1 
00593                && line[i] == '*' && line[i+1] == '/') {
00594                if (fParseContext.size()>1)
00595                   fParseContext.pop_back();
00596 
00597                currentType.back() = 0;
00598                i += 2;
00599                fDocOutput->DecorateEntityEnd(line, i, kComment);
00600                if (!fCommentAtBOL) {
00601                   if (InContext(kDirective))
00602                      ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(line(copiedToCommentUpTo, i));
00603                   else
00604                      fLineComment += line(copiedToCommentUpTo, i);
00605                   copiedToCommentUpTo = i;
00606                }
00607             } else if (startOfLine == i
00608                && line[i] == '#'
00609                && context == kCode) {
00610                ExpandCPPLine(line, i);
00611             }
00612          } // if context is comment or code
00613 
00614          if (i < line.Length())
00615             fDocOutput->ReplaceSpecialChars(line, i);
00616 
00617          if (closeString) {
00618             fDocOutput->DecorateEntityEnd(line, i, kString);
00619             if (fParseContext.size()>1)
00620                fParseContext.pop_back();
00621 
00622             currentType.back() = 0;
00623          }
00624          --i; // i already moved by ReplaceSpecialChar
00625 
00626          continue;
00627       } // end of "not a word"
00628 
00629       // get the word
00630       Ssiz_t endWord = i;
00631       while (endWord < line.Length() && IsName(line[endWord]))
00632          endWord++;
00633 
00634       if (Context() == kString || Context() == kCPP) {
00635          // don't replace in strings, cpp, etc
00636          i = endWord - 1;
00637          continue;
00638       }
00639 
00640       TString word(line(i, endWord - i));
00641 
00642       // '"' escapes handling of "Begin_..."/"End_..."
00643       if ((i == 0 || (i > 0 && line[i - 1] != '"'))
00644          && HandleDirective(line, i, word, copiedToCommentUpTo)) {
00645          // something special happened; the currentType is gone.
00646          currentType.back() = 0;
00647          continue;
00648       }
00649 
00650       // don't replace keywords in comments
00651       if (Context() == kCode
00652          && fgKeywords.find(word.Data()) != fgKeywords.end()) {
00653          fDocOutput->DecorateEntityBegin(line, i, kKeyword);
00654          i += word.Length();
00655          fDocOutput->DecorateEntityEnd(line, i, kKeyword);
00656          --i; // -1 for ++i
00657          currentType.back() = 0;
00658          continue;
00659       }
00660 
00661       // Now decorate scopes and member, referencing their documentation:
00662 
00663       // generic layout:
00664       // A::B::C::member[arr]->othermember
00665       // we iterate through this, first scope is A, and currentType will be set toA,
00666       // next we see ::B, "::" signals to use currentType,...
00667 
00668       TDataType* subType = 0;
00669       TClass* subClass = 0;
00670       TDataMember *datamem = 0;
00671       TMethod *meth = 0;
00672       const char* globalTypeName = 0;
00673       if (!currentType.size()) {
00674          Warning("DecorateKeywords", "type context is empty!");
00675          currentType.push_back(0);
00676       }
00677       TClass* lookupScope = currentType.back();
00678 
00679       if (scoping == kNada) {
00680          if (fCurrentClass)
00681             lookupScope = fCurrentClass;
00682          else
00683             lookupScope = fRecentClass;
00684       }
00685 
00686       if (scoping == kNada) {
00687          subType = gROOT->GetType(word);
00688          if (!subType)
00689             subClass = fHtml->GetClass(word);
00690          if (!subType && !subClass) {
00691             TGlobal *global = gROOT->GetGlobal(word);
00692             if (global) {
00693                // cannot doc globals; take at least their type...
00694                globalTypeName = global->GetTypeName();
00695                subClass = fHtml->GetClass(globalTypeName);
00696                if (!subClass)
00697                   subType = gROOT->GetType(globalTypeName);
00698                else // hack to prevent current THtml obj from showing up - we only want gHtml
00699                   if (subClass == THtml::Class() && word != "gHtml")
00700                      subClass = 0;
00701             }
00702          }
00703          if (!subType && !subClass) {
00704             // too bad - cannot doc yet...
00705             //TFunction *globFunc = gROOT->GetGlobalFunctionWithPrototype(word);
00706             //globFunc = 0;
00707          }
00708          if (!subType && !subClass) {
00709             // also try template
00710             while (isspace(line[endWord])) ++endWord;
00711             if (line[endWord] == '<' || line[endWord] == '>') {
00712                // check for possible template
00713                Ssiz_t endWordT = endWord + 1;
00714                int templateLevel = 1;
00715                while (endWordT < line.Length()
00716                       && (templateLevel
00717                           || IsName(line[endWordT])
00718                           || line[endWordT] == '<' 
00719                           || line[endWordT] == '>')) {
00720                   if (line[endWordT] == '<')
00721                      ++templateLevel;
00722                   else if (line[endWordT] == '>')
00723                      --templateLevel;
00724                   endWordT++;
00725                }
00726                subClass = fHtml->GetClass(line(i, endWordT - i).Data());
00727                if (subClass)
00728                   word = line(i, endWordT - i);
00729             }
00730          }
00731       }
00732 
00733       if (lookupScope && !subType && !subClass) {
00734          if (scoping == kScope) {
00735             TString subClassName(lookupScope->GetName());
00736             subClassName += "::";
00737             subClassName += word;
00738             subClass = fHtml->GetClass(subClassName);
00739             if (!subClass)
00740                subType = gROOT->GetType(subClassName);
00741          }
00742          if (!subClass && !subType) {
00743             // also try A::B::c()
00744             datamem = lookupScope->GetDataMember(word);
00745             if (!datamem)
00746                meth = lookupScope->GetMethodAllAny(word);
00747          }
00748          if (!subClass && !subType && !datamem && !meth) {
00749             // also try template
00750             while (isspace(line[endWord])) ++endWord;
00751             if (line[endWord] == '<' || line[endWord] == '>') {
00752                // check for possible template
00753                Ssiz_t endWordT = endWord + 1;
00754                int templateLevel = 1;
00755                while (endWordT < line.Length()
00756                       && (templateLevel
00757                           || IsName(line[endWordT])
00758                           || line[endWordT] == '<' 
00759                           || line[endWordT] == '>')) {
00760                   if (line[endWordT] == '<')
00761                      ++templateLevel;
00762                   else if (line[endWordT] == '>')
00763                      --templateLevel;
00764                   endWordT++;
00765                }
00766                TString subClassName(lookupScope->GetName());
00767                subClassName += "::";
00768                subClassName += line(i, endWordT - i);
00769                subClass = fHtml->GetClass(subClassName);
00770                if (subClass)
00771                   word = line(i, endWordT - i);
00772             }
00773          }
00774       }
00775       // create the link
00776       TString mangledWord(word);
00777       fDocOutput->ReplaceSpecialChars(mangledWord);
00778       line.Replace(i, word.Length(), mangledWord);
00779 
00780       TSubString substr(line(i, mangledWord.Length()));
00781       if (subType) {
00782          fDocOutput->ReferenceEntity(substr, subType,
00783             globalTypeName ? globalTypeName : subType->GetName());
00784          currentType.back() = 0;
00785       } else if (subClass) {
00786          fDocOutput->ReferenceEntity(substr, subClass,
00787             globalTypeName ? globalTypeName : subClass->GetName());
00788 
00789          currentType.back() = subClass;
00790          fRecentClass = subClass;
00791       } else if (datamem || meth) {
00792             if (datamem) {
00793                fDocOutput->ReferenceEntity(substr, datamem);
00794 
00795                if (datamem->GetTypeName())
00796                   currentType.back() = fHtml->GetClass(datamem->GetTypeName());
00797             } else {
00798                fDocOutput->ReferenceEntity(substr, meth);
00799 
00800                TString retTypeName = meth->GetReturnTypeName();
00801                if (retTypeName.BeginsWith("const "))
00802                   retTypeName.Remove(0,6);
00803                Ssiz_t pos=0;
00804                while (IsWord(retTypeName[pos]) || retTypeName[pos]=='<' || retTypeName[pos]=='>' || retTypeName[pos]==':')
00805                   ++pos;
00806                retTypeName.Remove(pos, retTypeName.Length());
00807                if (retTypeName.Length())
00808                   currentType.back() = fHtml->GetClass(retTypeName);
00809             }
00810       } else
00811          currentType.back() = 0;
00812 
00813       //i += mangledWord.Length();
00814       i += substr.Length();
00815 
00816       --i; // due to ++i
00817    } // while i < line.Length()
00818    if (i > line.Length())
00819       i = line.Length();
00820 
00821    // clean up, no strings across lines
00822    if (Context() == kString) {
00823       fDocOutput->DecorateEntityEnd(line, i, kString);
00824       if (fParseContext.size()>1)
00825          fParseContext.pop_back();
00826       currentType.back() = 0;
00827    }
00828 
00829    // HandleDirective already copied the chunk before the directive
00830    // from fLineSource to fLineComment. So we're done up to "i" in
00831    // fLineSource; next time we encounter a directive we just need
00832    // to copy from startOfComment on.
00833    if ((InContext(kComment) || fCommentAtBOL) && copiedToCommentUpTo < line.Length()) {
00834       if (InContext(kDirective))
00835          ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(line(copiedToCommentUpTo, line.Length()));
00836       else
00837          fLineComment += line(copiedToCommentUpTo, line.Length());
00838    }
00839 
00840    // Do this after we append to fLineComment, otherwise the closing 
00841    // </span> gets sent to the directive.
00842    // clean up, no CPP comment across lines
00843    if (InContext(kComment) & kCXXComment) {
00844       fDocOutput->DecorateEntityEnd(line, i, kComment);
00845       if (fLineComment.Length()) {
00846          Ssiz_t pos = fLineComment.Length();
00847          fDocOutput->DecorateEntityEnd(fLineComment, pos, kComment);
00848       }
00849       RemoveCommentContext(kTRUE);
00850       currentType.back() = 0;
00851    }
00852 }
00853 
00854 //______________________________________________________________________________
00855 void TDocParser::DecrementMethodCount(const char* name)
00856 {
00857    // reduce method count for method called name,
00858    // removing it from fMethodCounts once the count reaches 0.
00859 
00860    typedef std::map<std::string /*method name*/, Int_t > MethodCount_t;
00861    MethodCount_t::iterator iMethodName = fMethodCounts.find(name);
00862    if (iMethodName != fMethodCounts.end()) {
00863       --(iMethodName->second);
00864       if (iMethodName->second <= 0)
00865          fMethodCounts.erase(iMethodName);
00866    }
00867 }
00868 
00869 //______________________________________________________________________________
00870 void  TDocParser::DeleteDirectiveOutput() const
00871 {
00872    // Delete output generated by prior runs of all known directives;
00873    // the output file names might have changes.
00874 
00875    TIter iClass(gROOT->GetListOfClasses());
00876    TClass* cl = 0;
00877    while ((cl = (TClass*) iClass()))
00878       if (cl != TDocDirective::Class()
00879          && cl->InheritsFrom(TDocDirective::Class())) {
00880          TDocDirective* directive = (TDocDirective*) cl->New();
00881          if (!directive) continue;
00882          directive->SetParser(const_cast<TDocParser*>(this));
00883          directive->DeleteOutput();
00884          delete directive;
00885       }
00886 }
00887 
00888 //______________________________________________________________________________
00889 void TDocParser::ExpandCPPLine(TString& line, Ssiz_t& pos)
00890 {
00891 // Expand preprocessor statements
00892 //
00893 //
00894 // Input: line - line containing the CPP statement,
00895 //        pos  - position of '#'
00896 //
00897 //  NOTE: Looks for the #include statements and
00898 //        creates link to the corresponding file
00899 //        if such file exists
00900 //
00901 
00902    Bool_t linkExist    = kFALSE;
00903    Ssiz_t posEndOfLine = line.Length();
00904    Ssiz_t posHash      = pos;
00905 
00906    Ssiz_t posInclude = line.Index("include", pos);
00907    if (posInclude != kNPOS) {
00908       TString filename;
00909       Ssiz_t posStartFilename = posInclude + 7;
00910       if (line.Tokenize(filename, posStartFilename, "[<\"]")) {
00911          Ssiz_t posEndFilename = posStartFilename;
00912          if (line.Tokenize(filename, posEndFilename, "[>\"]")) {
00913             R__LOCKGUARD(fHtml->GetMakeClassMutex());
00914 
00915             TString filesysFileName;
00916             if (fHtml->GetPathDefinition().GetFileNameFromInclude(filename, filesysFileName)) {
00917                fDocOutput->CopyHtmlFile(filesysFileName);
00918 
00919                TString endOfLine(line(posEndFilename - 1, line.Length()));
00920                line.Remove(posStartFilename, line.Length());
00921                for (Ssiz_t i = pos; i < line.Length();)
00922                   fDocOutput->ReplaceSpecialChars(line, i);
00923 
00924                line += "<a href=\"./";
00925                line += gSystem->BaseName(filename);
00926                line += "\">";
00927                line += filename + "</a>" + endOfLine[0]; // add include file's closing '>' or '"'
00928                posEndOfLine = line.Length() - 1; // set the "processed up to" to it
00929                fDocOutput->ReplaceSpecialChars(line, posEndOfLine); // and run replace-special-char on it
00930 
00931                line += endOfLine(1, endOfLine.Length()); // add the unprocessed part of the line back
00932 
00933                linkExist = kTRUE;
00934             }
00935          }
00936       }
00937    }
00938 
00939    if (!linkExist) {
00940       fDocOutput->ReplaceSpecialChars(line);
00941       posEndOfLine = line.Length();
00942    }
00943 
00944    Ssiz_t posHashAfterDecoration = posHash;
00945    fDocOutput->DecorateEntityBegin(line, posHashAfterDecoration, kCPP);
00946    posEndOfLine += posHashAfterDecoration - posHash;
00947 
00948    fDocOutput->DecorateEntityEnd(line, posEndOfLine, kCPP);
00949    pos = posEndOfLine;
00950 }
00951 
00952 
00953 //______________________________________________________________________________
00954 void TDocParser::GetCurrentModule(TString& out_module) const {
00955    // Return the name of module for which sources are currently parsed.
00956    if (fCurrentModule) out_module = fCurrentModule;
00957    else if (fCurrentClass) fHtml->GetModuleNameForClass(out_module, fCurrentClass);
00958    else out_module = "(UNKNOWN MODULE WHILE PARSING)";
00959 }
00960 
00961 //______________________________________________________________________________
00962 Bool_t TDocParser::HandleDirective(TString& line, Ssiz_t& pos, TString& word, 
00963                                    Ssiz_t& copiedToCommentUpTo)
00964 {
00965    // Process directives to the documentation engine, like "Begin_Html" / "End_Html",
00966    // "Begin_Macro" / "End_Macro", and "Begin_Latex" / "End_Latex".
00967    
00968    Bool_t begin = kTRUE;
00969    TClass* clDirective = IsDirective(line, pos, word, begin);
00970    if (!clDirective)
00971       return kFALSE;
00972 
00973    // we'll need end later on: afer the begin block, both end _and_ begin can be true.
00974    Bool_t end = !begin;
00975 
00976    TDocDirective* directive = 0; // allow re-use of object from begin block in end
00977 
00978    if (begin) {
00979       // copy from fLineSource to fLineComment, starting at copiedToCommentUpTo
00980       if (InContext(kDirective))
00981          ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(fLineSource(copiedToCommentUpTo, pos - copiedToCommentUpTo));
00982       else
00983          fLineComment += fLineSource(copiedToCommentUpTo, pos - copiedToCommentUpTo);
00984       copiedToCommentUpTo = pos;
00985 
00986       pos += word.Length(); // skip the keyword
00987 
00988       directive = (TDocDirective*) clDirective->New();
00989       if (!directive)
00990          return kFALSE;
00991 
00992       directive->SetParser(this);
00993       if (fCurrentMethodTag.Length())
00994          directive->SetTag(fCurrentMethodTag);
00995       directive->SetCounter(fDirectiveCount++);
00996 
00997       // parse parameters
00998       TString params;
00999       if (begin && line[pos] == '(') {
01000          std::list<char> waitForClosing;
01001          Ssiz_t endParam = pos + 1;
01002          for (; endParam < line.Length() 
01003             && (line[endParam] != ')' || !waitForClosing.empty()); ++endParam) {
01004             const char c = line[endParam];
01005             if (!waitForClosing.empty() && waitForClosing.back() == c) {
01006                waitForClosing.pop_back();
01007                continue;
01008             }
01009             switch (c) {
01010                case '"':
01011                   if (waitForClosing.empty() || waitForClosing.back() != '\'')
01012                      waitForClosing.push_back('"');
01013                   break;
01014                case '\'':
01015                   if (waitForClosing.empty() || waitForClosing.back() != '"')
01016                      waitForClosing.push_back('\'');
01017                   break;
01018                case '(':
01019                   if (waitForClosing.empty() || (waitForClosing.back() != '"' && waitForClosing.back() != '\''))
01020                      waitForClosing.push_back(')');
01021                   break;
01022                case '\\':
01023                   ++endParam; // skip next char
01024                default:
01025                   break;
01026             };
01027          }
01028          if (waitForClosing.empty()) {
01029             params = line(pos + 1, endParam - (pos + 1));
01030             pos += params.Length() + 2; // params + parentheses
01031          }
01032          directive->SetParameters(params);
01033       }
01034 
01035       // check for end tag in current line
01036       Ssiz_t posEndTag = pos;
01037       const char* endTag = directive->GetEndTag();
01038       Ssiz_t lenEndTag = strlen(endTag);
01039       while (kNPOS != (posEndTag = line.Index(endTag, posEndTag, TString::kIgnoreCase))) {
01040          if (line[posEndTag - 1] == '"') {
01041             posEndTag += lenEndTag;
01042             continue; // escaping '"'
01043          }
01044          break;
01045       }
01046       if (posEndTag != kNPOS) {
01047          end = kTRUE; // we just continue below!
01048       } else {
01049          fDirectiveHandlers.AddLast(directive);
01050 
01051          fParseContext.push_back(kDirective);
01052          if (InContext(kComment) & kCXXComment)
01053             fParseContext.back() |= kCXXComment;
01054 
01055          posEndTag = line.Length();
01056       }
01057 
01058       directive->AddLine(line(pos, posEndTag - pos));
01059       TString remainder(line(posEndTag, line.Length()));
01060       line.Remove(posEndTag, line.Length());
01061 
01062       while (pos < line.Length())
01063          fDocOutput->ReplaceSpecialChars(line, pos);
01064 
01065       pos = line.Length();
01066       // skip the remainder of the line
01067       copiedToCommentUpTo = line.Length();
01068       line += remainder;
01069    }
01070 
01071    // no else - "end" can also be set by begin having an end tag!
01072    if (end) {
01073 
01074       if (!begin)
01075          pos += word.Length(); // skip the keyword
01076       else pos += word.Length() - 2; // "Begin" is 2 chars longer than "End"
01077 
01078       if (!directive) directive = (TDocDirective*) fDirectiveHandlers.Last();
01079 
01080       if (!directive) {
01081          Warning("HandleDirective", "Cannot find directive handler object %s !", 
01082             fLineRaw.Data());
01083          return kFALSE;
01084       }
01085 
01086       if (!begin) {
01087          Ssiz_t start = 0;
01088          if (!InContext(kComment) || (InContext(kComment) & kCXXComment)) {
01089             // means we are in a C++ comment
01090             while (isspace((UChar_t)fLineRaw[start])) ++start;
01091             if (fLineRaw[start] == '/' && fLineRaw[start + 1] == '/')
01092                start += 2;
01093             else start = 0;
01094          }
01095          directive->AddLine(line(start, pos - word.Length() - start));
01096 
01097          TString remainder(line(pos, line.Length()));
01098          line.Remove(pos, line.Length());
01099          fDocOutput->ReplaceSpecialChars(line);
01100          pos = line.Length();
01101          line += remainder;
01102       }
01103       copiedToCommentUpTo = pos;
01104 
01105       TString result;
01106       directive->GetResult(result);
01107 
01108       if (!begin)
01109          fDirectiveHandlers.Remove(fDirectiveHandlers.LastLink());
01110       delete directive;
01111 
01112       if (!begin) {
01113          // common to all directives: pop context
01114          Bool_t isInCxxComment = InContext(kDirective) & kCXXComment;
01115          if (fParseContext.size()>1)
01116             fParseContext.pop_back();
01117          if (isInCxxComment && !InContext(kComment)) {
01118             fParseContext.push_back(kComment | kCXXComment);
01119             fDocOutput->DecorateEntityBegin(line, pos, kComment);
01120          }
01121       }
01122 
01123       if (InContext(kDirective) && fDirectiveHandlers.Last())
01124          ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(result(0, result.Length()));
01125       else 
01126          fLineComment += result;
01127 
01128       /* NO - this can happen e.g. for "BEGIN_HTML / *..." (see doc in this class)
01129       if (Context() != kComment) {
01130          Warning("HandleDirective", "Popping back a directive context, but enclosing context is not a comment! At:\n%s", 
01131             fLineRaw.Data());
01132          fParseContext.push_back(kComment);
01133       }
01134       */
01135    }
01136 
01137    return kTRUE;
01138 }
01139 
01140 //______________________________________________________________________________
01141 UInt_t TDocParser::InContext(Int_t context) const
01142 {
01143    // checks whether we are in a parse context, return the entry closest
01144    // to the current context.
01145    // If context is a EParseContextFlag just look for the first match in 
01146    // the flags
01147 
01148    UInt_t lowerContext = context & kParseContextMask;
01149    UInt_t contextFlag  = context & kParseContextFlagMask;
01150 
01151    for (std::list<UInt_t>::const_reverse_iterator iPC = fParseContext.rbegin();
01152       iPC != fParseContext.rend(); ++iPC)
01153       if (!lowerContext || ((lowerContext && ((*iPC & kParseContextMask) == lowerContext))
01154          && (!contextFlag || (contextFlag && (*iPC & contextFlag)))))
01155          return *iPC;
01156 
01157    return 0;
01158 }
01159 
01160 //______________________________________________________________________________
01161 void TDocParser::InitKeywords() const
01162 {
01163    // fill C++ keywords into fgKeywords
01164 
01165    if (!fgKeywords.empty())
01166       return;
01167 
01168    fgKeywords.insert("asm");
01169    fgKeywords.insert("auto");
01170    fgKeywords.insert("bool");
01171    fgKeywords.insert("break");
01172    fgKeywords.insert("case");
01173    fgKeywords.insert("catch");
01174    fgKeywords.insert("char");
01175    fgKeywords.insert("class");
01176    fgKeywords.insert("const");
01177    fgKeywords.insert("const_cast");
01178    fgKeywords.insert("continue");
01179    fgKeywords.insert("default");
01180    fgKeywords.insert("delete");
01181    fgKeywords.insert("do");
01182    fgKeywords.insert("double");
01183    fgKeywords.insert("dynamic_cast");
01184    fgKeywords.insert("else");
01185    fgKeywords.insert("enum");
01186    fgKeywords.insert("explicit");
01187    fgKeywords.insert("export");
01188    fgKeywords.insert("extern");
01189    fgKeywords.insert("false");
01190    fgKeywords.insert("float");
01191    fgKeywords.insert("for");
01192    fgKeywords.insert("friend");
01193    fgKeywords.insert("goto");
01194    fgKeywords.insert("if");
01195    fgKeywords.insert("inline");
01196    fgKeywords.insert("int");
01197    fgKeywords.insert("long");
01198    fgKeywords.insert("mutable");
01199    fgKeywords.insert("namespace");
01200    fgKeywords.insert("new");
01201    fgKeywords.insert("operator");
01202    fgKeywords.insert("private");
01203    fgKeywords.insert("protected");
01204    fgKeywords.insert("public");
01205    fgKeywords.insert("register");
01206    fgKeywords.insert("reinterpret_cast");
01207    fgKeywords.insert("return");
01208    fgKeywords.insert("short");
01209    fgKeywords.insert("signed");
01210    fgKeywords.insert("sizeof");
01211    fgKeywords.insert("static");
01212    fgKeywords.insert("static_cast");
01213    fgKeywords.insert("struct");
01214    fgKeywords.insert("switch");
01215    fgKeywords.insert("template");
01216    fgKeywords.insert("this");
01217    fgKeywords.insert("throw");
01218    fgKeywords.insert("true");
01219    fgKeywords.insert("try");
01220    fgKeywords.insert("typedef");
01221    fgKeywords.insert("typeid");
01222    fgKeywords.insert("typename");
01223    fgKeywords.insert("union");
01224    fgKeywords.insert("unsigned");
01225    fgKeywords.insert("using");
01226    fgKeywords.insert("virtual");
01227    fgKeywords.insert("void");
01228    fgKeywords.insert("volatile");
01229    fgKeywords.insert("wchar_t");
01230    fgKeywords.insert("while");
01231 }
01232 
01233 //______________________________________________________________________________
01234 TClass* TDocParser::IsDirective(const TString& line, Ssiz_t pos,
01235                                         const TString& word, Bool_t& begin) const
01236 {
01237    // return whether word at line's pos is a valid directive, and returns its 
01238    // TDocDirective's TClass object, or 0 if it's not a directive. Set begin
01239    // to kTRUE for "Begin_..."
01240    // You can implement your own handlers by implementing a class deriving
01241    // from TDocHandler, and calling it TDocTagDirective for "BEGIN_TAG",
01242    // "END_TAG" blocks.
01243 
01244    // '"' serves as escape char
01245    if (pos > 0 &&  line[pos - 1] == '"')
01246       return 0;
01247 
01248    begin      = word.BeginsWith("begin_", TString::kIgnoreCase);
01249    Bool_t end = word.BeginsWith("end_", TString::kIgnoreCase);
01250 
01251    if (!begin && !end) 
01252       return 0;
01253 
01254    /* NO - we can have "BEGIN_HTML / * ..."
01255    if (!InContext(kComment))
01256       return 0;
01257    */
01258 
01259    TString tag = word( begin ? 6 : 4, word.Length());
01260 
01261    if (!tag.Length())
01262       return 0;
01263 
01264    tag.ToLower();
01265    tag[0] -= 'a' - 'A'; // first char is caps
01266    tag.Prepend("TDoc");
01267    tag += "Directive";
01268 
01269    TClass* clDirective = TClass::GetClass(tag, kFALSE);
01270 
01271    if (gDebug > 0 && !clDirective)
01272       Warning("IsDirective", "Unknown THtml directive %s in line %d!", word.Data(), fLineNo);
01273 
01274    return clDirective;
01275 }
01276 
01277 //______________________________________________________________________________
01278 Bool_t TDocParser::IsName(UChar_t c)
01279 {
01280 // Check if c is a valid C++ name character
01281 //
01282 //
01283 //  Input: c - a single character
01284 //
01285 // Output: TRUE if c is a valid C++ name character
01286 //         and FALSE if it's not.
01287 //
01288 //   NOTE: Valid name characters are [a..zA..Z0..9_~],
01289 //
01290 
01291    Bool_t ret = kFALSE;
01292 
01293    if (isalnum(c) || c == '_' || c == '~')
01294       ret = kTRUE;
01295 
01296    return ret;
01297 }
01298 
01299 
01300 //______________________________________________________________________________
01301 Bool_t TDocParser::IsWord(UChar_t c)
01302 {
01303 // Check if c is a valid first character for C++ name
01304 //
01305 //
01306 //  Input: c - a single character
01307 //
01308 // Output: TRUE if c is a valid first character for C++ name,
01309 //         and FALSE if it's not.
01310 //
01311 //   NOTE: Valid first characters are [a..zA..Z_~]
01312 //
01313 
01314    Bool_t ret = kFALSE;
01315 
01316    if (isalpha(c) || c == '_' || c == '~')
01317       ret = kTRUE;
01318 
01319    return ret;
01320 }
01321 
01322 
01323 //______________________________________________________________________________
01324 TMethod* TDocParser::LocateMethodInCurrentLine(Ssiz_t &posMethodName, TString& ret,
01325                                                TString& name, TString& params,
01326                                                Bool_t& isconst, std::ostream &srcOut,
01327                                                TString &anchor, std::ifstream& sourceFile,
01328                                                Bool_t allowPureVirtual)
01329 {
01330    // Search for a method starting at posMethodName, and return its return type, 
01331    // its name, and its arguments. If the end of arguments is not found in the 
01332    // current line, get a new line from sourceFile, beautify it to srcOut, creating
01333    // an anchor as necessary. When this function returns, posMethodName points to the
01334    // end of the function declaration, i.e. right after the arguments' closing bracket.
01335    // If posMethodName == kNPOS, we look for the first matching method in fMethodCounts.
01336 
01337    typedef std::map<std::string /*method name*/, Int_t > MethodCount_t;
01338    isconst = false;
01339 
01340    if (posMethodName == kNPOS) {
01341       name.Remove(0);
01342       TMethod * meth = 0;
01343       Ssiz_t posBlock = fLineRaw.Index('{');
01344       Ssiz_t posQuote = fLineRaw.Index('"');
01345       if (posQuote != kNPOS && (posBlock == kNPOS || posQuote < posBlock))
01346          posBlock = posQuote;
01347       if (posBlock == kNPOS) 
01348          posBlock = fLineRaw.Length();
01349       for (MethodCount_t::iterator iMethodName = fMethodCounts.begin();
01350          !name.Length() && iMethodName != fMethodCounts.end(); ++iMethodName) {
01351          TString lookFor(iMethodName->first);
01352          posMethodName = fLineRaw.Index(lookFor);
01353          if (posMethodName != kNPOS && posMethodName < posBlock 
01354             && (posMethodName == 0 || !IsWord(fLineRaw[posMethodName - 1]))) {
01355             // check whether the method name is followed by optional spaces and
01356             // an opening parathesis
01357             Ssiz_t posMethodEnd = posMethodName + lookFor.Length();
01358             while (isspace((UChar_t)fLineRaw[posMethodEnd])) ++posMethodEnd;
01359             if (fLineRaw[posMethodEnd] == '(') {
01360                meth = LocateMethodInCurrentLine(posMethodName, ret, name, params, isconst,
01361                                                 srcOut, anchor, sourceFile, allowPureVirtual);
01362                if (name.Length())
01363                   return meth;
01364             }
01365          }
01366       }
01367       return 0;
01368    }
01369 
01370    name = fLineRaw(posMethodName, fLineRaw.Length() - posMethodName);
01371 
01372    // extract return type
01373    ret = fLineRaw(0, posMethodName);
01374    if (ret.Length()) {
01375       while (ret.Length() && (IsName(ret[ret.Length() - 1]) || ret[ret.Length()-1] == ':'))
01376          ret.Remove(ret.Length() - 1, 1);
01377       Strip(ret);
01378       Bool_t didSomething = kTRUE;
01379       while (didSomething) {
01380          didSomething = kFALSE;
01381          if (ret.BeginsWith("inline ")) {
01382             didSomething = kTRUE;
01383             ret.Remove(0, 7);
01384          }
01385          if (ret.BeginsWith("static ")) {
01386             didSomething = kTRUE;
01387             ret.Remove(0, 7);
01388          }
01389          if (ret.BeginsWith("virtual ")) {
01390             didSomething = kTRUE;
01391             ret.Remove(0, 8);
01392          }
01393       } // while replacing static, virtual, inline
01394       Strip(ret);
01395    }
01396 
01397    // extract parameters
01398    Ssiz_t posParam = name.First('(');
01399    if (posParam == kNPOS || 
01400       // no strange return types, please
01401       ret.Contains("{") || ret.Contains("}") || ret.Contains("(") || ret.Contains(")")
01402       || ret.Contains("=")) {
01403       ret.Remove(0);
01404       name.Remove(0);
01405       params.Remove(0);
01406       return 0;
01407    }
01408 
01409    if (name.BeginsWith("operator")) {
01410       // op () (...)
01411       Ssiz_t checkOpBracketParam = posParam + 1;
01412       while (isspace((UChar_t)name[checkOpBracketParam])) 
01413          ++checkOpBracketParam;
01414       if (name[checkOpBracketParam] == ')') {
01415          ++checkOpBracketParam;
01416          while (isspace((UChar_t)name[checkOpBracketParam]))
01417             ++checkOpBracketParam;
01418          if (name[checkOpBracketParam] == '(')
01419             posParam = checkOpBracketParam;
01420       }
01421    } // check for op () (...)
01422 
01423    if (posParam == kNPOS) {
01424       ret.Remove(0);
01425       name.Remove(0);
01426       params.Remove(0);
01427       return 0;
01428    }
01429 
01430    params = name(posParam, name.Length() - posParam);
01431    name.Remove(posParam);
01432    while (name.Length() && isspace((UChar_t)name[name.Length() - 1]))
01433       name.Remove(name.Length() - 1);
01434    if (!name.Length()) {
01435       ret.Remove(0);
01436       name.Remove(0);
01437       params.Remove(0);
01438       return 0;
01439    }
01440 
01441    MethodCount_t::const_iterator iMethodName = fMethodCounts.find(name.Data());
01442    if (iMethodName == fMethodCounts.end() || iMethodName->second <= 0) {
01443       ret.Remove(0);
01444       name.Remove(0);
01445       params.Remove(0);
01446       return 0;
01447    }
01448 
01449    // find end of param
01450    Ssiz_t posParamEnd = 1;
01451    Int_t bracketLevel = 1;
01452    while (bracketLevel) {
01453       const char* paramEnd = strpbrk(params.Data() + posParamEnd, ")(\"'");
01454       if (!paramEnd) {
01455          // func with params over multiple lines
01456          // gotta write out this line before it gets lost
01457          if (!anchor.Length()) {
01458             // request an anchor, just in case...
01459             AnchorFromLine(fLineStripped, anchor);
01460             if (srcOut)
01461                srcOut << "<a name=\"" << anchor << "\"></a>";
01462          }
01463          ++fLineNumber;
01464          if (srcOut)
01465             WriteSourceLine(srcOut);
01466 
01467          fLineRaw.ReadLine(sourceFile, kFALSE);
01468          if (sourceFile.eof()) {
01469             Error("LocateMethodInCurrentLine", 
01470                "Cannot find end of signature for function %s!",
01471                name.Data());
01472             break;
01473          }
01474 
01475          fCommentAtBOL = kFALSE;
01476 
01477          // replace class names etc
01478          fLineStripped = fLineRaw;
01479          Strip(fLineStripped);
01480 
01481          fLineSource = fLineRaw;
01482          DecorateKeywords(fLineSource);
01483 
01484          posParamEnd = params.Length();
01485          params += fLineRaw;
01486       } else
01487          posParamEnd = paramEnd - params.Data();
01488       switch (params[posParamEnd]) {
01489          case '(': ++bracketLevel; ++posParamEnd; break;
01490          case ')': --bracketLevel; ++posParamEnd; break;
01491          case '"': // skip ")"
01492             ++posParamEnd;
01493             while (params.Length() > posParamEnd && params[posParamEnd] != '"') {
01494                // skip '\"'
01495                if (params[posParamEnd] == '\\') ++posParamEnd;
01496                ++posParamEnd;
01497             }
01498             if (params.Length() <= posParamEnd) {
01499                // something is seriously wrong - skip :-/
01500                ret.Remove(0);
01501                name.Remove(0);
01502                params.Remove(0);
01503                return 0;
01504             }
01505             ++posParamEnd; // skip trailing '"'
01506             break;
01507          case '\'': // skip ')'
01508             ++posParamEnd;
01509             if (params[posParamEnd] == '\\') ++posParamEnd;
01510             posParamEnd += 2;
01511             break;
01512          default:
01513             ++posParamEnd;
01514       }
01515    } // while bracketlevel, i.e. (...(..)...)
01516 
01517    {
01518       TString pastParams(params(posParamEnd, params.Length()));
01519       pastParams = pastParams.Strip(TString::kLeading);
01520       isconst = pastParams.BeginsWith("const") && !(isalnum(pastParams[5]) || pastParams[5] == '_');
01521    }
01522 
01523    Ssiz_t posBlock     = params.Index('{', posParamEnd);
01524    Ssiz_t posSemicolon = params.Index(';', posParamEnd);
01525    Ssiz_t posPureVirt  = params.Index('=', posParamEnd);
01526    if (posSemicolon != kNPOS)
01527       if ((posBlock == kNPOS || (posSemicolon < posBlock)) &&
01528          (posPureVirt == kNPOS || !allowPureVirtual)
01529          && !allowPureVirtual) // allow any "func();" if pv is allowed
01530          params.Remove(0);
01531 
01532    if (params.Length())
01533       params.Remove(posParamEnd);
01534 
01535    if (!params.Length()) {
01536       ret.Remove(0);
01537       name.Remove(0);
01538       return 0;
01539    }
01540    // update posMethodName to point behind the method
01541    posMethodName = posParam + posParamEnd;
01542    if (fCurrentClass) {
01543       TMethod* meth = fCurrentClass->GetMethodAny(name);
01544       if (meth) {
01545          fDirectiveCount = 0;
01546          fCurrentMethodTag = name + "_";
01547          fCurrentMethodTag += fMethodCounts[name.Data()];
01548          return meth;
01549       }
01550    }
01551 
01552    return 0;
01553 }
01554 
01555 
01556 //______________________________________________________________________________
01557 void TDocParser::Parse(std::ostream& out)
01558 {
01559    // Locate methods, starting in the source file, then inline, then 
01560    // immediately inside the class declaration. While doing that also
01561    // find the class description and special tags like the macro tag etc.
01562 
01563    fClassDocState = kClassDoc_LookingNothingFound;
01564 
01565    DeleteDirectiveOutput();
01566 
01567    LocateMethodsInSource(out);
01568    LocateMethodsInHeaderInline(out);
01569    LocateMethodsInHeaderClassDecl(out);
01570 
01571    if (!fSourceInfo[kInfoLastUpdate].Length()) {
01572       TDatime date;
01573       fSourceInfo[kInfoLastUpdate] = date.AsString();
01574    }
01575 }
01576 
01577 //______________________________________________________________________________
01578 void TDocParser::LocateMethods(std::ostream& out, const char* filename,
01579                           Bool_t lookForSourceInfo /*= kTRUE*/, 
01580                           Bool_t useDocxxStyle /*= kFALSE*/, 
01581                           Bool_t allowPureVirtual /*= kFALSE*/,
01582                           const char* methodPattern /*= 0*/, 
01583                           const char* sourceExt /*= 0 */)
01584 {
01585    // Collect methods from the source or header file called filename.
01586    // It generates a beautified version of the source file on the fly;
01587    // the output file is given by the fCurrentClass's name, and sourceExt.
01588    // Documentation is extracted to out.
01589    //   lookForSourceInfo: if set, author, lastUpdate, and copyright are 
01590    //     extracted (i.e. the values contained in fSourceInfo)
01591    //   useDocxxStyle: if set, documentation can be in front of the method
01592    //     name, not only inside the method. Useful doc Doc++/Doxygen style,
01593    //     and inline methods.
01594    //   lookForClassDescr: if set, the first line matching the class description 
01595    //     rules is assumed to be the class description for fCurrentClass; the 
01596    //     description is written to out.
01597    //   methodPattern: if set, methods have to be prepended by this tag. Usually
01598    //     the class name + "::". In header files, looking for in-place function
01599    //     definitions, this should be 0. In that case, only functions in 
01600    //     fMethodCounts are searched for.
01601 
01602    TString sourceFileName(filename);
01603    fCurrentFile = filename;
01604    if (!sourceFileName.Length()) {
01605       fHtml->GetImplFileName(fCurrentClass, kFALSE, sourceFileName);
01606       Error("LocateMethods", "Can't find source file '%s' for class %s!", 
01607          sourceFileName.Data(), fCurrentClass->GetName());
01608       return;
01609    }
01610    ifstream sourceFile(sourceFileName.Data());
01611    if (!sourceFile || !sourceFile.good()) {
01612       Error("LocateMethods", "Can't open file '%s' for reading!", sourceFileName.Data());
01613       return;
01614    }
01615 
01616    TPMERegexp patternRE(methodPattern ? methodPattern : "");
01617 
01618    TString codeOneLiner;
01619    TString methodRet;
01620    TString methodName;
01621    TString methodParam;
01622    Bool_t methodIsConst = kFALSE;
01623    TString anchor;
01624    TString docxxComment;
01625 
01626    Bool_t wroteMethodNowWaitingForOpenBlock = kFALSE;
01627 
01628    std::ofstream srcHtmlOut;
01629    TString srcHtmlOutName;
01630    if (sourceExt && sourceExt[0]) {
01631       static_cast<TClassDocOutput*>(fDocOutput)->CreateSourceOutputStream(srcHtmlOut, sourceExt, srcHtmlOutName);
01632       fLineNumber = 0;
01633    } else {
01634       sourceExt = 0;
01635       srcHtmlOutName = fCurrentClass->GetName();
01636       fDocOutput->NameSpace2FileName(srcHtmlOutName);
01637       gSystem->PrependPathName("src", srcHtmlOutName);
01638       srcHtmlOutName += ".h.html";
01639    }
01640 
01641    fParseContext.clear();
01642    fParseContext.push_back(kCode);
01643    fDocContext = kIgnore;
01644    fLineNo = 0;
01645 
01646    while (!sourceFile.eof()) {
01647       Bool_t needAnchor = kFALSE;
01648 
01649       ++fLineNo; // we count fortrany
01650 
01651       fLineRaw.ReadLine(sourceFile, kFALSE);
01652       if (sourceFile.eof()) break;
01653 
01654       fCommentAtBOL = InContext(kComment);
01655 
01656       // replace class names etc
01657       fLineStripped = fLineRaw;
01658       Strip(fLineStripped);
01659 
01660       fLineSource = fLineRaw;
01661       fLineComment = "";
01662       DecorateKeywords(fLineSource);
01663 
01664       if (!ProcessComment()) {
01665          // not a commented line
01666 
01667          if (fDocContext == kDocClass && fClassDocState < kClassDoc_Written) {
01668             TString strippedComment(fComment);
01669             Strip(strippedComment);
01670             if (strippedComment.Length() > 0) {
01671                fLastClassDoc = fComment;
01672                if (fClassDocState == kClassDoc_LookingNothingFound) {
01673                   fFirstClassDoc = fComment;
01674                   fClassDocState = kClassDoc_LookingHaveSomething;
01675                }
01676             }
01677             fDocContext = kIgnore;
01678          }
01679 
01680          Ssiz_t impIdx = fLineStripped.Index("ClassImp(");
01681          if (impIdx == 0 && fClassDocState == kClassDoc_LookingHaveSomething) {
01682             TString name(fCurrentClass->GetName());
01683             // take unscoped version
01684             Ssiz_t posLastScope = kNPOS;
01685             while ((posLastScope = name.Index("::")) != kNPOS)
01686                name.Remove(0, posLastScope + 2);
01687 
01688             Ssiz_t posName = fLineStripped.Index(name, impIdx);
01689             if (posName != kNPOS) {
01690                Ssiz_t posClosingParen = posName + name.Length();
01691                while (isspace(fLineStripped[posClosingParen])) ++posClosingParen;
01692                if (fLineStripped[posClosingParen] == ')') {
01693                   WriteClassDoc(out, kFALSE);
01694                   fDocContext = kIgnore;
01695                }
01696             }
01697          }
01698 
01699          if (fLineStripped.Length())
01700             // remove last class doc if it not followed by ClassImp
01701             // (with optional empty lines in between)
01702             fLastClassDoc = "";
01703 
01704          // write previous method
01705          if (methodName.Length() && !wroteMethodNowWaitingForOpenBlock) {
01706             TString savedComment;
01707             if (useDocxxStyle && docxxComment.Length()) {
01708                savedComment = fComment;
01709                fComment = docxxComment;
01710             }
01711             WriteMethod(out, methodRet, methodName, methodParam, methodIsConst,
01712                gSystem->BaseName(srcHtmlOutName), anchor, codeOneLiner);
01713             docxxComment.Remove(0);
01714             if (savedComment[0]) {
01715                fComment = savedComment;
01716             }
01717          }
01718 
01719          if (!wroteMethodNowWaitingForOpenBlock) {
01720             // check for method
01721             Ssiz_t posPattern = kNPOS;
01722             if (methodPattern) {
01723                posPattern = fLineRaw.Index((TPRegexp&)patternRE);
01724             }
01725             if (posPattern != kNPOS && methodPattern) {
01726                // no strings, no blocks in front of function declarations / implementations
01727                static const char vetoChars[] = "{\"";
01728                for (int ich = 0; posPattern != kNPOS && vetoChars[ich]; ++ich) {
01729                   Ssiz_t posVeto = fLineRaw.Index(vetoChars[ich]);
01730                   if (posVeto != kNPOS && posVeto < posPattern)
01731                      posPattern = kNPOS;
01732                }
01733             }
01734             if (posPattern != kNPOS || !methodPattern) {
01735                if (methodPattern) {
01736                   patternRE.Match(fLineRaw);
01737                   posPattern += patternRE[0].Length();
01738                }
01739                LocateMethodInCurrentLine(posPattern, methodRet, methodName, 
01740                                          methodParam, methodIsConst, srcHtmlOut,
01741                                          anchor, sourceFile, allowPureVirtual);
01742                if (methodName.Length()) {
01743                   fDocContext = kDocFunc;
01744                   needAnchor = !anchor.Length();
01745                   if (useDocxxStyle)
01746                      docxxComment = fComment;
01747                   fComment.Remove(0);
01748                   codeOneLiner.Remove(0);
01749 
01750                   wroteMethodNowWaitingForOpenBlock = fLineRaw.Index("{", posPattern) == kNPOS;
01751                   wroteMethodNowWaitingForOpenBlock &= fLineRaw.Index(";", posPattern) == kNPOS;
01752                } else if (fLineRaw.First("{};") != kNPOS)
01753                   // these chars reset the preceding comment
01754                   fComment.Remove(0);
01755             } // pattern matches - could be a method
01756             else 
01757                fComment.Remove(0);
01758          } else {
01759             wroteMethodNowWaitingForOpenBlock &= fLineRaw.Index("{") == kNPOS;
01760             wroteMethodNowWaitingForOpenBlock &= fLineRaw.Index(";") == kNPOS;
01761          } // if !wroteMethodNowWaitingForOpenBlock
01762 
01763          if (methodName.Length() && !wroteMethodNowWaitingForOpenBlock) {
01764             // make sure we don't have more '{' in commentLine than in fLineRaw
01765             if (!codeOneLiner.Length() &&
01766                 fLineSource.CountChar('{') == 1 && 
01767                 fLineSource.CountChar('}') == 1) {
01768                // a one-liner
01769                codeOneLiner = fLineSource;
01770                codeOneLiner.Remove(0, codeOneLiner.Index('{'));
01771                codeOneLiner.Remove(codeOneLiner.Index('}') + 1);
01772             }
01773          } // if method name and '{'
01774          // else not a comment, and we don't need the previous one:
01775          else if (!methodName.Length() && !useDocxxStyle)
01776             fComment.Remove(0);
01777 
01778          if (needAnchor || fExtraLinesWithAnchor.find(fLineNo) != fExtraLinesWithAnchor.end()) {
01779             AnchorFromLine(fLineStripped, anchor);
01780             if (sourceExt)
01781                srcHtmlOut << "<a name=\"" << anchor << "\"></a>";
01782          }
01783          // else anchor.Remove(0); - NO! WriteMethod will need it later!
01784       } // if !comment
01785 
01786       // check for last update,...
01787       Ssiz_t posTag = kNPOS;
01788       if (lookForSourceInfo)
01789          for (Int_t si = 0; si < (Int_t) kNumSourceInfos; ++si)
01790             if (!fSourceInfo[si].Length() && (posTag = fLineRaw.Index(fSourceInfoTags[si])) != kNPOS) {
01791                fSourceInfo[si] = fLineRaw(posTag + strlen(fSourceInfoTags[si]), fLineRaw.Length() - posTag);
01792                if (si == kInfoAuthor)
01793                   fDocOutput->FixupAuthorSourceInfo(fSourceInfo[kInfoAuthor]);
01794             }
01795 
01796 
01797       // write to .cxx.html
01798       ++fLineNumber;
01799       if (srcHtmlOut)
01800          WriteSourceLine(srcHtmlOut);
01801       else if (needAnchor)
01802          fExtraLinesWithAnchor.insert(fLineNo);
01803    } // while !sourceFile.eof()
01804 
01805    // deal with last func
01806    if (methodName.Length()) {
01807       if (useDocxxStyle && docxxComment.Length())
01808          fComment = docxxComment;
01809       WriteMethod(out, methodRet, methodName, methodParam, methodIsConst,
01810          gSystem->BaseName(srcHtmlOutName), anchor, codeOneLiner);
01811       docxxComment.Remove(0);
01812    } else
01813       WriteClassDoc(out);
01814 
01815    srcHtmlOut << "</pre>" << std::endl;
01816 
01817    fDocOutput->WriteLineNumbers(srcHtmlOut, fLineNumber, gSystem->BaseName(fCurrentFile));
01818 
01819    srcHtmlOut << "</div>" << std::endl;
01820 
01821    fDocOutput->WriteHtmlFooter(srcHtmlOut, "../");
01822 
01823    fParseContext.clear();
01824    fParseContext.push_back(kCode);
01825    fDocContext = kIgnore;
01826    fCurrentFile = "";
01827 }
01828 
01829 //______________________________________________________________________________
01830 void TDocParser::LocateMethodsInSource(std::ostream& out)
01831 {
01832    // Given fCurrentClass, look for methods in its source file, 
01833    // and extract documentation to out, while beautifying the source 
01834    // file in parallel.
01835 
01836    // for Doc++ style
01837    Bool_t useDocxxStyle = (fHtml->GetDocStyle() == "Doc++");
01838 
01839    TString pattern(fCurrentClass->GetName());
01840    // take unscoped version
01841    Ssiz_t posLastScope = kNPOS;
01842    while ((posLastScope = pattern.Index("::")) != kNPOS)
01843       pattern.Remove(0, posLastScope + 2);
01844    pattern += "::";
01845    
01846    TString implFileName;
01847    if (fHtml->GetImplFileName(fCurrentClass, kTRUE, implFileName)) {
01848       LocateMethods(out, implFileName, kFALSE /*source info*/, useDocxxStyle, 
01849                     kFALSE /*allowPureVirtual*/, pattern, ".cxx.html");
01850       Ssiz_t posGt = pattern.Index('>');
01851       if (posGt != kNPOS) {
01852          // template! Re-run with pattern '...<.*>::'
01853          Ssiz_t posLt = pattern.Index('<');
01854          if (posLt != kNPOS && posLt < posGt) {
01855             pattern.Replace(posLt + 1, posGt - posLt - 1, ".*");
01856             LocateMethods(out, implFileName, kFALSE /*source info*/, useDocxxStyle, 
01857                     kFALSE /*allowPureVirtual*/, pattern, ".cxx.html");
01858          }
01859       }
01860    }
01861 }
01862 
01863 //______________________________________________________________________________
01864 void TDocParser::LocateMethodsInHeaderInline(std::ostream& out)
01865 {
01866    // Given fCurrentClass, look for methods in its header file, 
01867    // and extract documentation to out.
01868 
01869    // for inline methods, always allow doc before func
01870    Bool_t useDocxxStyle = kTRUE; 
01871 
01872    TString pattern(fCurrentClass->GetName());
01873    // take unscoped version
01874    Ssiz_t posLastScope = kNPOS;
01875    while ((posLastScope = pattern.Index("::")) != kNPOS)
01876       pattern.Remove(0, posLastScope + 1);
01877    pattern += "::";
01878    
01879    TString declFileName;
01880    if (fHtml->GetDeclFileName(fCurrentClass, kTRUE, declFileName)) {
01881       LocateMethods(out, declFileName, kTRUE /*source info*/, useDocxxStyle, 
01882                     kFALSE /*allowPureVirtual*/, pattern, 0);
01883       Ssiz_t posGt = pattern.Index('>');
01884       if (posGt != kNPOS) {
01885          // template! Re-run with pattern '...<.*>::'
01886          Ssiz_t posLt = pattern.Index('<');
01887          if (posLt != kNPOS && posLt < posGt) {
01888             pattern.Replace(posLt + 1, posGt - posLt - 1, ".*");
01889             LocateMethods(out, declFileName, kTRUE /*source info*/, useDocxxStyle, 
01890                     kFALSE /*allowPureVirtual*/, pattern, 0);
01891          }
01892       }
01893    }
01894 }
01895 
01896 //______________________________________________________________________________
01897 void TDocParser::LocateMethodsInHeaderClassDecl(std::ostream& out)
01898 {
01899    // Given fCurrentClass, look for methods in its header file's
01900    // class declaration block, and extract documentation to out,
01901    // while beautifying the header file in parallel.
01902 
01903    TString declFileName;
01904    if (fHtml->GetDeclFileName(fCurrentClass, kTRUE, declFileName))
01905       LocateMethods(out, declFileName, kTRUE/*source info*/, kTRUE /*useDocxxStyle*/,
01906                     kTRUE /*allowPureVirtual*/, 0, ".h.html");
01907 }
01908 
01909 //______________________________________________________________________________
01910 Bool_t TDocParser::ProcessComment()
01911 {
01912    // Parse the current line as a comment, handling directives and re-formatting
01913    // the comment: remove "/*", "*/", "//", similar characters surrounding lines,
01914    // etc.
01915    //
01916    // Return kFALSE if the line is not a comment.
01917 
01918    if (!fCommentAtBOL
01919       && !(fLineStripped[0] == '/' 
01920          && (fLineStripped[1] == '/' || fLineStripped[1] == '*'))
01921       && !InContext(kComment) && !InContext(kDirective)) {
01922       fLineComment = "";
01923       return kFALSE;
01924    }
01925 
01926    //if (InContext(kDirective))
01927    //   return kTRUE; - NO! we might have a comment from a previous directive!
01928 
01929    // don't write out empty lines if the current directive is eating the line
01930    if (InContext(kDirective) && !fLineComment.Length())
01931       return kTRUE;
01932 
01933    TString commentLine(fLineComment.Strip());
01934 
01935    // remove all <span class="comment"> tags
01936    Bool_t mustDealWithCommentAtBOL = fCommentAtBOL; // whether we had a closing "*/"
01937    Ssiz_t posComment = kNPOS;
01938    if (!fCommentAtBOL) 
01939       posComment = commentLine.Index("<span class=\"comment\">", 0, TString::kIgnoreCase);
01940    Ssiz_t posSpanEnd = commentLine.Index("</span>", posComment == kNPOS?0:posComment, TString::kIgnoreCase);
01941    while ((mustDealWithCommentAtBOL && posSpanEnd != kNPOS) || posComment != kNPOS) {
01942       Int_t spanLevel = 1;
01943       Ssiz_t posSpan = commentLine.Index("<span", posComment + 1, TString::kIgnoreCase);
01944       while (spanLevel > 1 || (posSpan != kNPOS && posSpan < posSpanEnd)) {
01945          // another span was opened, take the next </span>
01946          if (posSpan != kNPOS && posSpan < posSpanEnd) {
01947             ++spanLevel;
01948             posSpan = commentLine.Index("<span", posSpan + 1, TString::kIgnoreCase);
01949             // posSpanEnd doesn't change
01950             continue;
01951          } // else
01952          --spanLevel;
01953          // posSpan doesn't change
01954          posSpanEnd = commentLine.Index("</span>", posSpanEnd + 1, TString::kIgnoreCase);
01955       }
01956       if (posSpanEnd != kNPOS) {
01957           // only remove span if </span> if it exists (or we end up with unbalanced spans)
01958          commentLine.Remove(posSpanEnd, 7);
01959          if (posComment != kNPOS) 
01960             commentLine.Remove(posComment, 22);
01961          else {
01962             mustDealWithCommentAtBOL = kFALSE;
01963             // now remove C comments
01964             posComment = 0;
01965          }
01966          posComment = commentLine.Index("<span class=\"comment\">", posComment, TString::kIgnoreCase);
01967       } else break;
01968    }
01969    if (posComment != kNPOS)
01970       commentLine.Remove(posComment, 22);
01971 
01972    // don't strip in C comments, do strip if opening:
01973    if (!InContext(kComment) || (InContext(kComment) & kCXXComment)
01974        || (fLineStripped[0] == '/' && fLineStripped[1] == '*'))
01975       Strip(commentLine);
01976 
01977    // look for start tag of class description
01978    if ((fClassDocState == kClassDoc_LookingNothingFound
01979       || fClassDocState == kClassDoc_LookingHaveSomething)
01980       && !fComment.Length() 
01981       && fDocContext == kIgnore && commentLine.Contains(fClassDescrTag)) {
01982       fDocContext = kDocClass;
01983    }
01984 
01985    char start_or_end = 0;
01986    // remove leading /*, //
01987    if (commentLine.Length()>1 && commentLine[0] == '/' 
01988        && (commentLine[1] == '/' || commentLine[1] == '*')) {
01989       start_or_end = commentLine[1];
01990       commentLine.Remove(0, 2);
01991    }
01992    // remove trailing */
01993    if (start_or_end != '/' && commentLine.Length()>1 
01994        && commentLine[commentLine.Length() - 2] == '*' 
01995        && commentLine[commentLine.Length() - 1] == '/') {
01996       start_or_end = commentLine[commentLine.Length() - 2];
01997       commentLine.Remove(commentLine.Length()-2);
01998    }
01999 
02000    // remove repeating characters from the end of the line
02001    if (start_or_end && commentLine.Length() > 3) {
02002       TString lineAllOneChar(commentLine.Strip());
02003 
02004       Ssiz_t len = lineAllOneChar.Length();
02005       if (len > 2) {
02006          Char_t c = lineAllOneChar[len - 1];
02007          if (c == lineAllOneChar[len - 2] && c == lineAllOneChar[len - 3]) {
02008             TString lineAllOneCharStripped = lineAllOneChar.Strip(TString::kTrailing, c);
02009             Strip(lineAllOneCharStripped);
02010             if (!lineAllOneCharStripped.Length()) {
02011                commentLine.Remove(0);
02012 
02013                // also a class doc signature: line consists of ////
02014                if ((fClassDocState == kClassDoc_LookingNothingFound
02015                   || fClassDocState == kClassDoc_LookingHaveSomething)
02016                   && !fComment.Length() 
02017                    && fDocContext == kIgnore && start_or_end=='/') {
02018                   fDocContext = kDocClass;
02019                }
02020             }
02021          }
02022       }
02023    }
02024 
02025    // remove leading and trailing chars from e.g. // some doc //
02026    if (commentLine.Length() > 0 && start_or_end == commentLine[commentLine.Length() - 1])
02027       // we already removed it as part of // or / *; also remove the trailing
02028       commentLine = commentLine.Strip(TString::kTrailing, start_or_end);
02029 
02030    if (commentLine.Length() > 2 && Context() != kDirective)
02031       while (commentLine.Length() > 2
02032              && !IsWord(commentLine[0])
02033              && commentLine[0] == commentLine[commentLine.Length() - 1])
02034          commentLine = commentLine.Strip(TString::kBoth, commentLine[0]);
02035    
02036    // remove leading '/' if we had // or '*' if we had / *
02037    while (start_or_end && commentLine[0] == start_or_end)
02038       commentLine.Remove(0, 1);
02039 
02040    fComment += commentLine + "\n";
02041 
02042    return kTRUE;
02043 }
02044 
02045 //______________________________________________________________________________
02046 void TDocParser::RemoveCommentContext(Bool_t cxxcomment)
02047 {
02048    // remove the top-most comment context that matches cxxcomment,
02049 
02050    UInt_t lookFor = kComment;
02051    if (cxxcomment) lookFor |= kCXXComment;
02052    std::list<UInt_t>::iterator iComment = fParseContext.end();
02053    for (std::list<UInt_t>::iterator iContext = fParseContext.begin();
02054       iContext != fParseContext.end(); ++ iContext)
02055       if (*iContext == lookFor) iComment =iContext;
02056    if (iComment != fParseContext.end())
02057       fParseContext.erase(iComment);
02058 }
02059 
02060 //______________________________________________________________________________
02061 Bool_t TDocParser::Strip(TString& str)
02062 {
02063    // strips ' ', tabs, and newlines from both sides of str
02064    Bool_t changed = str[0] == ' ' || str[0] == '\t' || str[0] == '\n';
02065    changed |= str.Length()
02066       && (str[str.Length() - 1] == ' ' || str[str.Length() - 1] == '\t' 
02067          || str[str.Length() - 1] == '\n');
02068    if (!changed) return kFALSE;
02069    Ssiz_t i = 0;
02070    while (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
02071       ++i;
02072    str.Remove(0,i);
02073    i = str.Length() - 1;
02074    while (i >= 0 && (str[i] == ' ' || str[i] == '\t' || str[i] == '\n'))
02075       --i;
02076    str.Remove(i + 1, str.Length());
02077    return kTRUE;
02078 }
02079 
02080 //______________________________________________________________________________
02081 void TDocParser::WriteClassDoc(std::ostream& out, Bool_t first /*= kTRUE*/)
02082 {
02083    // Write the class description depending (among others) on fClassDocState.
02084 
02085    if (fClassDocState == kClassDoc_LookingHaveSomething || fClassDocState == kClassDoc_LookingNothingFound) {
02086       TString& classDoc = first || !fLastClassDoc.Length() ? fFirstClassDoc : fLastClassDoc;
02087       static_cast<TClassDocOutput*>(fDocOutput)->WriteClassDescription(out, classDoc);
02088       fClassDocState = kClassDoc_Written;
02089    }
02090 
02091 }
02092 
02093 namespace {
02094    static void RemoveUnneededSpaces(TString& s) {
02095       // Remove spaces except between identifier characters.
02096       // Assumes s is stripped (does not start nor end with space).
02097       for (Ssiz_t i = 1; i < s.Length() - 1; ++i) {
02098          if (s[i] == ' ') {
02099             char p = s[i - 1];
02100             char n = s[i + 1];
02101             if (((isalnum(p) || p == '_') && (isalnum(n) || n == '_'))
02102                 || (p == '>' && n == '>')) {
02103                // "id id" or "> >": keep space
02104             } else {
02105                while (isspace(s[i])) {
02106                   s.Remove(i, 1);
02107                }
02108             }
02109          }
02110       }
02111    }
02112 
02113    static void ParseParameters(TString& strippedParams, TList& paramArr) {
02114       // Extract a list of strings (the parameters without initializers) from
02115       // the signature.
02116       int nest = 0;
02117       bool init = false;
02118       bool quoted = false;
02119       Ssiz_t len = strippedParams.Length();
02120       TString arg;
02121       for (Ssiz_t i = 0; i < len; ++i) {
02122          switch (strippedParams[i]) {
02123          case '<': // fallthrough
02124          case '(': // fallthrough
02125          case '[': ++nest; break;
02126          case '>': // fallthrough
02127          case ')': // fallthrough
02128          case ']': --nest; break;
02129          case '=': init = true; break;
02130          case '\'': ++i; if (strippedParams[i] == '\\') ++i; ++i; continue;
02131          case '\\': ++i; continue; break;
02132          case '"': quoted = !quoted; break;
02133          case ',': {
02134             if (!quoted && !nest) {
02135                TString strippedArg(arg.Strip(TString::kBoth));
02136                paramArr.AddLast(new TObjString(strippedArg));
02137                init = false;
02138                arg.Remove(0);
02139                continue;
02140             }
02141          }
02142          }
02143          if (!init) {
02144             arg += strippedParams[i];
02145          }
02146       }
02147       TString strippedLastArg(arg.Strip(TString::kBoth));
02148       if (strippedLastArg.Length()) {
02149          paramArr.AddLast(new TObjString(strippedLastArg));
02150       }
02151    }
02152 
02153    void MatchOverloadSignatures(TCollection* candidates, TList* paramArr)
02154    {
02155       // Check type identity of candidate signatures. For each argument, check whether it
02156       // reduces the list of candidates to > 0 elements.
02157       TList suppressed;
02158       TIter iCandidate(candidates);
02159       int nparams = paramArr->GetSize();
02160       for (int iparam = 0; iparam < nparams && candidates->GetSize() > 1; ++iparam) {
02161          TString& srcArg = ((TObjString*)paramArr->At(iparam))->String();
02162          TString noParName(srcArg);
02163          while (noParName.Length()
02164                 && (isalnum(noParName[noParName.Length() - 1]) || noParName[noParName.Length() - 1] == '_'))
02165             noParName.Remove(noParName.Length() - 1);
02166          noParName = noParName.Strip(TString::kTrailing);
02167 
02168          if (noParName.Length()) {
02169             RemoveUnneededSpaces(noParName);
02170          }
02171          RemoveUnneededSpaces(srcArg);
02172          // comparison:
02173          // 0: strcmp
02174          // 1: source's parameter has last identifier (parameter name?) removed
02175          // 2: candidate type name contained in source parameter
02176          for (int comparison = 0; comparison < 5; ++comparison) {
02177             if (comparison == 1 && noParName == srcArg)
02178                // there is no parameter name to ignore
02179                continue;
02180             suppressed.Clear();
02181             iCandidate.Reset();
02182             TDocMethodWrapper* method = 0;
02183             while ((method = (TDocMethodWrapper*) iCandidate())) {
02184                TMethodArg* methArg = (TMethodArg*) method->GetMethod()->GetListOfMethodArgs()->At(iparam);
02185                TString sMethArg = methArg->GetFullTypeName();
02186                RemoveUnneededSpaces(sMethArg);
02187                bool matches = false;
02188                switch (comparison) {
02189                case 0: matches = (srcArg == sMethArg); break;
02190                case 1: matches = (noParName == sMethArg); break;
02191                case 2: matches = srcArg.Contains(sMethArg) || sMethArg.Contains(srcArg); break;
02192                }
02193                if (!matches) {
02194                   suppressed.Add(method);
02195                }
02196             }
02197             if (suppressed.GetSize()
02198                 && suppressed.GetSize() < candidates->GetSize()) {
02199                candidates->RemoveAll(&suppressed);
02200                break;
02201             }
02202             if (!suppressed.GetSize()) {
02203                // we have a match, no point in trying a looser matching
02204                break;
02205             }
02206          }
02207       }
02208       if (candidates->GetSize() > 1) {
02209          // use TDocMethodWrapper::kDocumented bit
02210          suppressed.Clear();
02211          iCandidate.Reset();
02212          TDocMethodWrapper* method = 0;
02213          while ((method = (TDocMethodWrapper*) iCandidate())) {
02214             if (method->TestBit(TDocMethodWrapper::kDocumented)) {
02215                suppressed.AddLast(method);
02216             }
02217          }
02218          if (suppressed.GetSize()
02219              && suppressed.GetSize() < candidates->GetSize()) {
02220             candidates->RemoveAll(&suppressed);
02221          }
02222       }
02223    }
02224 }
02225 
02226 //______________________________________________________________________________
02227 void TDocParser::WriteMethod(std::ostream& out, TString& ret, 
02228                              TString& name, TString& params, Bool_t isconst,
02229                              const char* filename, TString& anchor, 
02230                              TString& codeOneLiner)
02231 {
02232    // Write a method, forwarding to TClassDocOutput
02233 
02234    // if we haven't found the class description until now it's too late.
02235    if (fClassDocState < kClassDoc_Written)
02236       WriteClassDoc(out);
02237 
02238    TString strippedParams(params);
02239    if (strippedParams[0] == '(') {
02240       strippedParams.Remove(0, 1);
02241       strippedParams.Remove(strippedParams.Length() - 1);
02242       strippedParams = strippedParams.Strip(TString::kBoth);
02243    }
02244 
02245    TList paramArr;
02246    paramArr.SetOwner();
02247    ParseParameters(strippedParams, paramArr);
02248    int nparams = paramArr.GetSize();
02249 
02250    // Collect overload candidates
02251    TList candidates;
02252    for (int access = 0; access < 3; ++access) {
02253       TList* methList = fMethods[access].GetListForObject(name);
02254       if (!methList) continue;
02255 
02256       TIter nextMethod(methList);
02257       TDocMethodWrapper* method = 0;
02258       while ((method = (TDocMethodWrapper *) nextMethod())) {
02259          if (name == method->GetName()
02260              && isconst == ((method->GetMethod()->Property() & kIsMethConst) > 0)
02261              && method->GetMethod()->GetListOfMethodArgs()->GetSize() == nparams) {
02262             candidates.Add(method);
02263          }
02264       }
02265    }
02266 
02267    if (nparams && candidates.GetSize() > 1) {
02268       MatchOverloadSignatures(&candidates, &paramArr);
02269    }
02270 
02271    TDocMethodWrapper* guessedMethod = 0;
02272    if (candidates.GetSize() == 1) {
02273       guessedMethod = (TDocMethodWrapper*) candidates.First();
02274       guessedMethod->SetBit(TDocMethodWrapper::kDocumented);
02275    }
02276 
02277    static_cast<TClassDocOutput*>(fDocOutput)->WriteMethod(out, ret, name, params, filename, anchor,
02278                                                            fComment, codeOneLiner, guessedMethod);
02279 
02280    DecrementMethodCount(name);
02281    ret.Remove(0);
02282    name.Remove(0);
02283    params.Remove(0);
02284    anchor.Remove(0);
02285    fComment.Remove(0);
02286 
02287    fDocContext = kIgnore;
02288 }
02289 
02290 //______________________________________________________________________________
02291 void TDocParser::WriteSourceLine(std::ostream& out)
02292 {
02293    // Write fLineSource to out.
02294    // Adjust relative paths first.
02295 
02296    fDocOutput->AdjustSourcePath(fLineSource);
02297    out << fLineSource << std::endl;
02298 
02299 }

Generated on Tue Jul 5 14:24:55 2011 for ROOT_528-00b_version by  doxygen 1.5.1