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

# Content
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 }