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