ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/MitAna/Utils/src/TDocParser.cxx
Revision: 1.1
Committed: Tue Aug 11 23:09:28 2009 UTC (15 years, 8 months ago) by loizides
Content type: text/plain
Branch: MAIN
CVS Tags: Mit_032, Mit_031, Mit_025c_branch2, Mit_025c_branch1, Mit_030, Mit_029c, Mit_029b, Mit_030_pre1, Mit_029a, Mit_029, Mit_029_pre1, Mit_028a, Mit_025c_branch0, Mit_028, Mit_027a, Mit_027, Mit_026, Mit_025e, Mit_025d, Mit_025c, Mit_025b, Mit_025a, Mit_025, Mit_025pre2, Mit_024b, Mit_025pre1, Mit_024a, Mit_024, Mit_023, Mit_022a, Mit_022, Mit_020d, TMit_020d, Mit_020c, Mit_021, Mit_021pre2, Mit_021pre1, Mit_020b, Mit_020a, Mit_020, Mit_020pre1, Mit_018, Mit_017, Mit_017pre3, Mit_017pre2, Mit_017pre1, Mit_016, Mit_015b, Mit_015a, Mit_015, Mit_014e, Mit_014d, Mit_014c, Mit_014b, Mit_014a, Mit_014, Mit_014pre3, Mit_014pre2, Mit_014pre1, Mit_013d, Mit_013c, Mit_013b, Mit_013a, Mit_013, Mit_013pre1, Mit_012i, Mit_012h, Mit_012g, Mit_012f, Mit_012e, Mit_012d, Mit_012c, Mit_012b, Mit_012a, Mit_012, Mit_011a, Mit_011, Mit_010a, HEAD
Branch point for: Mit_025c_branch
Log Message:
Added THtml from ROOT

File Contents

