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