# User Rev Content
1 loizides 1.1 // @(#)root/html:$Id: TDocParser.cxx 26869 2008-12-12 11:41:14Z axel $
2     // Author: Axel Naumann 2007-01-09
3    
4     /*************************************************************************
5     * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers. *
6     * All rights reserved. *
7     * *
8     * For the licensing terms see $ROOTSYS/LICENSE. *
9     * For the list of contributors see $ROOTSYS/README/CREDITS. *
10     *************************************************************************/
11    
12     #include "MitAna/Utils/interface/TDocParser.h"
13    
14     #include "Riostream.h"
15     #include "TBaseClass.h"
16     #include "TClass.h"
17     #include "MitAna/Utils/interface/TClassDocOutput.h"
18     #include "TDataMember.h"
19     #include "TDataType.h"
20     #include "TDatime.h"
21     #include "MitAna/Utils/interface/TDocDirective.h"
22     #include "TEnv.h"
23     #include "TGlobal.h"
24     #include "THtml.h"
25     #include "TMethod.h"
26     #include "TROOT.h"
27     #include "TSystem.h"
28     #include "TVirtualMutex.h"
29     #include <string>
30    
31     namespace {
32    
33     class TMethodWrapperImpl: public TDocParser::TMethodWrapper {
34     public:
35     TMethodWrapperImpl(const TMethod* m): fMeth(m) {}
36    
37     static void SetClass(const TClass* cl) { fgClass = cl; }
38    
39     const char* GetName() const { return fMeth->GetName(); }
40     Int_t GetNargs() const { return fMeth->GetNargs(); }
41     virtual const TMethod* GetMethod() const { return fMeth; }
42     Bool_t IsSortable() const { return kTRUE; }
43    
44     Int_t Compare(const TObject *obj) const {
45     const TMethodWrapperImpl* m = dynamic_cast<const TMethodWrapperImpl*>(obj);
46     if (!m) return 1;
47    
48     Int_t ret = strcasecmp(GetName(), m->GetName());
49     if (ret == 0) {
50     if (GetNargs() < m->GetNargs()) return -1;
51     else if (GetNargs() > m->GetNargs()) return 1;
52     if (GetMethod()->GetClass()->InheritsFrom(m->GetMethod()->GetClass()))
53     return -1;
54     else
55     return 1;
56     }
57    
58     const char* l(GetName());
59     const char* r(m->GetName());
60     if (l[0] == '~' && r[0] == '~') {
61     ++l;
62     ++r;
63     }
64     if (fgClass->InheritsFrom(l)) {
65     if (fgClass->InheritsFrom(r)) {
66     if (gROOT->GetClass(l)->InheritsFrom(r))
67     return -1;
68     else return 1;
69     } else return -1;
70     } else if (fgClass->InheritsFrom(r))
71     return 1;
72    
73     if (l[0] == '~') return -1;
74     if (r[0] == '~') return 1;
75     return (ret < 0) ? -1 : 1;
76     }
77    
78     private:
79     static const TClass* fgClass; // current class, defining inheritance sort order
80     const TMethod* fMeth; // my method
81     };
82    
83     const TClass* TMethodWrapperImpl::fgClass = 0;
84     }
85    
86    
87     //______________________________________________________________________________
88     ////////////////////////////////////////////////////////////////////////////////
89     //
90     // Parse C++ source or header, and extract documentation.
91     //
92     // Also handles special macros like
93     /* Begin_Macro(GUI, source)
94     {
95     TGMainFrame* f = new TGMainFrame(0, 100, 100);
96     f->SetName("testMainFrame"); // that's part of the name of the image
97     TGButton* b = new TGTextButton(f, "Test Button");
98     f->AddFrame(b);
99     f->MapSubwindows();
100     f->Resize(f->GetDefaultSize());
101    
102     f->MapWindow();
103     return f; // *HIDE*
104     }
105     End_Macro */
106     // or multiline Latex aligned at =:
107     /* Begin_Latex(separator='=',align=rcl) C = d #sqrt{#frac{2}{#lambdaD}} #int^{x}_{0}cos(#frac{#pi}{2}t^{2})dt
108     D(x) = d End_Latex */
109     // even without alignment: Begin_Latex
110     // x=sin^2(y)
111     // y = #sqrt{sin(x)}
112     // End_Latex and what about running an external macro?
113     /* BEGIN_MACRO(source)
114    
115    
116     testmacro.C END_MACRO
117    
118    
119     and some nested stuff which doesn't work yet: */
120     // BEGIN_HTML
121     /* BEGIN_LATEX Wow,^{an}_{image}^{inside}_{a}^{html}_{block}
122     END_LATEX
123     */
124     // END_HTML
125     ////////////////////////////////////////////////////////////////////////////////
126    
127     ClassImp(TDocParser)
128    
129     std::set<std::string> TDocParser::fgKeywords;
130    
131     //______________________________________________________________________________
132     TDocParser::TDocParser(TClassDocOutput& docOutput, TClass* cl):
133     fHtml(docOutput.GetHtml()), fDocOutput(&docOutput), fLineNo(0),
134     fCurrentClass(cl), fRecentClass(0), fCurrentModule(0),
135     fDirectiveCount(0), fDocContext(kIgnore),
136     fCheckForMethod(kFALSE), fClassDocState(kClassDoc_Uninitialized),
137     fCommentAtBOL(kFALSE)
138     {
139     // Constructor called for parsing class sources
140    
141     InitKeywords();
142    
143     fSourceInfoTags[kInfoLastUpdate] = fHtml->GetLastUpdateTag();
144     fSourceInfoTags[kInfoAuthor] = fHtml->GetAuthorTag();
145     fSourceInfoTags[kInfoCopyright] = fHtml->GetCopyrightTag();
146    
147     fClassDescrTag = fHtml->GetClassDocTag();
148    
149     TMethodWrapperImpl::SetClass(cl);
150    
151     AddClassMethodsRecursively(0);
152     AddClassDataMembersRecursively(0);
153    
154     // needed for list of methods,...
155     fParseContext.push_back(kCode);
156    
157     // create an array of method names
158     TMethod *method;
159     TIter nextMethod(fCurrentClass->GetListOfMethods());
160     fMethodCounts.clear();
161     while ((method = (TMethod *) nextMethod())) {
162     ++fMethodCounts[method->GetName()];
163     }
164    
165     }
166    
167     //______________________________________________________________________________
168     TDocParser::TDocParser(TDocOutput& docOutput):
169     fHtml(docOutput.GetHtml()), fDocOutput(&docOutput), fLineNo(0),
170     fCurrentClass(0), fRecentClass(0), fDirectiveCount(0), fDocContext(kIgnore),
171     fCheckForMethod(kFALSE), fClassDocState(kClassDoc_Uninitialized),
172     fCommentAtBOL(kFALSE)
173     {
174     // constructor called for parsing text files with Convert()
175     InitKeywords();
176    
177     fSourceInfoTags[kInfoLastUpdate] = fHtml->GetLastUpdateTag();
178     fSourceInfoTags[kInfoAuthor] = fHtml->GetAuthorTag();
179     fSourceInfoTags[kInfoCopyright] = fHtml->GetCopyrightTag();
180    
181     fClassDescrTag = fHtml->GetClassDocTag();
182    
183     TMethodWrapperImpl::SetClass(0);
184     }
185    
186     //______________________________________________________________________________
187     TDocParser::~TDocParser()
188     {
189     // destructor, checking whether all methods have been found for gDebug > 3
190     if (gDebug > 3) {
191     for (std::map<std::string, Int_t>::const_iterator iMethod = fMethodCounts.begin();
192     iMethod != fMethodCounts.end(); ++iMethod)
193     if (iMethod->second)
194     Info("~TDocParser", "Implementation of method %s::%s could not be found.",
195     fCurrentClass ? fCurrentClass->GetName() : "",
196     iMethod->first.c_str());
197     TIter iDirective(&fDirectiveHandlers);
198     TDocDirective* directive = 0;
199     while ((directive = (TDocDirective*) iDirective())) {
200     TString directiveName;
201     directive->GetName(directiveName);
202     Warning("~TDocParser", "Missing \"%s\" for macro %s", directive->GetEndTag(), directiveName.Data());
203     }
204     }
205     }
206    
207     //______________________________________________________________________________
208     void TDocParser::AddClassMethodsRecursively(TBaseClass* bc)
209     {
210     // Add accessible (i.e. non-private) methods of base class bc
211     // and its base classes' methods to methodNames.
212     // If bc==0, we add fCurrentClass's methods (and also private functions).
213    
214     // make a loop on member functions
215     TClass *cl = fCurrentClass;
216     if (bc)
217     cl = bc->GetClassPointer(kFALSE);
218     if (!cl) return;
219    
220     TMethod *method;
221     TIter nextMethod(cl->GetListOfMethods());
222    
223     while ((method = (TMethod *) nextMethod())) {
224    
225     if (!strcmp(method->GetName(), "Dictionary") ||
226     !strcmp(method->GetName(), "Class_Version") ||
227     !strcmp(method->GetName(), "Class_Name") ||
228     !strcmp(method->GetName(), "DeclFileName") ||
229     !strcmp(method->GetName(), "DeclFileLine") ||
230     !strcmp(method->GetName(), "ImplFileName") ||
231     !strcmp(method->GetName(), "ImplFileLine") ||
232     (bc && (method->GetName()[0] == '~' // d'tor
233     || !strcmp(method->GetName(), method->GetReturnTypeName()))) // c'tor
234     )
235     continue;
236    
237    
238     Int_t mtype = 0;
239     if (kIsPrivate & method->Property())
240     mtype = 0;
241     else if (kIsProtected & method->Property())
242     mtype = 1;
243     else if (kIsPublic & method->Property())
244     mtype = 2;
245    
246     if (bc) {
247     if (mtype == 0) continue;
248     if (bc->Property() & kIsPrivate)
249     mtype = 0;
250     else if ((bc->Property() & kIsProtected) && mtype == 2)
251     mtype = 1;
252     }
253    
254     Bool_t hidden = kFALSE;
255     for (Int_t access = 0; !hidden && access < 3; ++access) {
256     TMethodWrapperImpl* other = (TMethodWrapperImpl*) fMethods[access].FindObject(method->GetName());
257     hidden |= (other) && (other->GetMethod()->GetClass() != method->GetClass());
258     }
259     if (!hidden)
260     fMethods[mtype].Add(new TMethodWrapperImpl(method));
261     }
262    
263     TIter iBase(cl->GetListOfBases());
264     TBaseClass* base = 0;
265     while ((base = (TBaseClass*)iBase()))
266     AddClassMethodsRecursively(base);
267    
268     if (!bc)
269     for (Int_t access = 0; access < 3; ++access) {
270     fMethods[access].SetOwner();
271     fMethods[access].Sort();
272     }
273     }
274    
275     //______________________________________________________________________________
276     void TDocParser::AddClassDataMembersRecursively(TBaseClass* bc) {
277     // Add data members of fCurrentClass and of bc to datamembers, recursively.
278     // Real data members are in idx 0..2 (public, protected, private access),
279     // enum constants in idx 3..5.
280    
281     // make a loop on member functions
282     TClass *cl = fCurrentClass;
283     if (bc)
284     cl = bc->GetClassPointer(kFALSE);
285     if (!cl) return;
286    
287     TDataMember *dm;
288     TIter nextDM(cl->GetListOfDataMembers());
289    
290     while ((dm = (TDataMember *) nextDM())) {
291     if (!strcmp(dm->GetName(), "fgIsA"))
292     continue;
293     Int_t mtype = 0;
294     if (kIsPrivate & dm->Property())
295     mtype = 0;
296     else if (kIsProtected & dm->Property())
297     mtype = 1;
298     else if (kIsPublic & dm->Property())
299     mtype = 2;
300    
301     if (bc) {
302     if (mtype == 0) continue;
303     if (bc->Property() & kIsPrivate)
304     mtype = 0;
305     else if ((bc->Property() & kIsProtected) && mtype == 2)
306     mtype = 1;
307     }
308    
309     const Int_t flagEnumConst = G__BIT_ISENUM | G__BIT_ISCONSTANT | G__BIT_ISSTATIC;
310     if ((dm->Property() & flagEnumConst) == flagEnumConst
311     && dm->GetDataType() && dm->GetDataType()->GetType() == kInt_t)
312     mtype += 3;
313    
314     fDataMembers[mtype].Add(dm);
315     }
316    
317     TIter iBase(cl->GetListOfBases());
318     TBaseClass* base = 0;
319     while ((base = (TBaseClass*)iBase()))
320     AddClassDataMembersRecursively(base);
321    
322     if (!bc)
323     for (Int_t access = 0; access < 6; ++access) {
324     fDataMembers[access].SetOwner(kFALSE);
325     if (access < 3) // don't sort enums; we keep them in enum tag order
326     fDataMembers[access].Sort();
327     }
328     }
329    
330    
331     //______________________________________________________________________________
332     void TDocParser::AnchorFromLine(const TString& line, TString& anchor) {
333     // Create an anchor from the given line, by hashing it and
334     // convertig the hash into a custom base64 string.
335    
336     const char base64String[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.";
337    
338     // use hash of line instead of e.g. line number.
339     // advantages: more stable (lines can move around, we still find them back),
340     // no need for keeping a line number context
341     UInt_t hash = ::Hash(line);
342     anchor.Remove(0);
343     // force first letter to be [A-Za-z], to be id compatible
344     anchor += base64String[hash % 52];
345     hash /= 52;
346     while (hash) {
347     anchor += base64String[hash % 64];
348     hash /= 64;
349     }
350     }
351    
352     //______________________________________________________________________________
353     void TDocParser::Convert(std::ostream& out, std::istream& in, const char* relpath,
354     Bool_t isCode)
355     {
356     // Parse text file "in", add links etc, and write output to "out".
357     // If "isCode", "in" is assumed to be C++ code.
358     fParseContext.clear();
359     if (isCode) fParseContext.push_back(kCode);
360     else fParseContext.push_back(kComment); // so we can find "BEGIN_HTML"/"END_HTML" in plain text
361    
362     while (!in.eof()) {
363     fLineRaw.ReadLine(in, kFALSE);
364     if (in.eof())
365     break;
366    
367     // remove leading spaces
368     fLineComment = "";
369     fLineSource = fLineRaw;
370     fLineStripped = fLineRaw;
371     Strip(fLineStripped);
372    
373     DecorateKeywords(fLineSource);
374     ProcessComment();
375    
376     if (fLineComment.Length() || InContext(kDirective)) {
377     GetDocOutput()->AdjustSourcePath(fLineComment, relpath);
378     out << fLineComment << endl;
379     } else {
380     GetDocOutput()->AdjustSourcePath(fLineSource, relpath);
381     out << fLineSource << endl;
382     }
383     }
384     }
385    
386     //______________________________________________________________________________
387     void TDocParser::DecorateKeywords(std::ostream& out, const char *text)
388     {
389     // Expand keywords in text, writing to out.
390     TString str(text);
391     DecorateKeywords(str);
392     out << str;
393     }
394    
395     //______________________________________________________________________________
396     void TDocParser::DecorateKeywords(TString& line)
397     {
398     // Find keywords in line and create URLs around them. Escape characters with a
399     // special meaning for HTML. Protect "Begin_Html"/"End_Html" pairs, and set the
400     // parsing context. Evaluate sequences like a::b->c.
401     // Skip regions where directives are active.
402    
403     std::list<TClass*> currentType;
404    
405     enum {
406     kNada,
407     kMember,
408     kScope,
409     kNumAccesses
410     } scoping = kNada;
411    
412     currentType.push_back(0);
413    
414     Ssiz_t i = 0;
415     while (isspace((UChar_t)line[i]))
416     ++i;
417    
418     Ssiz_t startOfLine = i;
419    
420     // changed when the end of a directive is encountered, i.e.
421     // from where fLineSource needs to be appended to fLineComment
422     Ssiz_t copiedToCommentUpTo = 0;
423    
424     if (InContext(kDirective) && fDirectiveHandlers.Last()) {
425     // we're only waiting for an "End_Whatever" and ignoring everything else
426     TDocDirective* directive = (TDocDirective*)fDirectiveHandlers.Last();
427     const char* endTag = directive->GetEndTag();
428     Ssiz_t posEndTag = i;
429     while (kNPOS != (posEndTag = line.Index(endTag, posEndTag, TString::kIgnoreCase)))
430     if (posEndTag == 0 || line[posEndTag - 1] != '"') // escaping '"'
431     break;
432     if (posEndTag != kNPOS)
433     i = posEndTag;
434     else {
435     Ssiz_t start = 0;
436     if (!InContext(kComment) || (InContext(kComment) & kCXXComment)) {
437     // means we are in a C++ comment
438     while (isspace((UChar_t)fLineRaw[start])) ++start;
439     if (fLineRaw[start] == '/' && fLineRaw[start + 1] == '/')
440     start += 2;
441     else start = 0;
442     }
443     directive->AddLine(fLineRaw(start, fLineRaw.Length()));
444     while(i < line.Length())
445     fDocOutput->ReplaceSpecialChars(line, i);
446     copiedToCommentUpTo = i;
447     }
448     }
449    
450     for (; i < line.Length(); ++i) {
451    
452     if (!currentType.back())
453     scoping = kNada;
454    
455     // evaluate scope relation
456     if (Context() == kCode
457     || Context() == kComment) {
458     if (currentType.back())
459     switch (line[i]) {
460     case ':':
461     if (line[i + 1] == ':') {
462     scoping = kScope;
463     i += 1;
464     continue;
465     }
466     break;
467     case '-':
468     if (line[i + 1] == '>') {
469     scoping = kMember;
470     i += 1;
471     continue;
472     }
473     break;
474     case '.':
475     if (line[i + 1] != '.') {
476     // prevent "..."
477     scoping = kMember;
478     continue;
479     }
480     break;
481     }
482     switch (line[i]) {
483     case '(':
484     currentType.push_back(0);
485     scoping = kNada;
486     continue;
487     break;
488     case ')':
489     if (currentType.size() > 1)
490     currentType.pop_back();
491     scoping = kMember;
492     continue;
493     break;
494     }
495     if (i >= line.Length())
496     break;
497     } else // code or comment
498     currentType.back() = 0;
499    
500    
501     if (!IsWord(line[i])){
502    
503     Bool_t haveHtmlEscapedChar = Context() == kString
504     && i > 2 && line[i] == '\'' && line[i-1] == ';';
505     if (haveHtmlEscapedChar) {
506     Ssiz_t posBegin = i - 2;
507     while (posBegin > 0 && IsWord(line[posBegin]))
508     --posBegin;
509     haveHtmlEscapedChar = posBegin > 0 &&
510     line[posBegin] == '&' && line[posBegin - 1] == '\'';
511     }
512     EParseContext context = Context();
513     Bool_t closeString = context == kString
514     && ( line[i] == '"'
515     || (line[i] == '\''
516     && ( (i > 1 && line[i - 2] == '\'')
517     || (i > 3 && line[i - 2] == '\\' && line[i - 3] == '\'')))
518     || haveHtmlEscapedChar)
519     && (i == 0 || line[i - 1] != '\\'); // but not "foo \"str...
520     if (context == kCode || context == kComment) {
521     if (line[i] == '"' || (line[i] == '\'' && (
522     // 'a'
523     (line.Length() > i + 2 && line[i + 2] == '\'') ||
524     // '\a'
525     (line.Length() > i + 3 && line[i + 1] == '\'' && line[i + 3] == '\'')))) {
526    
527     fDocOutput->DecorateEntityBegin(line, i, kString);
528     fParseContext.push_back(kString);
529     currentType.back() = 0;
530     closeString = kFALSE;
531     } else if (context == kCode
532     && line[i] == '/' && (line[i+1] == '/' || line[i+1] == '*')) {
533     fParseContext.push_back(kComment);
534     if (line[i+1] == '/')
535     fParseContext.back() |= kCXXComment;
536     currentType.back() = 0;
537     fDocOutput->DecorateEntityBegin(line, i, kComment);
538     ++i;
539     } else if (context == kComment
540     && !(fParseContext.back() & kCXXComment)
541     && line.Length() > i + 1
542     && line[i] == '*' && line[i+1] == '/') {
543     if (fParseContext.size()>1)
544     fParseContext.pop_back();
545    
546     currentType.back() = 0;
547     i += 2;
548     fDocOutput->DecorateEntityEnd(line, i, kComment);
549     if (!fCommentAtBOL) {
550     if (InContext(kDirective))
551     ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(line(copiedToCommentUpTo, i));
552     else
553     fLineComment += line(copiedToCommentUpTo, i);
554     copiedToCommentUpTo = i;
555     }
556     } else if (startOfLine == i
557     && line[i] == '#'
558     && context == kCode) {
559     ExpandCPPLine(line, i);
560     }
561     } // if context is comment or code
562    
563     if (i < line.Length())
564     fDocOutput->ReplaceSpecialChars(line, i);
565    
566     if (closeString) {
567     fDocOutput->DecorateEntityEnd(line, i, kString);
568     if (fParseContext.size()>1)
569     fParseContext.pop_back();
570    
571     currentType.back() = 0;
572     }
573     --i; // i already moved by ReplaceSpecialChar
574    
575     continue;
576     } // end of "not a word"
577    
578     // get the word
579     Ssiz_t endWord = i;
580     while (endWord < line.Length() && IsName(line[endWord]))
581     endWord++;
582    
583     if (Context() == kString || Context() == kCPP) {
584     // don't replace in strings, cpp, etc
585     i = endWord - 1;
586     continue;
587     }
588    
589     TString word(line(i, endWord - i));
590    
591     // '"' escapes handling of "Begin_..."/"End_..."
592     if ((i == 0 || (i > 0 && line[i - 1] != '"'))
593     && HandleDirective(line, i, word, copiedToCommentUpTo)) {
594     // something special happened; the currentType is gone.
595     currentType.back() = 0;
596     continue;
597     }
598    
599     // don't replace keywords in comments
600     if (Context() == kCode
601     && fgKeywords.find(word.Data()) != fgKeywords.end()) {
602     fDocOutput->DecorateEntityBegin(line, i, kKeyword);
603     i += word.Length();
604     fDocOutput->DecorateEntityEnd(line, i, kKeyword);
605     --i; // -1 for ++i
606     currentType.back() = 0;
607     continue;
608     }
609    
610     // Now decorate scopes and member, referencing their documentation:
611    
612     // generic layout:
613     // A::B::C::member[arr]->othermember
614     // we iterate through this, first scope is A, and currentType will be set toA,
615     // next we see ::B, "::" signals to use currentType,...
616    
617     TDataType* subType = 0;
618     TClass* subClass = 0;
619     TDataMember *datamem = 0;
620     TMethod *meth = 0;
621     const char* globalTypeName = 0;
622     if (!currentType.size()) {
623     Warning("DecorateKeywords", "type context is empty!");
624     currentType.push_back(0);
625     }
626     TClass* lookupScope = currentType.back();
627    
628     if (scoping == kNada) {
629     if (fCurrentClass)
630     lookupScope = fCurrentClass;
631     else
632     lookupScope = fRecentClass;
633     }
634    
635     if (scoping == kNada) {
636     subType = gROOT->GetType(word);
637     if (!subType)
638     subClass = fHtml->GetClass(word);
639     if (!subType && !subClass) {
640     TGlobal *global = gROOT->GetGlobal(word);
641     if (global) {
642     // cannot doc globals; take at least their type...
643     globalTypeName = global->GetTypeName();
644     subClass = fHtml->GetClass(globalTypeName);
645     if (!subClass)
646     subType = gROOT->GetType(globalTypeName);
647     // else // hack to prevent current THtml obj from showing up - we only want gHtml
648     // if (subClass == THtml::Class() && word != "gHtml")
649     // subClass = 0;
650     }
651     }
652     if (!subType && !subClass) {
653     // too bad - cannot doc yet...
654     //TFunction *globFunc = gROOT->GetGlobalFunctionWithPrototype(word);
655     //globFunc = 0;
656     }
657     if (!subType && !subClass) {
658     // also try template
659     while (isspace(line[endWord])) ++endWord;
660     if (line[endWord] == '<' || line[endWord] == '>') {
661     // check for possible template
662     Ssiz_t endWordT = endWord + 1;
663     int templateLevel = 1;
664     while (endWordT < line.Length()
665     && (templateLevel
666     || IsName(line[endWordT])
667     || line[endWordT] == '<'
668     || line[endWordT] == '>')) {
669     if (line[endWordT] == '<')
670     ++templateLevel;
671     else if (line[endWordT] == '>')
672     --templateLevel;
673     endWordT++;
674     }
675     subClass = fHtml->GetClass(line(i, endWordT - i).Data());
676     if (subClass)
677     word = line(i, endWordT - i);
678     }
679     }
680     }
681    
682     if (lookupScope && !subType && !subClass) {
683     if (scoping == kScope) {
684     TString subClassName(lookupScope->GetName());
685     subClassName += "::";
686     subClassName += word;
687     subClass = fHtml->GetClass(subClassName);
688     if (!subClass)
689     subType = gROOT->GetType(subClassName);
690     }
691     if (!subClass && !subType) {
692     // also try A::B::c()
693     datamem = lookupScope->GetDataMember(word);
694     if (!datamem)
695     meth = lookupScope->GetMethodAllAny(word);
696     }
697     if (!subClass && !subType && !datamem && !meth) {
698     // also try template
699     while (isspace(line[endWord])) ++endWord;
700     if (line[endWord] == '<' || line[endWord] == '>') {
701     // check for possible template
702     Ssiz_t endWordT = endWord + 1;
703     int templateLevel = 1;
704     while (endWordT < line.Length()
705     && (templateLevel
706     || IsName(line[endWordT])
707     || line[endWordT] == '<'
708     || line[endWordT] == '>')) {
709     if (line[endWordT] == '<')
710     ++templateLevel;
711     else if (line[endWordT] == '>')
712     --templateLevel;
713     endWordT++;
714     }
715     TString subClassName(lookupScope->GetName());
716     subClassName += "::";
717     subClassName += line(i, endWordT - i);
718     subClass = fHtml->GetClass(subClassName);
719     if (subClass)
720     word = line(i, endWordT - i);
721     }
722     }
723     }
724     // create the link
725     TString mangledWord(word);
726     fDocOutput->ReplaceSpecialChars(mangledWord);
727     line.Replace(i, word.Length(), mangledWord);
728    
729     TSubString substr(line(i, mangledWord.Length()));
730     if (subType) {
731     fDocOutput->ReferenceEntity(substr, subType,
732     globalTypeName ? globalTypeName : subType->GetName());
733     currentType.back() = 0;
734     } else if (subClass) {
735     fDocOutput->ReferenceEntity(substr, subClass,
736     globalTypeName ? globalTypeName : subClass->GetName());
737    
738     currentType.back() = subClass;
739     fRecentClass = subClass;
740     } else if (datamem || meth) {
741     if (datamem) {
742     fDocOutput->ReferenceEntity(substr, datamem);
743    
744     if (datamem->GetTypeName())
745     currentType.back() = fHtml->GetClass(datamem->GetTypeName());
746     } else {
747     fDocOutput->ReferenceEntity(substr, meth);
748    
749     TString retTypeName = meth->GetReturnTypeName();
750     if (retTypeName.BeginsWith("const "))
751     retTypeName.Remove(0,6);
752     Ssiz_t pos=0;
753     while (IsWord(retTypeName[pos]) || retTypeName[pos]=='<' || retTypeName[pos]=='>' || retTypeName[pos]==':')
754     ++pos;
755     retTypeName.Remove(pos, retTypeName.Length());
756     if (retTypeName.Length())
757     currentType.back() = fHtml->GetClass(retTypeName);
758     }
759     } else
760     currentType.back() = 0;
761    
762     //i += mangledWord.Length();
763     i += substr.Length();
764    
765     --i; // due to ++i
766     } // while i < line.Length()
767     if (i > line.Length())
768     i = line.Length();
769    
770     // clean up, no strings across lines
771     if (Context() == kString) {
772     fDocOutput->DecorateEntityEnd(line, i, kString);
773     if (fParseContext.size()>1)
774     fParseContext.pop_back();
775     currentType.back() = 0;
776     }
777    
778     // HandleDirective already copied the chunk before the directive
779     // from fLineSource to fLineComment. So we're done up to "i" in
780     // fLineSource; next time we encounter a directive we just need
781     // to copy from startOfComment on.
782     if ((InContext(kComment) || fCommentAtBOL) && copiedToCommentUpTo < line.Length()) {
783     if (InContext(kDirective))
784     ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(line(copiedToCommentUpTo, line.Length()));
785     else
786     fLineComment += line(copiedToCommentUpTo, line.Length());
787     }
788    
789     // Do this after we append to fLineComment, otherwise the closing
790     // </span> gets sent to the directive.
791     // clean up, no CPP comment across lines
792     if (InContext(kComment) & kCXXComment) {
793     fDocOutput->DecorateEntityEnd(line, i, kComment);
794     if (fLineComment.Length()) {
795     Ssiz_t pos = fLineComment.Length();
796     fDocOutput->DecorateEntityEnd(fLineComment, pos, kComment);
797     }
798     RemoveCommentContext(kTRUE);
799     currentType.back() = 0;
800     }
801     }
802    
803     //______________________________________________________________________________
804     void TDocParser::DecrementMethodCount(const char* name)
805     {
806     // reduce method count for method called name,
807     // removing it from fMethodCounts once the count reaches 0.
808    
809     typedef std::map<std::string /*method name*/, Int_t > MethodCount_t;
810     MethodCount_t::iterator iMethodName = fMethodCounts.find(name);
811     if (iMethodName != fMethodCounts.end()) {
812     --(iMethodName->second);
813     if (iMethodName->second <= 0)
814     fMethodCounts.erase(iMethodName);
815     }
816     }
817    
818     //______________________________________________________________________________
819     void TDocParser::DeleteDirectiveOutput() const
820     {
821     // Delete output generated by prior runs of all known directives;
822     // the output file names might have changes.
823    
824     TIter iClass(gROOT->GetListOfClasses());
825     TClass* cl = 0;
826     while ((cl = (TClass*) iClass()))
827     if (cl != TDocDirective::Class()
828     && cl->InheritsFrom(TDocDirective::Class())) {
829     TDocDirective* directive = (TDocDirective*) cl->New();
830     if (!directive) continue;
831     directive->SetParser(const_cast<TDocParser*>(this));
832     directive->DeleteOutput();
833     delete directive;
834     }
835     }
836    
837     //______________________________________________________________________________
838     void TDocParser::ExpandCPPLine(TString& line, Ssiz_t& pos)
839     {
840     // Expand preprocessor statements
841     //
842     //
843     // Input: line - line containing the CPP statement,
844     // pos - position of '#'
845     //
846     // NOTE: Looks for the #include statements and
847     // creates link to the corresponding file
848     // if such file exists
849     //
850    
851     Bool_t linkExist = kFALSE;
852     Ssiz_t posEndOfLine = line.Length();
853     Ssiz_t posHash = pos;
854    
855     Ssiz_t posInclude = line.Index("include", pos);
856     if (posInclude != kNPOS) {
857     TString filename;
858     Ssiz_t posStartFilename = posInclude + 7;
859     if (line.Tokenize(filename, posStartFilename, "[<\"]")) {
860     Ssiz_t posEndFilename = posStartFilename;
861     if (line.Tokenize(filename, posEndFilename, "[>\"]")) {
862     R__LOCKGUARD(fHtml->GetMakeClassMutex());
863    
864     TString filesysFileName;
865     if (fHtml->GetPathDefinition().GetFileNameFromInclude(filename, filesysFileName)) {
866     fDocOutput->CopyHtmlFile(filesysFileName);
867    
868     TString endOfLine(line(posEndFilename - 1, line.Length()));
869     line.Remove(posStartFilename, line.Length());
870     for (Ssiz_t i = pos; i < line.Length();)
871     fDocOutput->ReplaceSpecialChars(line, i);
872    
873     line += "<a href=\"../";
874     line += gSystem->BaseName(filename);
875     line += "\">";
876     line += filename + "</a>" + endOfLine[0]; // add include file's closing '>' or '"'
877     posEndOfLine = line.Length() - 1; // set the "processed up to" to it
878     fDocOutput->ReplaceSpecialChars(line, posEndOfLine); // and run replace-special-char on it
879    
880     line += endOfLine(1, endOfLine.Length()); // add the unprocessed part of the line back
881    
882     linkExist = kTRUE;
883     }
884     }
885     }
886     }
887    
888     if (!linkExist) {
889     fDocOutput->ReplaceSpecialChars(line);
890     posEndOfLine = line.Length();
891     }
892    
893     Ssiz_t posHashAfterDecoration = posHash;
894     fDocOutput->DecorateEntityBegin(line, posHashAfterDecoration, kCPP);
895     posEndOfLine += posHashAfterDecoration - posHash;
896    
897     fDocOutput->DecorateEntityEnd(line, posEndOfLine, kCPP);
898     pos = posEndOfLine;
899     }
900    
901    
902     //______________________________________________________________________________
903     void TDocParser::GetCurrentModule(TString& out_module) const {
904     // Return the name of module for which sources are currently parsed.
905     if (fCurrentModule) out_module = fCurrentModule;
906     else if (fCurrentClass) fHtml->GetModuleNameForClass(out_module, fCurrentClass);
907     else out_module = "(UNKNOWN MODULE WHILE PARSING)";
908     }
909    
910     //______________________________________________________________________________
911     Bool_t TDocParser::HandleDirective(TString& line, Ssiz_t& pos, TString& word,
912     Ssiz_t& copiedToCommentUpTo)
913     {
914     // Process directives to the documentation engine, like "Begin_Html" / "End_Html",
915     // "Begin_Macro" / "End_Macro", and "Begin_Latex" / "End_Latex".
916    
917     Bool_t begin = kTRUE;
918     TClass* clDirective = IsDirective(line, pos, word, begin);
919     if (!clDirective)
920     return kFALSE;
921    
922     // we'll need end later on: afer the begin block, both end _and_ begin can be true.
923     Bool_t end = !begin;
924    
925     TDocDirective* directive = 0; // allow re-use of object from begin block in end
926    
927     if (begin) {
928     // copy from fLineSource to fLineComment, starting at copiedToCommentUpTo
929     if (InContext(kDirective))
930     ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(fLineSource(copiedToCommentUpTo, pos - copiedToCommentUpTo));
931     else
932     fLineComment += fLineSource(copiedToCommentUpTo, pos - copiedToCommentUpTo);
933     copiedToCommentUpTo = pos;
934    
935     pos += word.Length(); // skip the keyword
936    
937     directive = (TDocDirective*) clDirective->New();
938     if (!directive)
939     return kFALSE;
940    
941     directive->SetParser(this);
942     if (fCurrentMethodTag.Length())
943     directive->SetTag(fCurrentMethodTag);
944     directive->SetCounter(fDirectiveCount++);
945    
946     // parse parameters
947     TString params;
948     if (begin && line[pos] == '(') {
949     std::list<char> waitForClosing;
950     Ssiz_t endParam = pos + 1;
951     for (; endParam < line.Length()
952     && (line[endParam] != ')' || !waitForClosing.empty()); ++endParam) {
953     const char c = line[endParam];
954     if (!waitForClosing.empty() && waitForClosing.back() == c) {
955     waitForClosing.pop_back();
956     continue;
957     }
958     switch (c) {
959     case '"':
960     if (waitForClosing.empty() || waitForClosing.back() != '\'')
961     waitForClosing.push_back('"');
962     break;
963     case '\'':
964     if (waitForClosing.empty() || waitForClosing.back() != '"')
965     waitForClosing.push_back('\'');
966     break;
967     case '(':
968     if (waitForClosing.empty() || (waitForClosing.back() != '"' && waitForClosing.back() != '\''))
969     waitForClosing.push_back(')');
970     break;
971     case '\\':
972     ++endParam; // skip next char
973     default:
974     break;
975     };
976     }
977     if (waitForClosing.empty()) {
978     params = line(pos + 1, endParam - (pos + 1));
979     pos += params.Length() + 2; // params + parentheses
980     }
981     directive->SetParameters(params);
982     }
983    
984     // check for end tag in current line
985     Ssiz_t posEndTag = pos;
986     const char* endTag = directive->GetEndTag();
987     Ssiz_t lenEndTag = strlen(endTag);
988     while (kNPOS != (posEndTag = line.Index(endTag, posEndTag, TString::kIgnoreCase))) {
989     if (line[posEndTag - 1] == '"') {
990     posEndTag += lenEndTag;
991     continue; // escaping '"'
992     }
993     break;
994     }
995     if (posEndTag != kNPOS) {
996     end = kTRUE; // we just continue below!
997     } else {
998     fDirectiveHandlers.AddLast(directive);
999    
1000     fParseContext.push_back(kDirective);
1001     if (InContext(kComment) & kCXXComment)
1002     fParseContext.back() |= kCXXComment;
1003    
1004     posEndTag = line.Length();
1005     }
1006    
1007     directive->AddLine(line(pos, posEndTag - pos));
1008     TString remainder(line(posEndTag, line.Length()));
1009     line.Remove(posEndTag, line.Length());
1010    
1011     while (pos < line.Length())
1012     fDocOutput->ReplaceSpecialChars(line, pos);
1013    
1014     pos = line.Length();
1015     // skip the remainder of the line
1016     copiedToCommentUpTo = line.Length();
1017     line += remainder;
1018     }
1019    
1020     // no else - "end" can also be set by begin having an end tag!
1021     if (end) {
1022    
1023     if (!begin)
1024     pos += word.Length(); // skip the keyword
1025     else pos += word.Length() - 2; // "Begin" is 2 chars longer than "End"
1026    
1027     if (!directive) directive = (TDocDirective*) fDirectiveHandlers.Last();
1028    
1029     if (!directive) {
1030     Warning("HandleDirective", "Cannot find directive handler object!",
1031     fLineRaw.Data());
1032     return kFALSE;
1033     }
1034    
1035     if (!begin) {
1036     Ssiz_t start = 0;
1037     if (!InContext(kComment) || (InContext(kComment) & kCXXComment)) {
1038     // means we are in a C++ comment
1039     while (isspace((UChar_t)fLineRaw[start])) ++start;
1040     if (fLineRaw[start] == '/' && fLineRaw[start + 1] == '/')
1041     start += 2;
1042     else start = 0;
1043     }
1044     directive->AddLine(line(start, pos - word.Length() - start));
1045    
1046     TString remainder(line(pos, line.Length()));
1047     line.Remove(pos, line.Length());
1048     fDocOutput->ReplaceSpecialChars(line);
1049     pos = line.Length();
1050     line += remainder;
1051     }
1052     copiedToCommentUpTo = pos;
1053    
1054     TString result;
1055     directive->GetResult(result);
1056    
1057     if (!begin)
1058     fDirectiveHandlers.Remove(fDirectiveHandlers.LastLink());
1059     delete directive;
1060    
1061     if (!begin) {
1062     // common to all directives: pop context
1063     Bool_t isInCxxComment = InContext(kDirective) & kCXXComment;
1064     if (fParseContext.size()>1)
1065     fParseContext.pop_back();
1066     if (isInCxxComment && !InContext(kComment)) {
1067     fParseContext.push_back(kComment | kCXXComment);
1068     fDocOutput->DecorateEntityBegin(line, pos, kComment);
1069     }
1070     }
1071    
1072     if (InContext(kDirective) && fDirectiveHandlers.Last())
1073     ((TDocDirective*)fDirectiveHandlers.Last())->AddLine(result(0, result.Length()));
1074     else
1075     fLineComment += result;
1076    
1077     /* NO - this can happen e.g. for "BEGIN_HTML / *..." (see doc in this class)
1078     if (Context() != kComment) {
1079     Warning("HandleDirective", "Popping back a directive context, but enclosing context is not a comment! At:\n%s",
1080     fLineRaw.Data());
1081     fParseContext.push_back(kComment);
1082     }
1083     */
1084     }
1085    
1086     return kTRUE;
1087     }
1088    
1089     //______________________________________________________________________________
1090     UInt_t TDocParser::InContext(Int_t context) const
1091     {
1092     // checks whether we are in a parse context, return the entry closest
1093     // to the current context.
1094     // If context is a EParseContextFlag just look for the first match in
1095     // the flags
1096    
1097     UInt_t lowerContext = context & kParseContextMask;
1098     UInt_t contextFlag = context & kParseContextFlagMask;
1099    
1100     for (std::list<UInt_t>::const_reverse_iterator iPC = fParseContext.rbegin();
1101     iPC != fParseContext.rend(); ++iPC)
1102     if (!lowerContext || ((lowerContext && ((*iPC & kParseContextMask) == lowerContext))
1103     && (!contextFlag || (contextFlag && (*iPC & contextFlag)))))
1104     return *iPC;
1105    
1106     return 0;
1107     }
1108    
1109     //______________________________________________________________________________
1110     void TDocParser::InitKeywords() const
1111     {
1112     // fill C++ keywords into fgKeywords
1113    
1114     if (!fgKeywords.empty())
1115     return;
1116    
1117     fgKeywords.insert("asm");
1118     fgKeywords.insert("auto");
1119     fgKeywords.insert("bool");
1120     fgKeywords.insert("break");
1121     fgKeywords.insert("case");
1122     fgKeywords.insert("catch");
1123     fgKeywords.insert("char");
1124     fgKeywords.insert("class");
1125     fgKeywords.insert("const");
1126     fgKeywords.insert("const_cast");
1127     fgKeywords.insert("continue");
1128     fgKeywords.insert("default");
1129     fgKeywords.insert("delete");
1130     fgKeywords.insert("do");
1131     fgKeywords.insert("double");
1132     fgKeywords.insert("dynamic_cast");
1133     fgKeywords.insert("else");
1134     fgKeywords.insert("enum");
1135     fgKeywords.insert("explicit");
1136     fgKeywords.insert("export");
1137     fgKeywords.insert("extern");
1138     fgKeywords.insert("false");
1139     fgKeywords.insert("float");
1140     fgKeywords.insert("for");
1141     fgKeywords.insert("friend");
1142     fgKeywords.insert("goto");
1143     fgKeywords.insert("if");
1144     fgKeywords.insert("inline");
1145     fgKeywords.insert("int");
1146     fgKeywords.insert("long");
1147     fgKeywords.insert("mutable");
1148     fgKeywords.insert("namespace");
1149     fgKeywords.insert("new");
1150     fgKeywords.insert("operator");
1151     fgKeywords.insert("private");
1152     fgKeywords.insert("protected");
1153     fgKeywords.insert("public");
1154     fgKeywords.insert("register");
1155     fgKeywords.insert("reinterpret_cast");
1156     fgKeywords.insert("return");
1157     fgKeywords.insert("short");
1158     fgKeywords.insert("signed");
1159     fgKeywords.insert("sizeof");
1160     fgKeywords.insert("static");
1161     fgKeywords.insert("static_cast");
1162     fgKeywords.insert("struct");
1163     fgKeywords.insert("switch");
1164     fgKeywords.insert("template");
1165     fgKeywords.insert("this");
1166     fgKeywords.insert("throw");
1167     fgKeywords.insert("true");
1168     fgKeywords.insert("try");
1169     fgKeywords.insert("typedef");
1170     fgKeywords.insert("typeid");
1171     fgKeywords.insert("typename");
1172     fgKeywords.insert("union");
1173     fgKeywords.insert("unsigned");
1174     fgKeywords.insert("using");
1175     fgKeywords.insert("virtual");
1176     fgKeywords.insert("void");
1177     fgKeywords.insert("volatile");
1178     fgKeywords.insert("wchar_t");
1179     fgKeywords.insert("while");
1180     }
1181    
1182     //______________________________________________________________________________
1183     TClass* TDocParser::IsDirective(const TString& line, Ssiz_t pos,
1184     const TString& word, Bool_t& begin) const
1185     {
1186     // return whether word at line's pos is a valid directive, and returns its
1187     // TDocDirective's TClass object, or 0 if it's not a directive. Set begin
1188     // to kTRUE for "Begin_..."
1189     // You can implement your own handlers by implementing a class deriving
1190     // from TDocHandler, and calling it TDocTagDirective for "BEGIN_TAG",
1191     // "END_TAG" blocks.
1192    
1193     // '"' serves as escape char
1194     if (pos > 0 && line[pos - 1] == '"')
1195     return 0;
1196    
1197     begin = word.BeginsWith("begin_", TString::kIgnoreCase);
1198     Bool_t end = word.BeginsWith("end_", TString::kIgnoreCase);
1199    
1200     if (!begin && !end)
1201     return 0;
1202    
1203     /* NO - we can have "BEGIN_HTML / * ..."
1204     if (!InContext(kComment))
1205     return 0;
1206     */
1207    
1208     TString tag = word( begin ? 6 : 4, word.Length());
1209    
1210     if (!tag.Length())
1211     return 0;
1212    
1213     tag.ToLower();
1214     tag[0] -= 'a' - 'A'; // first char is caps
1215     tag.Prepend("TDoc");
1216     tag += "Directive";
1217    
1218     TClass* clDirective = TClass::GetClass(tag, kFALSE);
1219    
1220     if (gDebug > 0 && !clDirective)
1221     Warning("IsDirective", "Unknown THtml directive %s in line %d!", word.Data(), fLineNo);
1222    
1223     return clDirective;
1224     }
1225    
1226     //______________________________________________________________________________
1227     Bool_t TDocParser::IsName(UChar_t c)
1228     {
1229     // Check if c is a valid C++ name character
1230     //
1231     //
1232     // Input: c - a single character
1233     //
1234     // Output: TRUE if c is a valid C++ name character
1235     // and FALSE if it's not.
1236     //
1237     // NOTE: Valid name characters are [a..zA..Z0..9_~],
1238     //
1239    
1240     Bool_t ret = kFALSE;
1241    
1242     if (isalnum(c) || c == '_' || c == '~')
1243     ret = kTRUE;
1244    
1245     return ret;
1246     }
1247    
1248    
1249     //______________________________________________________________________________
1250     Bool_t TDocParser::IsWord(UChar_t c)
1251     {
1252     // Check if c is a valid first character for C++ name
1253     //
1254     //
1255     // Input: c - a single character
1256     //
1257     // Output: TRUE if c is a valid first character for C++ name,
1258     // and FALSE if it's not.
1259     //
1260     // NOTE: Valid first characters are [a..zA..Z_~]
1261     //
1262    
1263     Bool_t ret = kFALSE;
1264    
1265     if (isalpha(c) || c == '_' || c == '~')
1266     ret = kTRUE;
1267    
1268     return ret;
1269     }
1270    
1271    
1272     //______________________________________________________________________________
1273     TMethod* TDocParser::LocateMethodInCurrentLine(Ssiz_t &posMethodName, TString& ret, TString& name, TString& params,
1274     std::ostream &srcOut, TString &anchor, std::ifstream& sourceFile, Bool_t allowPureVirtual)
1275     {
1276     // Search for a method starting at posMethodName, and return its return type,
1277     // its name, and its arguments. If the end of arguments is not found in the
1278     // current line, get a new line from sourceFile, beautify it to srcOut, creating
1279     // an anchor as necessary. When this function returns, posMethodName points to the
1280     // end of the function declaration, i.e. right after the arguments' closing bracket.
1281     // If posMethodName == kNPOS, we look for the first matching method in fMethodCounts.
1282    
1283     typedef std::map<std::string /*method name*/, Int_t > MethodCount_t;
1284    
1285     if (posMethodName == kNPOS) {
1286     name.Remove(0);
1287     TMethod * meth = 0;
1288     Ssiz_t posBlock = fLineRaw.Index('{');
1289     if (posBlock == kNPOS)
1290     posBlock = fLineRaw.Length();
1291     for (MethodCount_t::iterator iMethodName = fMethodCounts.begin();
1292     !name.Length() && iMethodName != fMethodCounts.end(); ++iMethodName) {
1293     TString lookFor(iMethodName->first);
1294     posMethodName = fLineRaw.Index(lookFor);
1295     if (posMethodName != kNPOS && posMethodName < posBlock
1296     && (posMethodName == 0 || !IsWord(fLineRaw[posMethodName - 1]))) {
1297     // check whether the method name is followed by optional spaces and
1298     // an opening parathesis
1299     Ssiz_t posMethodEnd = posMethodName + lookFor.Length();
1300     while (isspace((UChar_t)fLineRaw[posMethodEnd])) ++posMethodEnd;
1301     if (fLineRaw[posMethodEnd] == '(') {
1302     meth = LocateMethodInCurrentLine(posMethodName, ret, name, params, srcOut,
1303     anchor, sourceFile, allowPureVirtual);
1304     if (name.Length())
1305     return meth;
1306     }
1307     }
1308     }
1309     return 0;
1310     }
1311    
1312     name = fLineRaw(posMethodName, fLineRaw.Length() - posMethodName);
1313    
1314     // extract return type
1315     ret = fLineRaw(0, posMethodName);
1316     if (ret.Length()) {
1317     while (ret.Length() && (IsName(ret[ret.Length() - 1]) || ret[ret.Length()-1] == ':'))
1318     ret.Remove(ret.Length() - 1, 1);
1319     Strip(ret);
1320     Bool_t didSomething = kTRUE;
1321     while (didSomething) {
1322     didSomething = kFALSE;
1323     if (ret.BeginsWith("inline ")) {
1324     didSomething = kTRUE;
1325     ret.Remove(0, 7);
1326     }
1327     if (ret.BeginsWith("static ")) {
1328     didSomething = kTRUE;
1329     ret.Remove(0, 7);
1330     }
1331     if (ret.BeginsWith("virtual ")) {
1332     didSomething = kTRUE;
1333     ret.Remove(0, 8);
1334     }
1335     } // while replacing static, virtual, inline
1336     Strip(ret);
1337     }
1338    
1339     // extract parameters
1340     Ssiz_t posParam = name.First('(');
1341     if (posParam == kNPOS ||
1342     // no strange return types, please
1343     ret.Contains("{") || ret.Contains("}") || ret.Contains("(") || ret.Contains(")")
1344     || ret.Contains("=")) {
1345     ret.Remove(0);
1346     name.Remove(0);
1347     params.Remove(0);
1348     return 0;
1349     }
1350    
1351     if (name.BeginsWith("operator")) {
1352     // op () (...)
1353     Ssiz_t checkOpBracketParam = posParam + 1;
1354     while (isspace((UChar_t)name[checkOpBracketParam]))
1355     ++checkOpBracketParam;
1356     if (name[checkOpBracketParam] == ')') {
1357     ++checkOpBracketParam;
1358     while (isspace((UChar_t)name[checkOpBracketParam]))
1359     ++checkOpBracketParam;
1360     if (name[checkOpBracketParam] == '(')
1361     posParam = checkOpBracketParam;
1362     }
1363     } // check for op () (...)
1364    
1365     if (posParam == kNPOS) {
1366     ret.Remove(0);
1367     name.Remove(0);
1368     params.Remove(0);
1369     return 0;
1370     }
1371    
1372     params = name(posParam, name.Length() - posParam);
1373     name.Remove(posParam);
1374     while (name.Length() && isspace((UChar_t)name[name.Length() - 1]))
1375     name.Remove(name.Length() - 1);
1376     if (!name.Length()) {
1377     ret.Remove(0);
1378     name.Remove(0);
1379     params.Remove(0);
1380     return 0;
1381     }
1382    
1383     MethodCount_t::const_iterator iMethodName = fMethodCounts.find(name.Data());
1384     if (iMethodName == fMethodCounts.end() || iMethodName->second <= 0) {
1385     ret.Remove(0);
1386     name.Remove(0);
1387     params.Remove(0);
1388     return 0;
1389     }
1390    
1391     // find end of param
1392     Ssiz_t posParamEnd = 1;
1393     Int_t bracketLevel = 1;
1394     while (bracketLevel) {
1395     const char* paramEnd = strpbrk(params.Data() + posParamEnd, ")(\"'");
1396     if (!paramEnd) {
1397     // func with params over multiple lines
1398     // gotta write out this line before it gets lost
1399     if (!anchor.Length()) {
1400     // request an anchor, just in case...
1401     AnchorFromLine(fLineStripped, anchor);
1402     if (srcOut)
1403     srcOut << "<a name=\"" << anchor << "\"></a>";
1404     }
1405     if (srcOut)
1406     WriteSourceLine(srcOut);
1407    
1408     fLineRaw.ReadLine(sourceFile, kFALSE);
1409     if (sourceFile.eof()) {
1410     Error("LocateMethodInCurrentLine",
1411     "Cannot find end of signature for function %s!",
1412     name.Data());
1413     break;
1414     }
1415    
1416     fCommentAtBOL = kFALSE;
1417    
1418     // replace class names etc
1419     fLineStripped = fLineRaw;
1420     Strip(fLineStripped);
1421    
1422     fLineSource = fLineRaw;
1423     DecorateKeywords(fLineSource);
1424    
1425     posParamEnd = params.Length();
1426     params += fLineRaw;
1427     } else
1428     posParamEnd = paramEnd - params.Data();
1429     switch (params[posParamEnd]) {
1430     case '(': ++bracketLevel; ++posParamEnd; break;
1431     case ')': --bracketLevel; ++posParamEnd; break;
1432     case '"': // skip ")"
1433     ++posParamEnd;
1434     while (params.Length() > posParamEnd && params[posParamEnd] != '"') {
1435     // skip '\"'
1436     if (params[posParamEnd] == '\\') ++posParamEnd;
1437     ++posParamEnd;
1438     }
1439     if (params.Length() <= posParamEnd) {
1440     // something is seriously wrong - skip :-/
1441     ret.Remove(0);
1442     name.Remove(0);
1443     params.Remove(0);
1444     return 0;
1445     }
1446     ++posParamEnd; // skip trailing '"'
1447     break;
1448     case '\'': // skip ')'
1449     ++posParamEnd;
1450     if (params[posParamEnd] == '\\') ++posParamEnd;
1451     posParamEnd += 2;
1452     break;
1453     default:
1454     ++posParamEnd;
1455     }
1456     } // while bracketlevel, i.e. (...(..)...)
1457     Ssiz_t posBlock = params.Index('{', posParamEnd);
1458     Ssiz_t posSemicolon = params.Index(';', posParamEnd);
1459     Ssiz_t posPureVirt = params.Index('=', posParamEnd);
1460     if (posSemicolon != kNPOS)
1461     if ((posBlock == kNPOS || (posSemicolon < posBlock)) &&
1462     (posPureVirt == kNPOS || !allowPureVirtual)
1463     && !allowPureVirtual) // allow any "func();" if pv is allowed
1464     params.Remove(0);
1465    
1466     if (params.Length())
1467     params.Remove(posParamEnd);
1468    
1469     if (!params.Length()) {
1470     ret.Remove(0);
1471     name.Remove(0);
1472     return 0;
1473     }
1474     // update posMethodName to point behind the method
1475     posMethodName = posParam + posParamEnd;
1476     if (fCurrentClass) {
1477     TMethod* meth = fCurrentClass->GetMethodAny(name);
1478     if (meth) {
1479     fDirectiveCount = 0;
1480     fCurrentMethodTag = name + "_";
1481     fCurrentMethodTag += fMethodCounts[name.Data()];
1482     return meth;
1483     }
1484     }
1485    
1486     return 0;
1487     }
1488    
1489    
1490     //______________________________________________________________________________
1491     void TDocParser::Parse(std::ostream& out)
1492     {
1493     // Locate methods, starting in the source file, then inline, then
1494     // immediately inside the class declaration. While doing that also
1495     // find the class description and special tags like the macro tag etc.
1496    
1497     fClassDocState = kClassDoc_LookingNothingFound;
1498    
1499     DeleteDirectiveOutput();
1500    
1501     LocateMethodsInSource(out);
1502     LocateMethodsInHeaderInline(out);
1503     LocateMethodsInHeaderClassDecl(out);
1504    
1505     if (!fSourceInfo[kInfoLastUpdate].Length()) {
1506     TDatime date;
1507     fSourceInfo[kInfoLastUpdate] = date.AsString();
1508     }
1509     }
1510    
1511     //______________________________________________________________________________
1512     void TDocParser::LocateMethods(std::ostream& out, const char* filename,
1513     Bool_t lookForSourceInfo /*= kTRUE*/,
1514     Bool_t useDocxxStyle /*= kFALSE*/,
1515     Bool_t allowPureVirtual /*= kFALSE*/,
1516     const char* methodPattern /*= 0*/,
1517     const char* sourceExt /*= 0 */)
1518     {
1519     // Collect methods from the source or header file called filename.
1520     // It generates a beautified version of the source file on the fly;
1521     // the output file is given by the fCurrentClass's name, and sourceExt.
1522     // Documentation is extracted to out.
1523     // lookForSourceInfo: if set, author, lastUpdate, and copyright are
1524     // extracted (i.e. the values contained in fSourceInfo)
1525     // useDocxxStyle: if set, documentation can be in front of the method
1526     // name, not only inside the method. Useful doc Doc++/Doxygen style,
1527     // and inline methods.
1528     // lookForClassDescr: if set, the first line matching the class description
1529     // rules is assumed to be the class description for fCurrentClass; the
1530     // description is written to out.
1531     // methodPattern: if set, methods have to be prepended by this tag. Usually
1532     // the class name + "::". In header files, looking for in-place function
1533     // definitions, this should be 0. In that case, only functions in
1534     // fMethodCounts are searched for.
1535    
1536     TString sourceFileName(filename);
1537     fCurrentFile = filename;
1538     if (!sourceFileName.Length()) {
1539     fHtml->GetImplFileName(fCurrentClass, kFALSE, sourceFileName);
1540     Error("LocateMethods", "Can't find source file '%s' for class %s!",
1541     sourceFileName.Data(), fCurrentClass->GetName());
1542     return;
1543     }
1544     ifstream sourceFile(sourceFileName.Data());
1545     if (!sourceFile || !sourceFile.good()) {
1546     Error("LocateMethods", "Can't open file '%s' for reading!", sourceFileName.Data());
1547     return;
1548     }
1549    
1550     TString pattern(methodPattern);
1551    
1552     TString codeOneLiner;
1553     TString methodRet;
1554     TString methodName;
1555     TString methodParam;
1556     TString anchor;
1557     TString docxxComment;
1558    
1559     Bool_t wroteMethodNowWaitingForOpenBlock = kFALSE;
1560    
1561     std::ofstream srcHtmlOut;
1562     TString srcHtmlOutName;
1563     if (sourceExt && sourceExt[0])
1564     dynamic_cast<TClassDocOutput*>(fDocOutput)->CreateSourceOutputStream(srcHtmlOut, sourceExt, srcHtmlOutName);
1565     else {
1566     sourceExt = 0;
1567     srcHtmlOutName = fCurrentClass->GetName();
1568     fDocOutput->NameSpace2FileName(srcHtmlOutName);
1569     gSystem->PrependPathName("src", srcHtmlOutName);
1570     srcHtmlOutName += ".h.html";
1571     }
1572    
1573     fParseContext.clear();
1574     fParseContext.push_back(kCode);
1575     fDocContext = kIgnore;
1576     fLineNo = 0;
1577    
1578     while (!sourceFile.eof()) {
1579     Bool_t needAnchor = kFALSE;
1580    
1581     ++fLineNo; // we count fortrany
1582    
1583     fLineRaw.ReadLine(sourceFile, kFALSE);
1584     if (sourceFile.eof()) break;
1585    
1586     fCommentAtBOL = InContext(kComment);
1587    
1588     // replace class names etc
1589     fLineStripped = fLineRaw;
1590     Strip(fLineStripped);
1591    
1592     fLineSource = fLineRaw;
1593     fLineComment = "";
1594     DecorateKeywords(fLineSource);
1595    
1596     if (!ProcessComment()) {
1597     // not a commented line
1598    
1599     if (fDocContext == kDocClass && fClassDocState < kClassDoc_Written) {
1600     TString strippedComment(fComment);
1601     Strip(strippedComment);
1602     if (strippedComment.Length() > 0) {
1603     fLastClassDoc = fComment;
1604     if (fClassDocState == kClassDoc_LookingNothingFound) {
1605     fFirstClassDoc = fComment;
1606     fClassDocState = kClassDoc_LookingHaveSomething;
1607     }
1608     }
1609     fDocContext = kIgnore;
1610     }
1611    
1612     Ssiz_t impIdx = fLineStripped.Index("ClassImp(");
1613     if (impIdx == 0 && fClassDocState == kClassDoc_LookingHaveSomething) {
1614     TString name(fCurrentClass->GetName());
1615     // take unscoped version
1616     Ssiz_t posLastScope = kNPOS;
1617     while ((posLastScope = name.Index("::")) != kNPOS)
1618     name.Remove(0, posLastScope + 2);
1619    
1620     Ssiz_t posName = fLineStripped.Index(name, impIdx);
1621     if (posName != kNPOS) {
1622     Ssiz_t posClosingParen = posName + name.Length();
1623     while (isspace(fLineStripped[posClosingParen])) ++posClosingParen;
1624     if (fLineStripped[posClosingParen] == ')') {
1625     WriteClassDoc(out, kFALSE);
1626     fDocContext = kIgnore;
1627     }
1628     }
1629     }
1630    
1631     if (fLineStripped.Length())
1632     // remove last class doc if it not followed by ClassImp
1633     // (with optional empty lines in between)
1634     fLastClassDoc = "";
1635    
1636     // write previous method
1637     if (methodName.Length() && !wroteMethodNowWaitingForOpenBlock) {
1638     if (useDocxxStyle && docxxComment.Length())
1639     fComment = docxxComment;
1640     WriteMethod(out, methodRet, methodName, methodParam,
1641     gSystem->BaseName(srcHtmlOutName), anchor, codeOneLiner);
1642     docxxComment.Remove(0);
1643     }
1644    
1645     if (!wroteMethodNowWaitingForOpenBlock) {
1646     // check for method
1647     Ssiz_t posPattern = pattern.Length() ? fLineRaw.Index(pattern) : kNPOS;
1648     if (posPattern != kNPOS || !pattern.Length()) {
1649     posPattern += pattern.Length();
1650     LocateMethodInCurrentLine(posPattern, methodRet, methodName,
1651     methodParam, srcHtmlOut, anchor, sourceFile, allowPureVirtual);
1652     if (methodName.Length()) {
1653     fDocContext = kDocFunc;
1654     needAnchor = !anchor.Length();
1655     if (useDocxxStyle)
1656     docxxComment = fComment;
1657     fComment.Remove(0);
1658     codeOneLiner.Remove(0);
1659    
1660     wroteMethodNowWaitingForOpenBlock = fLineRaw.Index("{", posPattern) == kNPOS;
1661     wroteMethodNowWaitingForOpenBlock &= fLineRaw.Index(";", posPattern) == kNPOS;
1662     } else if (fLineRaw.First("{};") != kNPOS)
1663     // these chars reset the preceding comment
1664     fComment.Remove(0);
1665     } // pattern matches - could be a method
1666     else
1667     fComment.Remove(0);
1668     } else {
1669     wroteMethodNowWaitingForOpenBlock &= fLineRaw.Index("{") == kNPOS;
1670     wroteMethodNowWaitingForOpenBlock &= fLineRaw.Index(";") == kNPOS;
1671     } // if !wroteMethodNowWaitingForOpenBlock
1672    
1673     if (methodName.Length() && !wroteMethodNowWaitingForOpenBlock) {
1674     // make sure we don't have more '{' in commentLine than in fLineRaw
1675     if (!codeOneLiner.Length() &&
1676     fLineSource.CountChar('{') == 1 &&
1677     fLineSource.CountChar('}') == 1) {
1678     // a one-liner
1679     codeOneLiner = fLineSource;
1680     codeOneLiner.Remove(0, codeOneLiner.Index('{'));
1681     codeOneLiner.Remove(codeOneLiner.Index('}') + 1);
1682     }
1683     } // if method name and '{'
1684     // else not a comment, and we don't need the previous one:
1685     else if (!methodName.Length() && !useDocxxStyle)
1686     fComment.Remove(0);
1687    
1688     if (needAnchor || fExtraLinesWithAnchor.find(fLineNo) != fExtraLinesWithAnchor.end()) {
1689     AnchorFromLine(fLineStripped, anchor);
1690     if (sourceExt)
1691     srcHtmlOut << "<a name=\"" << anchor << "\"></a>";
1692     }
1693     // else anchor.Remove(0); - NO! WriteMethod will need it later!
1694     } // if !comment
1695    
1696     // check for last update,...
1697     Ssiz_t posTag = kNPOS;
1698     if (lookForSourceInfo)
1699     for (Int_t si = 0; si < (Int_t) kNumSourceInfos; ++si)
1700     if (!fSourceInfo[si].Length() && (posTag = fLineRaw.Index(fSourceInfoTags[si])) != kNPOS) {
1701     fSourceInfo[si] = fLineRaw(posTag + strlen(fSourceInfoTags[si]), fLineRaw.Length() - posTag);
1702     if (si == kInfoAuthor)
1703     fDocOutput->FixupAuthorSourceInfo(fSourceInfo[kInfoAuthor]);
1704     }
1705    
1706    
1707     // write to .cxx.html
1708     if (srcHtmlOut)
1709     WriteSourceLine(srcHtmlOut);
1710     else if (needAnchor)
1711     fExtraLinesWithAnchor.insert(fLineNo);
1712     } // while !sourceFile.eof()
1713    
1714     // deal with last func
1715     if (methodName.Length()) {
1716     if (useDocxxStyle && docxxComment.Length())
1717     fComment = docxxComment;
1718     WriteMethod(out, methodRet, methodName, methodParam,
1719     gSystem->BaseName(srcHtmlOutName), anchor, codeOneLiner);
1720     docxxComment.Remove(0);
1721     } else
1722     WriteClassDoc(out);
1723    
1724     srcHtmlOut << "</pre>" << std::endl;
1725     fDocOutput->WriteHtmlFooter(srcHtmlOut, "../");
1726    
1727     fParseContext.clear();
1728     fParseContext.push_back(kCode);
1729     fDocContext = kIgnore;
1730     fCurrentFile = "";
1731     }
1732    
1733     //______________________________________________________________________________
1734     void TDocParser::LocateMethodsInSource(std::ostream& out)
1735     {
1736     // Given fCurrentClass, look for methods in its source file,
1737     // and extract documentation to out, while beautifying the source
1738     // file in parallel.
1739    
1740     // for Doc++ style
1741     Bool_t useDocxxStyle = (fHtml->GetDocStyle() == "Doc++");
1742    
1743     TString pattern(fCurrentClass->GetName());
1744     // take unscoped version
1745     Ssiz_t posLastScope = kNPOS;
1746     while ((posLastScope = pattern.Index("::")) != kNPOS)
1747     pattern.Remove(0, posLastScope + 2);
1748     pattern += "::";
1749    
1750     TString implFileName;
1751     if (fHtml->GetImplFileName(fCurrentClass, kTRUE, implFileName))
1752     LocateMethods(out, implFileName, kFALSE /*source info*/, useDocxxStyle,
1753     kFALSE /*allowPureVirtual*/, pattern, ".cxx.html");
1754     else out << "</div>" << endl; // close class descr div
1755     }
1756    
1757     //______________________________________________________________________________
1758     void TDocParser::LocateMethodsInHeaderInline(std::ostream& out)
1759     {
1760     // Given fCurrentClass, look for methods in its header file,
1761     // and extract documentation to out.
1762    
1763     // for inline methods, always allow doc before func
1764     Bool_t useDocxxStyle = kTRUE;
1765    
1766     TString pattern(fCurrentClass->GetName());
1767     // take unscoped version
1768     Ssiz_t posLastScope = kNPOS;
1769     while ((posLastScope = pattern.Index("::")) != kNPOS)
1770     pattern.Remove(0, posLastScope + 1);
1771     pattern += "::";
1772    
1773     TString declFileName;
1774     if (fHtml->GetDeclFileName(fCurrentClass, kTRUE, declFileName))
1775     LocateMethods(out, declFileName, kTRUE /*source info*/, useDocxxStyle,
1776     kFALSE /*allowPureVirtual*/, pattern, 0);
1777     }
1778    
1779     //______________________________________________________________________________
1780     void TDocParser::LocateMethodsInHeaderClassDecl(std::ostream& out)
1781     {
1782     // Given fCurrentClass, look for methods in its header file's
1783     // class declaration block, and extract documentation to out,
1784     // while beautifying the header file in parallel.
1785    
1786     TString declFileName;
1787     if (fHtml->GetDeclFileName(fCurrentClass, kTRUE, declFileName))
1788     LocateMethods(out, declFileName, kTRUE/*source info*/, kTRUE /*useDocxxStyle*/,
1789     kTRUE /*allowPureVirtual*/, 0, ".h.html");
1790     }
1791    
1792     //______________________________________________________________________________
1793     Bool_t TDocParser::ProcessComment()
1794     {
1795     // Parse the current line as a comment, handling directives and re-formatting
1796     // the comment: remove "/*", "*/", "//", similar characters surrounding lines,
1797     // etc.
1798     //
1799     // Return kFALSE if the line is not a comment.
1800    
1801     if (!fCommentAtBOL
1802     && !(fLineStripped[0] == '/'
1803     && (fLineStripped[1] == '/' || fLineStripped[1] == '*'))
1804     && !InContext(kComment) && !InContext(kDirective)) {
1805     fLineComment = "";
1806     return kFALSE;
1807     }
1808    
1809     //if (InContext(kDirective))
1810     // return kTRUE; - NO! we might have a comment from a previous directive!
1811    
1812     // don't write out empty lines if the current directive is eating the line
1813     if (InContext(kDirective) && !fLineComment.Length())
1814     return kTRUE;
1815    
1816     TString commentLine(fLineComment.Strip());
1817    
1818     // remove all <span class="comment"> tags
1819     Bool_t mustDealWithCommentAtBOL = fCommentAtBOL; // whether we had a closing "*/"
1820     Ssiz_t posComment = kNPOS;
1821     if (!fCommentAtBOL)
1822     posComment = commentLine.Index("<span class=\"comment\">", 0, TString::kIgnoreCase);
1823     Ssiz_t posSpanEnd = commentLine.Index("</span>", posComment == kNPOS?0:posComment, TString::kIgnoreCase);
1824     while ((mustDealWithCommentAtBOL && posSpanEnd != kNPOS) || posComment != kNPOS) {
1825     Int_t spanLevel = 1;
1826     Ssiz_t posSpan = commentLine.Index("<span", posComment + 1, TString::kIgnoreCase);
1827     while (spanLevel > 1 || (posSpan != kNPOS && posSpan < posSpanEnd)) {
1828     // another span was opened, take the next </span>
1829     if (posSpan != kNPOS && posSpan < posSpanEnd) {
1830     ++spanLevel;
1831     posSpan = commentLine.Index("<span", posSpan + 1, TString::kIgnoreCase);
1832     // posSpanEnd doesn't change
1833     continue;
1834     } // else
1835     --spanLevel;
1836     // posSpan doesn't change
1837     posSpanEnd = commentLine.Index("</span>", posSpanEnd + 1, TString::kIgnoreCase);
1838     }
1839     if (posSpanEnd != kNPOS) {
1840     // only remove span if </span> if it exists (or we end up with unbalanced spans)
1841     commentLine.Remove(posSpanEnd, 7);
1842     if (posComment != kNPOS)
1843     commentLine.Remove(posComment, 22);
1844     else {
1845     mustDealWithCommentAtBOL = kFALSE;
1846     // now remove C comments
1847     posComment = 0;
1848     }
1849     posComment = commentLine.Index("<span class=\"comment\">", posComment, TString::kIgnoreCase);
1850     } else break;
1851     }
1852     if (posComment != kNPOS)
1853     commentLine.Remove(posComment, 22);
1854    
1855     // don't strip in C comments, do strip if opening:
1856     if (!InContext(kComment) || (InContext(kComment) & kCXXComment)
1857     || (fLineStripped[0] == '/' && fLineStripped[1] == '*'))
1858     Strip(commentLine);
1859    
1860     // look for start tag of class description
1861     if ((fClassDocState == kClassDoc_LookingNothingFound
1862     || fClassDocState == kClassDoc_LookingHaveSomething)
1863     && !fComment.Length()
1864     && fDocContext == kIgnore && commentLine.Contains(fClassDescrTag)) {
1865     fDocContext = kDocClass;
1866     }
1867    
1868     char start_or_end = 0;
1869     // remove leading /*, //
1870     if (commentLine.Length()>1 && commentLine[0] == '/'
1871     && (commentLine[1] == '/' || commentLine[1] == '*')) {
1872     start_or_end = commentLine[1];
1873     commentLine.Remove(0, 2);
1874     }
1875     // remove trailing */
1876     if (start_or_end != '/' && commentLine.Length()>1
1877     && commentLine[commentLine.Length() - 2] == '*'
1878     && commentLine[commentLine.Length() - 1] == '/') {
1879     start_or_end = commentLine[commentLine.Length() - 2];
1880     commentLine.Remove(commentLine.Length()-2);
1881     }
1882    
1883     // remove repeating characters from the end of the line
1884     if (start_or_end && commentLine.Length() > 3) {
1885     TString lineAllOneChar(commentLine.Strip());
1886    
1887     Ssiz_t len = lineAllOneChar.Length();
1888     if (len > 2) {
1889     Char_t c = lineAllOneChar[len - 1];
1890     if (c == lineAllOneChar[len - 2] && c == lineAllOneChar[len - 3]) {
1891     TString lineAllOneCharStripped = lineAllOneChar.Strip(TString::kTrailing, c);
1892     Strip(lineAllOneCharStripped);
1893     if (!lineAllOneCharStripped.Length()) {
1894     commentLine.Remove(0);
1895    
1896     // also a class doc signature: line consists of ////
1897     if ((fClassDocState == kClassDoc_LookingNothingFound
1898     || fClassDocState == kClassDoc_LookingHaveSomething)
1899     && !fComment.Length()
1900     && fDocContext == kIgnore && start_or_end=='/') {
1901     fDocContext = kDocClass;
1902     }
1903     }
1904     }
1905     }
1906     }
1907    
1908     // remove leading and trailing chars from e.g. // some doc //
1909     if (commentLine.Length() > 0 && start_or_end == commentLine[commentLine.Length() - 1])
1910     // we already removed it as part of // or / *; also remove the trailing
1911     commentLine = commentLine.Strip(TString::kTrailing, start_or_end);
1912    
1913     if (commentLine.Length() > 2 && Context() != kDirective)
1914     while (commentLine.Length() > 2
1915     && !IsWord(commentLine[0])
1916     && commentLine[0] == commentLine[commentLine.Length() - 1])
1917     commentLine = commentLine.Strip(TString::kBoth, commentLine[0]);
1918    
1919     // remove leading '/' if we had // or '*' if we had / *
1920     while (start_or_end && commentLine[0] == start_or_end)
1921     commentLine.Remove(0, 1);
1922    
1923     fComment += commentLine + "\n";
1924    
1925     return kTRUE;
1926     }
1927    
1928     //______________________________________________________________________________
1929     void TDocParser::RemoveCommentContext(Bool_t cxxcomment)
1930     {
1931     // remove the top-most comment context that matches cxxcomment,
1932    
1933     UInt_t lookFor = kComment;
1934     if (cxxcomment) lookFor |= kCXXComment;
1935     std::list<UInt_t>::iterator iComment = fParseContext.end();
1936     for (std::list<UInt_t>::iterator iContext = fParseContext.begin();
1937     iContext != fParseContext.end(); ++ iContext)
1938     if (*iContext == lookFor) iComment =iContext;
1939     if (iComment != fParseContext.end())
1940     fParseContext.erase(iComment);
1941     }
1942    
1943     //______________________________________________________________________________
1944     Bool_t TDocParser::Strip(TString& str)
1945     {
1946     // strips ' ', tabs, and newlines from both sides of str
1947     Bool_t changed = str[0] == ' ' || str[0] == '\t' || str[0] == '\n';
1948     changed |= str.Length()
1949     && (str[str.Length() - 1] == ' ' || str[str.Length() - 1] == '\t'
1950     || str[str.Length() - 1] == '\n');
1951     if (!changed) return kFALSE;
1952     Ssiz_t i = 0;
1953     while (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')
1954     ++i;
1955     str.Remove(0,i);
1956     i = str.Length() - 1;
1957     while (i >= 0 && (str[i] == ' ' || str[i] == '\t' || str[i] == '\n'))
1958     --i;
1959     str.Remove(i + 1, str.Length());
1960     return kTRUE;
1961     }
1962    
1963     //______________________________________________________________________________
1964     void TDocParser::WriteClassDoc(std::ostream& out, Bool_t first /*= kTRUE*/)
1965     {
1966     // Write the class description depending (among others) on fClassDocState.
1967    
1968     if (fClassDocState == kClassDoc_LookingHaveSomething || fClassDocState == kClassDoc_LookingNothingFound) {
1969     TString& classDoc = first || !fLastClassDoc.Length() ? fFirstClassDoc : fLastClassDoc;
1970     dynamic_cast<TClassDocOutput*>(fDocOutput)->WriteClassDescription(out, classDoc);
1971     fClassDocState = kClassDoc_Written;
1972     }
1973    
1974     }
1975    
1976     //______________________________________________________________________________
1977     void TDocParser::WriteMethod(std::ostream& out, TString& ret,
1978     TString& name, TString& params,
1979     const char* filename, TString& anchor,
1980     TString& codeOneLiner)
1981     {
1982     // Write a method, forwarding to TClassDocOutput
1983    
1984     // if we haven't found the class description until now it's too late.
1985     if (fClassDocState < kClassDoc_Written)
1986     WriteClassDoc(out);
1987    
1988     TMethod* guessedMethod = 0;
1989     int nparams = params.CountChar(',');
1990     TString strippedParams(params);
1991     if (strippedParams[0] == '(') {
1992     strippedParams.Remove(0, 1);
1993     strippedParams.Remove(strippedParams.Length() - 1);
1994     }
1995     if (strippedParams.Strip(TString::kBoth).Length())
1996     ++nparams;
1997    
1998     TMethod* method = 0;
1999     TIter nextMethod(fCurrentClass->GetListOfMethods());
2000     while ((method = (TMethod *) nextMethod()))
2001     if (name == method->GetName()
2002     && method->GetListOfMethodArgs()->GetSize() == nparams) {
2003     if (guessedMethod) {
2004     // not unique, don't try to solve overload
2005     guessedMethod = 0;
2006     break;
2007     } else
2008     guessedMethod = method;
2009     }
2010    
2011     dynamic_cast<TClassDocOutput*>(fDocOutput)->WriteMethod(out, ret, name, params, filename, anchor,
2012     fComment, codeOneLiner, guessedMethod);
2013    
2014     DecrementMethodCount(name);
2015     ret.Remove(0);
2016     name.Remove(0);
2017     params.Remove(0);
2018     anchor.Remove(0);
2019     fComment.Remove(0);
2020    
2021     fDocContext = kIgnore;
2022     }
2023    
2024     //______________________________________________________________________________
2025     void TDocParser::WriteSourceLine(std::ostream& out)
2026     {
2027     // Write fLineSource to out.
2028     // Adjust relative paths first.
2029    
2030     fDocOutput->AdjustSourcePath(fLineSource);
2031     out << fLineSource << std::endl;
2032     }