ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/MitAna/Utils/src/THtml.cxx
Revision: 1.2
Committed: Fri Mar 11 04:03:54 2011 UTC (14 years, 1 month ago) by bendavid
Content type: text/plain
Branch: MAIN
CVS Tags: Mit_032, Mit_031, Mit_025c_branch2, Mit_025c_branch1, Mit_030, Mit_029c, Mit_029b, Mit_030_pre1, Mit_029a, Mit_029, Mit_029_pre1, Mit_028a, Mit_025c_branch0, Mit_028, Mit_027a, Mit_027, Mit_026, Mit_025e, Mit_025d, Mit_025c, Mit_025b, Mit_025a, Mit_025, Mit_025pre2, Mit_024b, Mit_025pre1, Mit_024a, Mit_024, Mit_023, Mit_022a, Mit_022, Mit_020d, TMit_020d, Mit_020c, Mit_021, Mit_021pre2, Mit_021pre1, Mit_020b, Mit_020a, Mit_020, Mit_020pre1, HEAD
Branch point for: Mit_025c_branch
Changes since 1.1: +2 -2 lines
Log Message:
various minor changes to acommodate new root and architecture

File Contents

# Content
1 // @(#)root/html:$Id: THtml.cxx,v 1.1 2009/08/11 23:09:28 loizides Exp $
2 // Author: Nenad Buncic (18/10/95), Axel Naumann (09/28/01)
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/THtml.h"
13 #include "RConfigure.h"
14 #include "Riostream.h"
15 #include "TBaseClass.h"
16 #include "TClass.h"
17 #include "MitAna/Utils/interface/TClassDocOutput.h"
18 #include "TClassEdit.h"
19 #include "TClassTable.h"
20 #include "TDataType.h"
21 #include "MitAna/Utils/interface/TDocInfo.h"
22 #include "MitAna/Utils/interface/TDocOutput.h"
23 #include "TEnv.h"
24 #include "TInterpreter.h"
25 #include "TObjString.h"
26 #include "TPRegexp.h"
27 #include "TRegexp.h"
28 #include "TROOT.h"
29 #include "TSystem.h"
30 #include "TThread.h"
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <set>
36 #include <fstream>
37
38 //THtml *gHtml = 0;
39
40 //______________________________________________________________________________
41 //______________________________________________________________________________
42 namespace {
43 class THtmlThreadInfo {
44 public:
45 THtmlThreadInfo(THtml* html, bool force): fHtml(html), fForce(force) {}
46 Bool_t GetForce() const {return fForce;}
47 THtml* GetHtml() const {return fHtml;}
48
49 private:
50 THtml* fHtml;
51 Bool_t fForce;
52 };
53 }
54
55
56 //______________________________________________________________________________
57 THtml::THelperBase::~THelperBase()
58 {
59 // Helper's destructor.
60 // Check that no THtml object is attached to the helper - it might still need it!
61 if (fHtml) {
62 fHtml->HelperDeleted(this);
63 }
64 }
65
66
67 //______________________________________________________________________________
68 void THtml::THelperBase::SetOwner(THtml* html) {
69 // Set the THtml object owning this object; if it's already set to
70 // a different THtml object than issue an error message and signal to
71 // the currently set object that we are not belonging to it anymore.
72 if (fHtml && html && html != fHtml) {
73 Error("SetOwner()", "Object already owned by an THtml instance!");
74 fHtml->HelperDeleted(this);
75 }
76 fHtml = html;
77 }
78
79
80 //______________________________________________________________________________
81 bool THtml::TModuleDefinition::GetModule(TClass* cl, TString& out_modulename) const
82 {
83 // Set out_modulename to cl's module name; return true if it's valid.
84 // If applicable, the module contains super modules separated by "/".
85 //
86 // ROOT takes the directory part of cl's implementation file name
87 // (or declaration file name, if the implementation file name is empty),
88 // removes the last subdirectory if it is "src/" or "inc/", and interprets
89 // the remaining path as the module hierarchy, converting it to upper case.
90 // hist/histpainter/src/THistPainter.cxx thus becomes the module
91 // HIST/HISTPAINTER. (Node: some ROOT packages get special treatment.)
92 // If the file cannot be mapped into this scheme, the class's library
93 // name (without directories, leading "lib" prefix or file extensions)
94 // ius taken as the module name. If the module cannot be determined it is
95 // set to "USER" and false is returned.
96 //
97 // If your software cannot be mapped into this scheme then derive your
98 // own class from TModuleDefinition and pass it to THtml::SetModuleDefinition().
99
100 out_modulename = "USER";
101 if (!cl) return false;
102
103 // Filename: impl or decl?
104 TString filename;
105 if (!GetOwner()->GetImplFileName(cl, kFALSE, filename))
106 if (!GetOwner()->GetDeclFileName(cl, kFALSE, filename))
107 return false;
108
109 // take the directory name without "/" or leading "."
110 out_modulename = gSystem->DirName(filename);
111
112 TString inputdir = GetOwner()->GetInputPath();
113 TString tok;
114 Ssiz_t start = 0;
115 while (inputdir.Tokenize(tok, start, THtml::GetDirDelimiter())) {
116 if (out_modulename.BeginsWith(tok)) {
117 out_modulename.Remove(0, tok.Length());
118 break;
119 }
120 }
121 while (out_modulename[0] == '.')
122 out_modulename.Remove(0, 1);
123 out_modulename.ReplaceAll("\\", "/");
124 while (out_modulename[0] == '/')
125 out_modulename.Remove(0, 1);
126 while (out_modulename.EndsWith("/"))
127 out_modulename.Remove(out_modulename.Length() - 1);
128
129 // remove "/src", "/inc"
130 if (out_modulename.EndsWith("/src")
131 || out_modulename.EndsWith("/inc"))
132 out_modulename.Remove(out_modulename.Length() - 4, 4);
133 else {
134 // remove "/src/whatever", "/inc/whatever"
135 Ssiz_t pos = out_modulename.Index("/src/");
136 if (pos == kNPOS)
137 pos = out_modulename.Index("/inc/");
138 if (pos != kNPOS)
139 out_modulename.Remove(pos);
140 }
141
142 while (out_modulename.EndsWith("/"))
143 out_modulename.Remove(out_modulename.Length() - 1);
144
145 // special treatment:
146 if (out_modulename == "MATH/GENVECTOR")
147 out_modulename = "MATHCORE";
148 else if (out_modulename == "MATH/MATRIX")
149 out_modulename = "SMATRIX";
150 else if (!out_modulename.Length()) {
151 const char* cname= cl->GetName();
152 if (strstr(cname, "::SMatrix<") || strstr(cname, "::SVector<"))
153 out_modulename = "SMATRIX";
154 else if (strstr(cname, "::TArrayProxy<") || strstr(cname, "::TClaArrayProxy<")
155 || strstr(cname, "::TImpProxy<") || strstr(cname, "::TClaImpProxy<"))
156 out_modulename = "TREEPLAYER";
157 else {
158 // determine the module name from the library name:
159 out_modulename = cl->GetSharedLibs();
160 Ssiz_t pos = out_modulename.Index(' ');
161 if (pos != kNPOS)
162 out_modulename.Remove(pos, out_modulename.Length());
163 if (out_modulename.BeginsWith("lib"))
164 out_modulename.Remove(0,3);
165 pos = out_modulename.Index('.');
166 if (pos != kNPOS)
167 out_modulename.Remove(pos, out_modulename.Length());
168
169 if (!out_modulename.Length()) {
170 out_modulename = "USER";
171 return false;
172 }
173 }
174 }
175
176 return true;
177 }
178
179 //______________________________________________________________________________
180 void THtml::TFileDefinition::ExpandSearchPath(TString& path) const
181 {
182 // Create all permutations of path and THtml's input path:
183 // path being PP/ and THtml's input being .:include/:src/ gives
184 // .:./PP/:include:include/PP/:src/:src/PP
185 THtml* owner = GetOwner();
186 if (!owner) return;
187
188 TString pathext;
189 TString inputdir = owner->GetInputPath();
190 TString tok;
191 Ssiz_t start = 0;
192 while (inputdir.Tokenize(tok, start, THtml::GetDirDelimiter())) {
193 if (pathext.Length())
194 pathext += GetDirDelimiter();
195 if (tok.EndsWith("\\"))
196 tok.Remove(tok.Length() - 1);
197 pathext += tok;
198 if (path.BeginsWith(tok))
199 pathext += GetDirDelimiter() + path;
200 else
201 pathext += GetDirDelimiter() + tok + "/" + path;
202 }
203 path = pathext;
204
205 }
206
207 //______________________________________________________________________________
208 void THtml::TFileDefinition::SplitClassIntoDirFile(const TString& clname, TString& dir, TString& filename) const
209 {
210 // Given a class name with a scope, split the class name into directory part
211 // and file name: A::B::C becomes module B, filename C.
212 TString token;
213 Ssiz_t from = 0;
214 filename = "";
215 dir = "";
216 while (clname.Tokenize(token, from, "::") ) {
217 dir = filename;
218 filename = token;
219 }
220
221 // convert from Scope, class to module, filename.h
222 dir.ToLower();
223 }
224
225
226 //______________________________________________________________________________
227 bool THtml::TFileDefinition::GetDeclFileName(const TClass* cl, TString& out_filename, TString& out_fsys) const
228 {
229 // Determine cl's declaration file name. Usually it's just
230 // cl->GetDeclFileName(), but sometimes conversions need to be done
231 // like include/ to abc/cde/inc/. If no declaration file name is
232 // available, look for b/inc/C.h for class A::B::C. out_fsys will contain
233 // the file system's (i.e. local machine's) full path name to the file.
234 // The function returns false if the class's header file cannot be found.
235 //
236 // If your software cannot be mapped into this scheme then derive your
237 // own class from TFileDefinition and pass it to THtml::SetFileDefinition().
238
239 return GetFileName(cl, true, out_filename, out_fsys);
240 }
241
242 //______________________________________________________________________________
243 bool THtml::TFileDefinition::GetImplFileName(const TClass* cl, TString& out_filename, TString& out_fsys) const
244 {
245 // Determine cl's implementation file name. Usually it's just
246 // cl->GetImplFileName(), but sometimes conversions need to be done.
247 // If no implementation file name is available look for b/src/C.cxx for
248 // class A::B::C. out_fsys will contain the file system's (i.e. local
249 // machine's) full path name to the file.
250 // The function returns false if the class's source file cannot be found.
251 //
252 // If your software cannot be mapped into this scheme then derive your
253 // own class from TFileDefinition and pass it to THtml::SetFileDefinition().
254
255 return GetFileName(cl, false, out_filename, out_fsys);
256 }
257
258
259 //______________________________________________________________________________
260 bool THtml::TFileDefinition::GetFileName(const TClass* cl, bool decl, TString& out_filename, TString& out_fsys) const
261 {
262 // Common implementation for GetDeclFileName(), GetImplFileName()
263
264 out_fsys = "";
265
266 if (!cl) {
267 out_filename = "";
268 return false;
269 }
270
271 TString possibleFileName;
272 TString possiblePath;
273 TString filesysname;
274
275 TString clfile = decl ? cl->GetDeclFileName() : cl->GetImplFileName();
276 out_filename = clfile;
277 if (clfile.Length()) {
278 // check that clfile doesn't start with one of the include paths;
279 // that's not what we want (include/TObject.h), we want the actual file
280 // if it exists (core/base/inc/TObject.h)
281
282 // special case for TMath namespace:
283 if (clfile == "include/TMathBase.h") {
284 clfile = "math/mathcore/inc/TMath.h";
285 out_filename = clfile;
286 }
287
288 TString inclDir;
289 TString inclPath(GetOwner()->GetPathInfo().fIncludePath);
290 Ssiz_t pos = 0;
291 Ssiz_t longestMatch = kNPOS;
292 while (inclPath.Tokenize(inclDir, pos, GetOwner()->GetDirDelimiter())) {
293 if (clfile.BeginsWith(inclDir) && (longestMatch == kNPOS || inclDir.Length() > longestMatch))
294 longestMatch = inclDir.Length();
295 }
296 if (longestMatch != kNPOS) {
297 clfile.Remove(0, longestMatch);
298 if (clfile.BeginsWith("/") || clfile.BeginsWith("\\"))
299 clfile.Remove(0, 1);
300 TString asincl(clfile);
301 GetOwner()->GetPathDefinition().GetFileNameFromInclude(asincl, clfile);
302 out_filename = clfile;
303 }
304 } else {
305 // check for a file named like the class:
306 filesysname = cl->GetName();
307 int templateLevel = 0;
308 Ssiz_t end = filesysname.Length();
309 Ssiz_t start = end - 1;
310 for (; start >= 0 && (templateLevel || filesysname[start] != ':'); --start) {
311 if (filesysname[start] == '>')
312 ++templateLevel;
313 else if (filesysname[start] == '<') {
314 --templateLevel;
315 if (!templateLevel)
316 end = start;
317 }
318 }
319 filesysname = filesysname(start + 1, end - start - 1);
320 if (decl)
321 filesysname += ".h";
322 else
323 filesysname += ".cxx";
324 TFileSysEntry* fsentry = (TFileSysEntry*) GetOwner()->GetLocalFiles()->GetEntries().FindObject(filesysname);
325 if (fsentry) {
326 fsentry->GetFullName(filesysname, kFALSE);
327 clfile = filesysname;
328 out_filename = filesysname;
329 }
330 }
331
332 if (!decl && !clfile.Length()) {
333 // determine possible impl file name from the decl file name,
334 // replacing ".whatever" by ".cxx", and looking for it in the known
335 // file names
336 TString declSysFileName;
337 if (GetFileName(cl, true, filesysname, declSysFileName)) {
338 filesysname = gSystem->BaseName(filesysname);
339 Ssiz_t posExt = filesysname.Last('.');
340 if (posExt != kNPOS)
341 filesysname.Remove(posExt);
342 filesysname += ".cxx";
343 TFileSysEntry* fsentry = (TFileSysEntry*) GetOwner()->GetLocalFiles()->GetEntries().FindObject(filesysname);
344 if (fsentry) {
345 fsentry->GetFullName(filesysname, kFALSE);
346 clfile = filesysname;
347 out_filename = filesysname;
348 }
349 }
350 }
351
352 if (clfile.Length() && !decl) {
353 // Do not return the source file for these packages, even though we can find them.
354 // THtml needs to have the class description in the source file if it finds the
355 // source file, and these classes have their class descriptions in the header files.
356 // THtml needs to be improved to collect all of a class' documentation before writing
357 // it out, so it can take the class doc from the header even though a source exists.
358 static const char* vetoClasses[] = {"math/mathcore/", "math/mathmore/", "math/genvector/",
359 "math/minuit2/", "math/smatrix/"};
360 for (unsigned int i = 0; i < sizeof(vetoClasses) / sizeof(char*); ++i) {
361 if (clfile.Contains(vetoClasses[i])) {
362 // of course there are exceptions from the exceptions:
363 // TComplex and TRandom, TRandom1,...
364 if (strcmp(cl->GetName(), "TComplex")
365 && strcmp(cl->GetName(), "TMath")
366 && strncmp(cl->GetName(), "TRandom", 7)) {
367 out_filename = "";
368 return false;
369 } else break;
370 }
371 }
372 }
373
374
375 if (!clfile.Length()) {
376 // determine possible decl file name from class + scope name:
377 // A::B::C::myclass will result in possible file name myclass.h
378 // in directory C/inc/
379 out_filename = cl->GetName();
380 if (!out_filename.Contains("::")) {
381 out_filename = "";
382 return false;
383 }
384 SplitClassIntoDirFile(out_filename, possiblePath, possibleFileName);
385
386 // convert from Scope, class to module, filename.h
387 if (possibleFileName.Length()) {
388 if (decl)
389 possibleFileName += ".h";
390 else
391 possibleFileName += ".cxx";
392 }
393 if (possiblePath.Length())
394 possiblePath += "/";
395 if (decl)
396 possiblePath += "inc/";
397 else
398 possiblePath += "src/";
399 out_filename = possiblePath + "/" + possibleFileName;
400 } else {
401 possiblePath = gSystem->DirName(clfile);
402 possibleFileName = gSystem->BaseName(clfile);
403 }
404
405 if (possiblePath.Length())
406 ExpandSearchPath(possiblePath);
407 else possiblePath=".";
408
409 out_fsys = gSystem->FindFile(possiblePath, possibleFileName, kReadPermission);
410 if (out_fsys.Length()) return true;
411 out_filename = "";
412 return false;
413 }
414
415 //______________________________________________________________________________
416 bool THtml::TPathDefinition::GetMacroPath(const TString& module, TString& out_dir) const
417 {
418 // Determine the path to look for macros (see TDocMacroDirective) for
419 // classes from a given module. If the path was sucessfully determined return true.
420 // For ROOT, this directory is the "doc/macros" subdirectory of the module
421 // directory; the path returned is GetDocDir(module) + "/macros".
422 //
423 // If your software cannot be mapped into this scheme then derive your
424 // own class from TPathDefinition and pass it to THtml::SetPathDefinition().
425
426 TString moduledoc;
427 if (!GetDocDir(module, moduledoc))
428 return false;
429 if (moduledoc.EndsWith("\\"))
430 moduledoc.Remove(moduledoc.Length() - 1);
431
432 TString macropath(GetOwner()->GetMacroPath());
433 TString macrodirpart;
434 out_dir = "";
435 Ssiz_t pos = 0;
436 while (macropath.Tokenize(macrodirpart, pos, ":")) {
437 out_dir += moduledoc + "/" + macrodirpart + ":";
438 }
439 return true;
440 }
441
442
443 //______________________________________________________________________________
444 bool THtml::TPathDefinition::GetDocDir(const TString& module, TString& doc_dir) const
445 {
446 // Determine the module's documentation directory. If module is empty,
447 // set doc_dir to the product's documentation directory.
448 // If the path was sucessfuly determined return true.
449 // For ROOT, this directory is the subdir "doc/" in the
450 // module's path; the directory returned is module + "/doc".
451 //
452 // If your software cannot be mapped into this scheme then derive your
453 // own class from TPathDefinition and pass it to THtml::SetPathDefinition().
454
455 if (module.Length())
456 doc_dir = module + "/";
457 doc_dir += GetOwner()->GetPathInfo().fDocPath;
458 return true;
459 }
460
461
462 //______________________________________________________________________________
463 bool THtml::TPathDefinition::GetIncludeAs(TClass* cl, TString& out_dir) const
464 {
465 // Determine the path and filename used in an include statement for the
466 // header file of the given class. E.g. the class ROOT::Math::Boost is
467 // meant to be included as "Math/Genvector/Boost.h" - which is what
468 // out_dir is set to. GetIncludeAs() returns whether the include
469 // statement's path was successfully determined.
470 //
471 // Any leading directory part that is part of fIncludePath (see SetIncludePath)
472 // will be removed. For ROOT, leading "include/" is removed; everything after
473 // is the include path. Only classes from TMVA are different; they are included
474 // as TMVA/ClassName.h.
475 //
476 // If your software cannot be mapped into this scheme then derive your
477 // own class from TPathDefinition and pass it to THtml::SetPathDefinition().
478
479 out_dir = "";
480 if (!cl || !GetOwner()) return false;
481
482 const char* clname = cl->GetName();
483 TString hdr;
484 if (!GetOwner()->GetDeclFileName(cl, kFALSE, hdr))
485 return false;
486
487 out_dir = hdr;
488 bool includePathMatches = false;
489 TString tok;
490 Ssiz_t pos = 0;
491 while (!includePathMatches && GetOwner()->GetPathInfo().fIncludePath.Tokenize(tok, pos, THtml::GetDirDelimiter()))
492 if (out_dir.BeginsWith(tok)) {
493 out_dir = hdr(tok.Length(), hdr.Length());
494 includePathMatches = true;
495 }
496
497 if (!includePathMatches) {
498 // We probably have a file super/module/inc/optional/filename.h.
499 // That gets translated into optional/filename.h.
500 // Assume that only one occurrence of "/inc/" exists in hdr.
501 // If /inc/ is not part of the include file name then
502 // just return the full path.
503 // If we have matched any include path then this ROOT-only
504 // algorithm is skipped!
505 Ssiz_t posInc = hdr.Index("/inc/");
506 if (posInc == kNPOS) return true;
507 hdr.Remove(0, posInc + 5);
508 out_dir = hdr;
509
510 // TMVA and RooStats special treatment:
511 // TMVA::Whatever claims to be in in math/tmva/inc/Whatever.h
512 // but it needs to get included as TMVA/Whatever.h
513 if (strstr(clname, "TMVA::"))
514 out_dir.Prepend("TMVA/");
515 if (strstr(clname, "RooStats::"))
516 out_dir.Prepend("RooStats/");
517 }
518
519 return (out_dir.Length());
520 }
521
522
523 //______________________________________________________________________________
524 bool THtml::TPathDefinition::GetFileNameFromInclude(const char* included, TString& out_fsname) const
525 {
526 // Set out_fsname to the full pathname corresponding to a file
527 // included as "included". Return false if this file cannot be determined
528 // or found. For ROOT, out_fsname corresponds to included prepended with
529 // "include"; only THtml prefers to work on the original files, e.g.
530 // core/base/inc/TObject.h instead of include/TObject.h, so the
531 // default implementation searches the TFileSysDB for an entry with
532 // basename(included) and with matching directory part, setting out_fsname
533 // to the TFileSysEntry's path.
534
535 if (!included) return false;
536
537 out_fsname = included;
538
539 if (!strncmp(included, "TMVA/", 5)) {
540 out_fsname.Remove(0, 4);
541 out_fsname.Prepend("tmva/inc");
542 return true;
543 }
544 // special treatment for roostats (same as in TMVA)
545 if (!strncmp(included, "RooStats/", 9)) {
546 out_fsname.Remove(0, 8);
547 out_fsname.Prepend("roofit/roostats/inc");
548 return true;
549 }
550
551 TString incBase(gSystem->BaseName(included));
552 TList* bucket = GetOwner()->GetLocalFiles()->GetEntries().GetListForObject(incBase);
553 if (!bucket) return false;
554
555 TString alldir(gSystem->DirName(included));
556 TObjArray* arrSubDirs = alldir.Tokenize("/");
557 TIter iEntry(bucket);
558 TFileSysEntry* entry = 0;
559 while ((entry = (TFileSysEntry*) iEntry())) {
560 if (incBase != entry->GetName()) continue;
561 // find entry with matching enclosing directory
562 THtml::TFileSysDir* parent = entry->GetParent();
563 for (int i = arrSubDirs->GetEntries() - 1; parent && i >= 0; --i) {
564 const TString& subdir(((TObjString*)(*arrSubDirs)[i])->String());
565 if (!subdir.Length() || subdir == ".")
566 continue;
567 if (subdir == parent->GetName())
568 parent = parent->GetParent();
569 else parent = 0;
570 }
571 if (parent) {
572 // entry found!
573 entry->GetFullName(out_fsname, kFALSE);
574 delete arrSubDirs;
575 return true;
576 }
577 }
578 delete arrSubDirs;
579 return false;
580 }
581
582 //______________________________________________________________________________
583 void THtml::TFileSysDir::Recurse(TFileSysDB* db, const char* path)
584 {
585 // Recursively fill entries by parsing the contents of path.
586
587 TString dir(path);
588 if (gDebug > 0 || GetLevel() < 2)
589 Info("Recurse", "scanning %s...", path);
590 TPMERegexp regexp(db->GetIgnore());
591 dir += "/";
592 void* hDir = gSystem->OpenDirectory(dir);
593 const char* direntry = 0;
594 while ((direntry = gSystem->GetDirEntry(hDir))) {
595 if (!direntry[0] || direntry[0] == '.' || regexp.Match(direntry)) continue;
596 TString entryPath(dir + direntry);
597 if (gSystem->AccessPathName(entryPath, kReadPermission))
598 continue;
599 FileStat_t buf;
600 gSystem->GetPathInfo(entryPath, buf);
601 if (R_ISDIR(buf.fMode)) {
602 // skip if we would nest too deeply, and skip soft links:
603 if (GetLevel() > db->GetMaxLevel()
604 #ifndef R__WIN32
605 || db->GetMapIno().GetValue(buf.fIno)
606 #endif
607 ) continue;
608 TFileSysDir* subdir = new TFileSysDir(direntry, this);
609 fDirs.Add(subdir);
610 #ifndef R__WIN32
611 db->GetMapIno().Add(buf.fIno, (Long_t)subdir);
612 #endif
613 subdir->Recurse(db, entryPath);
614 } else {
615 int delen = strlen(direntry);
616 // only .cxx and .h are taken
617 if (strcmp(direntry + delen - 4, ".cxx")
618 && strcmp(direntry + delen - 2, ".h"))
619 continue;
620 TFileSysEntry* entry = new TFileSysEntry(direntry, this);
621 db->GetEntries().Add(entry);
622 fFiles.Add(entry);
623 }
624 } // while dir entry
625 gSystem->FreeDirectory(hDir);
626 }
627
628
629 //______________________________________________________________________________
630 void THtml::TFileSysDB::Fill()
631 {
632 // Recursively fill entries by parsing the path specified in GetName();
633 // can be a THtml::GetDirDelimiter() delimited list of paths.
634
635 TString dir;
636 Ssiz_t posPath = 0;
637 while (fName.Tokenize(dir, posPath, THtml::GetDirDelimiter())) {
638 if (gSystem->AccessPathName(dir, kReadPermission)) {
639 Warning("Fill", "Cannot read InputPath \"%s\"!", dir.Data());
640 continue;
641 }
642 FileStat_t buf;
643 gSystem->GetPathInfo(dir, buf);
644 if (R_ISDIR(buf.fMode)) {
645 #ifndef R__WIN32
646 TFileSysRoot* prevroot = (TFileSysRoot*) GetMapIno().GetValue(buf.fIno);
647 if (prevroot != 0) {
648 Warning("Fill", "InputPath \"%s\" already present as \"%s\"!", dir.Data(), prevroot->GetName());
649 continue;
650 }
651 #endif
652 TFileSysRoot* root = new TFileSysRoot(dir, this);
653 fDirs.Add(root);
654 #ifndef R__WIN32
655 GetMapIno().Add(buf.fIno, (Long_t)root);
656 #endif
657 root->Recurse(this, dir);
658 } else {
659 Warning("Fill", "Cannot read InputPath \"%s\"!", dir.Data());
660 }
661 }
662 }
663
664
665 ////////////////////////////////////////////////////////////////////////////////
666 /* BEGIN_HTML
667 <p>The THtml class is designed to easily document
668 classes, code, and code related text files (like change logs). It generates HTML
669 pages conforming to the XHTML 1.0 transitional specifications; an example of
670 these pages is ROOT's own <a href="http://root.cern.ch/root/html/ClassIndex.html">
671 reference guide</a>. This page was verified to be valid XHTML 1.0 transitional,
672 which proves that all pages generated by THtml can be valid, as long as the user
673 provided XHTML (documentation, header, etc) is valid. You can check the current
674 THtml by clicking this icon:
675 <a href="http://validator.w3.org/check?uri=referer"><img
676 src="http://www.w3.org/Icons/valid-xhtml10"
677 alt="Valid XHTML 1.0 Transitional" height="31" width="88" style="border: none;"/></a></p>
678 Overview:
679 <ol style="list-style-type: upper-roman;">
680 <li><a href="#usage">Usage</a></li>
681 <li><a href="#conf">Configuration</a>
682 <ol><li><a href="#conf:input">Input files</a></li>
683 <li><a href="#conf:output">Output directory</a></li>
684 <li><a href="#conf:liblink">Linking other documentation</a></li>
685 <li><a href="#conf:classdoc">Recognizing class documentation</a></li>
686 <li><a href="#conf:tags">Author, copyright, etc.</a></li>
687 <li><a href="#conf:header">Header and footer</a></li>
688 <li><a href="#conf:search">Links to searches, home page, ViewVC</a></li>
689 <li><a href="#conf:charset">HTML Charset</a></li>
690 </ol></li>
691 <li><a href="#syntax">Documentation syntax</a>
692 <ol><li><a href="#syntax:classdesc">Class description</a></li>
693 <li><a href="#syntax:classidx">Class index</a></li>
694 <li><a href="#syntax:meth">Method documentation</a></li>
695 <li><a href="#syntax:datamem">Data member documentation</a></li>
696 </ol></li>
697 <li><a href="#directive">Documentation directives</a>
698 <ol><li><a href="#directive:html"><tt>BEGIN<!-- -->_HTML</tt> <tt>END<!-- -->_HTML</tt>: include 'raw' HTML</a></li>
699 <li><a href="#directive:macro"><tt>BEGIN<!-- -->_MACRO</tt> <tt>END<!-- -->_MACRO</tt>: include a picture generated by a macro</a></li>
700 <li><a href="#directive:latex"><tt>BEGIN<!-- -->_LATEX</tt> <tt>END<!-- -->_LATEX</tt>: include a latex picture</a></li>
701 </ol></li>
702 <li><a href="#index">Product and module index</a></li>
703 <li><a href="#aux">Auxiliary files: style sheet, JavaScript, help page</a></li>
704 <li><a href="#charts">Class Charts</a></li>
705 <li><a href="#confvar">Configuration variables</a></li>
706 <li><a href="#how">Behind the scenes</a></li>
707 </ol>
708
709
710 <h3><a name="usage">I. Usage</a></h3>
711 These are typical things people do with THtml:
712 <pre>
713 root[] <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> html; // create a <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> object
714 root[] html.MakeAll(); // generate documentation for all changed classes
715 </pre>
716 or to run on just a few classes:
717 <pre>
718 root[] <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> html; // create a <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> object
719 root[] html.MakeIndex(); // create auxilliary files (style sheet etc) and indices
720 root[] html.MakeClass("TMyClass"); // create documentation for TMyClass only
721 </pre>
722 To "beautify" (i.e. create links to documentation for class names etc) some text
723 file or macro, use:
724 <pre>
725 root[] html.Convert( "hsimple.C", "Histogram example" )
726 </pre>
727
728
729 <h3><a name="conf">II. Configuration</a></h3>
730 Most configuration options can be set as a call to THtml, or as a TEnv variable,
731 which you can set in your .rootrc.
732
733 <h4><a name="conf:input">II.1 Input files</a></h4>
734
735 <p>In your .rootrc, define Root.Html.SourceDir to point to directories containing
736 .cxx and .h files (see: <a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>)
737 of the classes you want to document, or call THtml::SetInputDir()</p>
738
739 <p>Example:</p><pre>
740 Root.Html.SourceDir: .:src:include
741 Root.Html.Root: http://root.cern.ch/root/html</pre>
742
743
744 <h4><a name="conf:output">II.2 Output directory</a></h4>
745
746 <p>The output directory can be specified using the Root.Html.OutputDir
747 configuration variable (default value: "htmldoc"). If that directory
748 doesn't exist <a href="http://root.cern.ch/root/html/THtml.html">THtml</a>
749 will create it.</p>
750
751 <p>Example:</p><pre>
752 Root.Html.OutputDir: htmldoc</pre>
753
754 <h4><a name="conf:liblink">II.3 Linking other documentation</a></h4>
755
756 <p>When trying to document a class, THtml searches for a source file in
757 the directories set via SetInputDir(). If it cannot find it, it assumes
758 that this class must have been documented before. Based on the library
759 this class is defined in, it checks the configuration variable
760 <tt>Root.Html.LibName</tt>, and creates a link using its value.
761 Alternatively, you can set these URLs via THtml::SetLibURL().</p>
762
763 <p>Example:<br/>
764 If a class MyClass is defined in class mylibs/libMyLib.so, and .rootrc
765 contains</p><pre>
766 Root.Html.MyLib: ../mylib/</pre>
767 <p>THtml will create a link to "../mylib/MyClass.html".</p>
768
769 <p>The library name association can be set up using the rootmap facility.
770 For the library in the example above, which contains a dictionary
771 generated from the linkdef MyLinkdef.h, the command to generate the
772 rootmap file is</p>
773 <pre> $ rlibmap -f -r rootmap -l mylib/libMyLib.so -d libCore.so -c MyLinkdef.h</pre>
774 <p>Here, <tt>-r</tt> specifies that the entries for libMyLib should be updated,
775 <tt>-l</tt> specifies the library we're dealing with, <tt>-d</tt> its
776 dependencies, and <tt>-c</tt> its linkdef. The rootmap file must be within
777 one of the <tt>LD_LIBRARY_PATH</tt> (or <tt>PATH</tt> for Windows) directories
778 when ROOT is started, otherwise ROOT will not use it.</p>
779
780 <h4><a name="conf:classdoc">II.4 Recognizing class documentation</a></h4>
781
782 <p>The class documentation has to appear in the header file containing the
783 class, right in front of its declaration. It is introduced by a string
784 defined by Root.Html.Description or SetClassDocTag(). See the section on
785 <a href="#syntax">documentation syntax</a> for further details.</p>
786
787 <p>Example:</p><pre>
788 Root.Html.Description: //____________________</pre>
789
790 <p>The class documentation will show which include statement is to be used
791 and which library needs to be linked to access it.
792 The include file name is determined via
793 <a href="http://root.cern.ch/root/html/TClass.html#TClass:GetDeclFileName">
794 TClass::GetDeclFileName()</a>;
795 leading parts are removed if they match any of the ':' separated entries in
796 THtml::GetIncludePath().</p>
797
798 <h4><a name="conf:tags">II.5 Author, copyright, etc.</a></h4>
799
800 <p>During the conversion,
801 <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will look for
802 some strings ("tags") in the source file, which have to appear right in
803 front of e.g. the author's name, copyright notice, etc. These tags can be
804 defined with the following environment variables: Root.Html.Author,
805 Root.Html.LastUpdate and Root.Html.Copyright, or with
806 SetAuthorTag(), SetLastUpdateTag(), SetCopyrightTag().</p>
807
808 <p>If the LastUpdate tag is not found, the current date and time are used.
809 This is useful when using
810 <a href="http://root.cern.ch/root/html/THtml.html#THtml:MakeAll">THtml::MakeAll()</a>'s
811 default option force=kFALSE, in which case
812 <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates
813 documentation only for changed classes.</p>
814
815 Authors can be a comma separated list of author entries. Each entry has
816 one of the following two formats
817 <ul><li><tt>Name (non-alpha)</tt>.
818 <p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate an
819 HTML link for <tt>Name</tt>, taking the Root.Html.XWho configuration
820 variable (defaults to "http://consult.cern.ch/xwho/people?") and adding
821 all parts of the name with spaces replaces by '+'. Non-alphanumerical
822 characters are printed out behind <tt>Name</tt>.</p>
823
824 <p>Example:</p>
825 <tt>// Author: Enrico Fermi</tt> appears in the source file.
826 <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate the link
827 <tt>http://consult.cern.ch/xwho/people?Enrico+Fermi</tt>. This works well for
828 people at CERN.</li>
829
830 <li><tt>Name &lt;link&gt; Info</tt>.
831 <p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will generate
832 an HTML link for <tt>Name</tt> as specified by <tt>link</tt> and print
833 <tt>Info</tt> behind <tt>Name</tt>.</p>
834
835 <p>Example:</p>
836 <tt>// Author: Enrico Fermi &lt;http://www.enricos-home.it&gt;</tt> or<br/>
837 <tt>// Author: Enrico Fermi &lt;mailto:enrico@fnal.gov&gt;</tt> in the
838 source file. That's world compatible.</li>
839 </ul>
840
841 <p>Example (with defaults given):</p><pre>
842 Root.Html.Author: // Author:
843 Root.Html.LastUpdate: // @(#)
844 Root.Html.Copyright: * Copyright
845 Root.Html.XWho: http://consult.cern.ch/xwho/people?</pre>
846
847
848 <h4><a name="conf:header">II.6 Header and footer</a></h4>
849
850 <p><a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates
851 a default header and footer for all pages. You can
852 specify your own versions with the configuration variables Root.Html.Header
853 and Root.Html.Footer, or by calling SetHeader(), SetFooter().
854 Both variables default to "", using the standard Root
855 versions. If it has a "+" appended, <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will
856 write both versions (user and root) to a file, for the header in the order
857 1st root, 2nd user, and for the footer 1st user, 2nd root (the root
858 versions containing "&lt;html&gt;" and &lt;/html&gt; tags, resp).</p>
859
860 <p>If you want to replace root's header you have to write a file containing
861 all HTML elements necessary starting with the &lt;doctype&gt; tag and ending with
862 (and including) the &lt;body&gt; tag. If you add your header it will be added
863 directly after Root's &lt;body&gt; tag. Any occurrence of the string <tt>%TITLE%</tt>
864 in the user's header file will be replaced by
865 a sensible, automatically generated title. If the header is generated for a
866 class, occurrences of <tt>%CLASS%</tt> will be replaced by the current class's name,
867 <tt>%SRCFILE%</tt> and <tt>%INCFILE%</tt> by the name of the source and header file, resp.
868 (as given by <a href="http://root.cern.ch/root/html/TClass.html#TClass:GetImplFileLine">TClass::GetImplFileName()</a>,
869 <a href="http://root.cern.ch/root/html/TClass.html#TClass:GetImplFileLine">TClass::GetDeclFileName()</a>).
870 If the header is not generated for a class, they will be replaced by "".</p>
871
872 <p>Root's footer starts with the tag &lt;!--SIGNATURE--&gt;. It includes the
873 author(s), last update, copyright, the links to the Root home page, to the
874 user home page, to the index file (ClassIndex.html), to the top of the page
875 and <tt>this page is automatically generated</tt> infomation. It ends with the
876 tags <tt>&lt;/body&gt;&lt;/html&gt;</tt>. If you want to replace it,
877 <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> will search for some
878 tags in your footer: Occurrences of the strings <tt>%AUTHOR%</tt>, <tt>%UPDATE%</tt>, and
879 <tt>%COPYRIGHT%</tt> are replaced by their
880 corresponding values before writing the html file. The <tt>%AUTHOR%</tt> tag will be
881 replaced by the exact string that follows Root.Html.Author, no link
882 generation will occur.</p>
883
884
885 <h4><a name="conf:search">II.7 Links to searches, home page, ViewVC</a></h4>
886
887 <p>Additional parameters can be set by Root.Html.Homepage (address of the
888 user's home page), Root.Html.SearchEngine (search engine for the class
889 documentation), Root.Html.Search (search URL, where %u is replaced by the
890 referer and %s by the escaped search expression), and a ViewVC base URL
891 Root.Html.ViewCVS. For the latter, the file name is appended or, if
892 the URL contains %f, %f is replaced by the file name.
893 All values default to "".</p>
894
895 <p>Examples:</p><pre>
896 Root.Html.Homepage: http://www.enricos-home.it
897 Root.Html.SearchEngine: http://root.cern.ch/root/Search.phtml
898 Root.Html.Search: http://www.google.com/search?q=%s+site%3A%u</pre>
899
900
901 <h4><a name="conf:charset">II.8 HTML Charset</a></h4>
902
903 <p>XHTML 1.0 transitional recommends the specification of the charset in the
904 content type meta tag, see e.g. <a href="http://www.w3.org/TR/2002/REC-xhtml1-20020801/">http://www.w3.org/TR/2002/REC-xhtml1-20020801/</a>
905 <a href="http://root.cern.ch/root/html/THtml.html">THtml</a> generates it for the HTML output files. It defaults to ISO-8859-1, and
906 can be changed using Root.Html.Charset.</p>
907
908 <p>Example:</p><pre>
909 Root.Html.Charset: EUC-JP</pre>
910
911 <h3><a name="syntax">III. Documentation syntax</a></h3>
912 <h4><a name="syntax:classdesc">III.1 Class description</a></h4>
913
914 <p>A class description block, which must be placed before the first
915 member function, has a following form:</p>
916 <pre>
917 ////////////////////////////////////////////////////////////////
918 // //
919 // TMyClass //
920 // //
921 // This is the description block. //
922 // //
923 ////////////////////////////////////////////////////////////////
924 </pre>
925 <p>The environment variable Root.Html.Description
926 (see: <a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>) contains
927 the delimiter string (default value: <tt>//_________________</tt>). It means
928 that you can also write your class description block like this:</p>
929 <pre>
930 //_____________________________________________________________
931 // A description of the class starts with the line above, and
932 // will take place here !
933 //
934 </pre>
935 <p>Note that <b><i>everything</i></b> until the first non-commented line is considered
936 as a valid class description block.</p>
937
938 <h4><a name="syntax:classidx">III.2 Class index</a></h4>
939
940 <p>All classes to be documented will have an entry in the ClassIndex.html,
941 showing their name with a link to their documentation page and a miniature
942 description. This discription for e.g. the class MyClass has to be given
943 in MyClass's header as a comment right after ClassDef(MyClass, n).</p>
944
945 <h4><a name="syntax:meth">III.3 Method documentation</a></h4>
946 <p>A member function description block starts immediately after '{'
947 and looks like this:</p>
948 <pre>
949 void TWorld::HelloWorldFunc(string *text)
950 {
951 // This is an example of description for the
952 // TWorld member function
953
954 helloWorld.Print( text );
955 }
956 </pre>
957 Like in a class description block, <b><i>everything</i></b> until the first
958 non-commented line is considered as a valid member function
959 description block.
960
961 If the rootrc variable <tt>Root.Html.DescriptionStyle</tt> is set to
962 <tt>Doc++</tt> THtml will also look for method documentation in front of
963 the function implementation. This feature is not recommended; source code
964 making use of this does not comply to the ROOT documentation standards, which
965 means future versions of THtml might not support it anymore.
966
967 <h4><a name="syntax:datamem">III.4 Data member documentation</a></h4>
968
969 <p>Data members are documented by putting a C++ comment behind their
970 declaration in the header file, e.g.</p>
971 <pre>
972 int fIAmADataMember; // this is a data member
973 </pre>
974
975
976 <h3><a name="directive">IV. Documentation directives</a></h3>
977 <em>NOTE that THtml does not yet support nested directives
978 (i.e. latex inside html etc)!</em>
979
980 <h4><a name="directive:html">IV.1 <tt>BEGIN<!-- -->_HTML</tt> <tt>END<!-- -->_HTML</tt>: include 'raw' HTML</a></h4>
981
982 <p>You can insert pure html code into your documentation comments. During the
983 generation of the documentation, this code will be inserted as is
984 into the html file.</p>
985 <p>Pure html code must be surrounded by the keywords
986 <tt>BEGIN<!-- -->_HTML</tt> and <tt>END<!-- -->_HTML</tt>, where the
987 case is ignored.
988 An example of pure html code is this class description you are reading right now.
989 THtml uses a
990 <a href="http://root.cern.ch/root/html/TDocHtmlDirective.html">TDocHtmlDirective</a>
991 object to process this directive.</p>
992
993 <h4><a name="directive:macro">IV.2 <tt>BEGIN<!-- -->_MACRO</tt> <tt>END<!-- -->_MACRO</tt>: include a picture generated by a macro</a></h4>
994
995 <p>THtml can create images from scripts. You can either call an external
996 script by surrounding it by "begin_macro"/"end_macro", or include an unnamed
997 macro within these keywords. The macro should return a pointer to an object;
998 this object will then be saved as a GIF file.</p>
999 <p>Objects deriving from
1000 <a href="http://root.cern.ch/root/html/TGObject.html">TGObject</a> (GUI elements)
1001 will need to run in graphics mode (non-batch). You must specify this as a parameter:
1002 "Begin_macro(GUI)...".
1003 To create a second tab that displays the source of the macro you can specify
1004 the argument "Begin_macro(source)...".
1005 Of course you can combine them,
1006 e.g. as "Begin_macro(source,gui)...".
1007 THtml uses a
1008 <a href="http://root.cern.ch/root/html/TDocMacroDirective.html">TDocMacroDirective</a>
1009 object to process this directive.</p>
1010 <p>This is an example:</p> END_HTML
1011 BEGIN_MACRO(source)
1012 {
1013 TCanvas* macro_example_canvas = new TCanvas("macro_example_canvas", "", 150, 150);
1014 macro_example_canvas->SetBorderSize(0);
1015 macro_example_canvas->SetFillStyle(1001);
1016 macro_example_canvas->SetFillColor(kWhite);
1017 macro_example_canvas->cd();
1018 TArc* macro_example_arc = new TArc(0.5,0.32,0.11,180,360);
1019 macro_example_arc->Draw();
1020 TEllipse* macro_example_ellipsis = new TEllipse(0.42,0.58,0.014,0.014,0,360,0);
1021 macro_example_ellipsis->SetFillStyle(0);
1022 macro_example_ellipsis->Draw();
1023 macro_example_ellipsis = new TEllipse(0.58,0.58,0.014,0.014,0,360,0);
1024 macro_example_ellipsis->SetFillStyle(0);
1025 macro_example_ellipsis->Draw();
1026 macro_example_ellipsis = new TEllipse(0.50,0.48,0.22,0.32,0,360,0);
1027 macro_example_ellipsis->SetFillStyle(0);
1028 macro_example_ellipsis->Draw();
1029 TLine* macro_example_line = new TLine(0.48,0.53,0.52,0.41);
1030 macro_example_line->Draw();
1031 return macro_example_canvas;
1032 }
1033 END_MACRO
1034
1035 BEGIN_HTML
1036 <h4><a name="directive:latex">IV.3 <tt>BEGIN<!-- -->_LATEX</tt> <tt>END<!-- -->_LATEX</tt>: include a latex picture</a></h4>
1037
1038 <p>You can specify <a href="http://root.cern.ch/root/html/TLatex.html">TLatex</a>
1039 style text and let THtml convert it into an image by surrounding it by "Begin_Latex", "End_Latex".
1040 You can have multiple lines, and e.g. align each line at the '=' sign by passing
1041 the argument <tt>separator='='</tt>. You can also specify how to align these parts;
1042 if you want the part left of the separator to be right aligned, and the right part
1043 to be left aligned, you could specify <tt>align='rl'</tt>.
1044 THtml uses a <a href="http://root.cern.ch/root/html/TDocLatexDirective.html">TDocLatexDirective</a>
1045 object to process the directive.
1046 This is an example output with arguments <tt>separator='=', align='rl'</tt>:</p>
1047 END_HTML BEGIN_LATEX(separator='=', align='rl')#kappa(x)^{2}=sin(x)^{x}
1048 x=#chi^{2} END_LATEX
1049
1050 BEGIN_HTML
1051
1052 <h3><a name="index">V. Product and module index</a></h3>
1053
1054 <p><a href="#THtml:MakeIndex">THtml::MakeIndex()</a> will generate index files for classes
1055 and types, all modules, and the product which you can set by
1056 <a href="#THtml:SetProductName">THtml::SetProductName()</a>.
1057 THtml will make use of external documentation in the module and product index,
1058 either by linking it or by including it.
1059 For the product THtml will include files found in the directory defined by
1060 <a href="#THtml:SetProductDocDir">THtml::SetProductDocDir()</a>.
1061 The files for modules are searched based on the source file directory of the
1062 module's classes; the (possibly relative) path set by
1063 <a href="#THtml:SetModuleDocPath">THtml::SetModuleDocPath()</a> will guide THtml
1064 to the files.</p>
1065
1066 <p>A filename starting with "index." will be included in the index page;
1067 all other files will be linked.
1068 Only files ending on <tt>.html</tt> or <tt>.txt</tt> will be taken into account;
1069 the text files will first be run through
1070 <a href="#THtml:Convert">THtml::Convert()</a>.
1071 You can see an example <a href="http://root.cern.ch/root/html/HIST_Index.html">here</a>;
1072 the part between "Index of HIST classes" and "Jump to" is created by parsing
1073 the module's doc directory.</p>
1074
1075 <h3><a name="aux">VI. Auxiliary files: style sheet, JavaScript, help page</a></h3>
1076
1077 <p>The documentation pages share a common set of javascript and CSS files. They
1078 are generated automatically when running <a href="#THtml:MakeAll">MakeAll()</a>;
1079 they can be generated on
1080 demand by calling <a href="#THtml:CreateAuxiliaryFiles">CreateAuxiliaryFiles()</a>.</p>
1081
1082
1083 <h3><a name="charts">VII. Class Charts</a></h3>
1084 THtml can generate a number of graphical representations for a class, which
1085 are displayed as a tabbed set of imaged ontop of the class description.
1086 It can show the inheritance, inherited and hidden members, directly and
1087 indirectly included files, and library dependencies.
1088
1089 These graphs are generated using the <a href="http://www.graphviz.org/">Graphviz</a>
1090 package. You can install it from <a href="http://www.graphviz.org">http://www.graphviz.org</a>.
1091 You can either put it into your $PATH, or tell THtml where to find it by calling
1092 <a href="#THtml:SetDotDir">SetDotDir()</a>.
1093
1094
1095 <h3><a name="confvar">VIII. Configuration variables</a></h3>
1096
1097 <p>Here is a list of all configuration variables that are known to THtml.
1098 You can set them in your .rootrc file, see
1099 <a href="http://root.cern.ch/root/html/TEnv.html">TEnv</a>.</p>
1100
1101 <pre>
1102 Root.Html.OutputDir (default: htmldoc)
1103 Root.Html.SourceDir (default: .:src/:include/)
1104 Root.Html.Author (default: // Author:) - start tag for authors
1105 Root.Html.LastUpdate (default: // @(#)) - start tag for last update
1106 Root.Html.Copyright (default: * Copyright) - start tag for copyright notice
1107 Root.Html.Description (default: //____________________ ) - start tag for class descr
1108 Root.Html.HomePage (default: ) - URL to the user defined home page
1109 Root.Html.Header (default: ) - location of user defined header
1110 Root.Html.Footer (default: ) - location of user defined footer
1111 Root.Html.Root (default: ) - URL of Root's class documentation
1112 Root.Html.SearchEngine (default: ) - link to the search engine
1113 Root.Html.Search (defualt: ) - link to search by replacing "%s" with user input
1114 Root.Html.ViewCVS (default: ) - URL of ViewCVS base
1115 Root.Html.XWho (default: http://consult.cern.ch/xwho/people?) - URL of CERN's xWho
1116 Root.Html.Charset (default: ISO-8859-1) - HTML character set
1117 </pre>
1118
1119 <h3><a name="how">IX. Behind the scene</a></h3>
1120
1121 <p>Internally, THtml is just an API class that sets up the list of known
1122 classes, and forwards API invocations to the "work horses".
1123 <a href="http://root.cern.ch/root/html/TDocOutput.html">TDocOutput</a>
1124 generates the output by letting a
1125 <a href="http://root.cern.ch/root/html/TDocParser.html">TDocParser</a>
1126 object parse the sources, which in turn invokes objects deriving from
1127 <a href="http://root.cern.ch/root/html/TDocDirective.html">TDocDirective</a>
1128 to process directives.</p>
1129
1130 END_HTML */
1131 ////////////////////////////////////////////////////////////////////////////////
1132
1133 ClassImp(THtml)
1134 //______________________________________________________________________________
1135 THtml::THtml():
1136 fCounterFormat("%12s %5s %s"),
1137 fProductName("(UNKNOWN PRODUCT)"),
1138 fThreadedClassIter(0), fMakeClassMutex(0),
1139 fGClient(0), fPathDef(0), fModuleDef(0), fFileDef(0),
1140 fLocalFiles(0), fBatch(kFALSE)
1141 {
1142 // Create a THtml object.
1143 // In case output directory does not exist an error
1144 // will be printed and gHtml stays 0 also zombie bit will be set.
1145
1146 // check for source directory
1147 fPathInfo.fInputPath = gEnv->GetValue("Root.Html.SourceDir", "./:src/:include/");
1148
1149 // check for output directory
1150 fPathInfo.fOutputDir = gEnv->GetValue("Root.Html.OutputDir", "htmldoc");
1151
1152 fLinkInfo.fXwho = gEnv->GetValue("Root.Html.XWho", "http://consult.cern.ch/xwho/people?");
1153 fLinkInfo.fROOTURL = gEnv->GetValue("Root.Html.Root", "http://root.cern.ch/root/html");
1154 fDocSyntax.fClassDocTag = gEnv->GetValue("Root.Html.Description", "//____________________");
1155 fDocSyntax.fAuthorTag = gEnv->GetValue("Root.Html.Author", "// Author:");
1156 fDocSyntax.fLastUpdateTag = gEnv->GetValue("Root.Html.LastUpdate", "// @(#)");
1157 fDocSyntax.fCopyrightTag = gEnv->GetValue("Root.Html.Copyright", "* Copyright");
1158 fOutputStyle.fHeader = gEnv->GetValue("Root.Html.Header", "");
1159 fOutputStyle.fFooter = gEnv->GetValue("Root.Html.Footer", "");
1160 fLinkInfo.fHomepage = gEnv->GetValue("Root.Html.Homepage", "");
1161 fLinkInfo.fSearchStemURL = gEnv->GetValue("Root.Html.Search", "");
1162 fLinkInfo.fSearchEngine = gEnv->GetValue("Root.Html.SearchEngine", "");
1163 fLinkInfo.fViewCVS = gEnv->GetValue("Root.Html.ViewCVS", "");
1164 fOutputStyle.fCharset = gEnv->GetValue("Root.Html.Charset", "ISO-8859-1");
1165 fDocSyntax.fDocStyle = gEnv->GetValue("Root.Html.DescriptionStyle", "");
1166
1167 fDocEntityInfo.fClasses.SetOwner();
1168 fDocEntityInfo.fModules.SetOwner();
1169 // insert html object in the list of special ROOT objects
1170 // if (!gHtml) {
1171 // gHtml = this;
1172 // gROOT->GetListOfSpecials()->Add(gHtml);
1173 // }
1174 cout << "THTML from MitAna/Utils ... " <<endl;
1175 }
1176
1177
1178 //______________________________________________________________________________
1179 THtml::~THtml()
1180 {
1181 // Default destructor
1182
1183 fDocEntityInfo.fClasses.Clear();
1184 fDocEntityInfo.fModules.Clear();
1185 // if (gHtml == this) {
1186 // gROOT->GetListOfSpecials()->Remove(gHtml);
1187 // gHtml = 0;
1188 // }
1189 delete fPathDef;
1190 delete fModuleDef;
1191 delete fFileDef;
1192 delete fLocalFiles;
1193 }
1194
1195 //______________________________________________________________________________
1196 void THtml::AddMacroPath(const char* path)
1197 {
1198 // Add path to the directories to be searched for macro files
1199 // that are to be executed via the TDocMacroDirective
1200 // ("Begin_Macro"/"End_Macro"); relative to the source file
1201 // that the directive is run on.
1202
1203 const char pathDelimiter =
1204 #ifdef R__WIN32
1205 ';';
1206 #else
1207 ':';
1208 #endif
1209 fPathInfo.fMacroPath += pathDelimiter;
1210 fPathInfo.fMacroPath += path;
1211 }
1212
1213
1214 //______________________________________________________________________________
1215 void THtml::CreateAuxiliaryFiles() const
1216 {
1217 // copy CSS, javascript file, etc to the output dir
1218 CreateJavascript();
1219 CreateStyleSheet();
1220 CopyFileFromEtcDir("HELP.html");
1221 }
1222
1223 //______________________________________________________________________________
1224 const THtml::TModuleDefinition& THtml::GetModuleDefinition() const
1225 {
1226 // Return the TModuleDefinition (or derived) object as set by
1227 // SetModuleDefinition(); create and return a TModuleDefinition object
1228 // if none was set.
1229 if (!fModuleDef) {
1230 fModuleDef = new TModuleDefinition();
1231 fModuleDef->SetOwner(const_cast<THtml*>(this));
1232 }
1233 return *fModuleDef;
1234 }
1235
1236 //______________________________________________________________________________
1237 const THtml::TFileDefinition& THtml::GetFileDefinition() const
1238 {
1239 // Return the TFileDefinition (or derived) object as set by
1240 // SetFileDefinition(); create and return a TFileDefinition object
1241 // if none was set.
1242 if (!fFileDef) {
1243 fFileDef = new TFileDefinition();
1244 fFileDef->SetOwner(const_cast<THtml*>(this));
1245 }
1246 return *fFileDef;
1247 }
1248
1249 //______________________________________________________________________________
1250 const THtml::TPathDefinition& THtml::GetPathDefinition() const
1251 {
1252 // Return the TModuleDefinition (or derived) object as set by
1253 // SetModuleDefinition(); create and return a TModuleDefinition object
1254 // if none was set.
1255 if (!fPathDef) {
1256 fPathDef = new TPathDefinition();
1257 fPathDef->SetOwner(const_cast<THtml*>(this));
1258 }
1259 return *fPathDef;
1260 }
1261
1262
1263 //______________________________________________________________________________
1264 const char* THtml::GetEtcDir() const
1265 {
1266 // Get the directory containing THtml's auxiliary files ($ROOTSYS/etc/html)
1267
1268 if (fPathInfo.fEtcDir.Length())
1269 return fPathInfo.fEtcDir;
1270
1271 R__LOCKGUARD(GetMakeClassMutex());
1272
1273 fPathInfo.fEtcDir = "html";
1274
1275 #ifdef ROOTETCDIR
1276 gSystem->PrependPathName(ROOTETCDIR, fPathInfo.fEtcDir);
1277 #else
1278 gSystem->PrependPathName("etc", fPathInfo.fEtcDir);
1279 # ifdef ROOTPREFIX
1280 gSystem->PrependPathName(ROOTPREFIX, fPathInfo.fEtcDir);
1281 # else
1282 if (getenv("ROOTSYS"))
1283 gSystem->PrependPathName(getenv("ROOTSYS"), fPathInfo.fEtcDir);
1284 # endif
1285 #endif
1286
1287 return fPathInfo.fEtcDir;
1288 }
1289
1290
1291 //______________________________________________________________________________
1292 TClassDocInfo *THtml::GetNextClass()
1293 {
1294 // Return the next class to be generated for MakeClassThreaded.
1295
1296 if (!fThreadedClassIter) return 0;
1297
1298 R__LOCKGUARD(GetMakeClassMutex());
1299
1300 TClassDocInfo* classinfo = 0;
1301 while ((classinfo = (TClassDocInfo*)(*fThreadedClassIter)())
1302 && !classinfo->IsSelected()) { }
1303
1304 if (!classinfo) {
1305 delete fThreadedClassIter;
1306 fThreadedClassIter = 0;
1307 }
1308
1309 fCounter.Form("%5d", fDocEntityInfo.fClasses.GetSize() - fThreadedClassCount++);
1310
1311 return classinfo;
1312 }
1313
1314
1315 //______________________________________________________________________________
1316 const char* THtml::GetURL(const char* lib /*=0*/) const
1317 {
1318 // Get the documentation URL for library lib.
1319 // If lib == 0 or no documentation URL has been set for lib, return the ROOT
1320 // documentation URL. The return value is always != 0.
1321
1322 R__LOCKGUARD(GetMakeClassMutex());
1323
1324 if (lib && strlen(lib)) {
1325 std::map<std::string, TString>::const_iterator iUrl = fLinkInfo.fLibURLs.find(lib);
1326 if (iUrl != fLinkInfo.fLibURLs.end()) return iUrl->second;
1327 return gEnv->GetValue(TString("Root.Html.") + lib, fLinkInfo.fROOTURL);
1328 }
1329 return fLinkInfo.fROOTURL;
1330 }
1331
1332 //______________________________________________________________________________
1333 Bool_t THtml::HaveDot()
1334 {
1335 // Check whether dot is available in $PATH or in the directory set
1336 // by SetDotPath()
1337
1338 if (fPathInfo.fFoundDot != PathInfo_t::kDotUnknown)
1339 return (fPathInfo.fFoundDot == PathInfo_t::kDotFound);
1340
1341 R__LOCKGUARD(GetMakeClassMutex());
1342
1343 Info("HaveDot", "Checking for Graphviz (dot)...");
1344 TString runDot("dot");
1345 if (fPathInfo.fDotDir.Length())
1346 gSystem->PrependPathName(fPathInfo.fDotDir, runDot);
1347 runDot += " -V";
1348 if (gDebug > 3)
1349 Info("HaveDot", "Running: %s", runDot.Data());
1350 if (gSystem->Exec(runDot)) {
1351 fPathInfo.fFoundDot = PathInfo_t::kDotNotFound;
1352 return kFALSE;
1353 }
1354 fPathInfo.fFoundDot = PathInfo_t::kDotFound;
1355 return kTRUE;
1356
1357 }
1358
1359 //______________________________________________________________________________
1360 void THtml::HelperDeleted(THtml::THelperBase* who)
1361 {
1362 // Inform the THtml object that one of its helper objects was deleted.
1363 // Called by THtml::HelperBase::~HelperBase().
1364
1365 THelperBase* helpers[3] = {fPathDef, fModuleDef, fFileDef};
1366 for (int i = 0; who && i < 3; ++i)
1367 if (who == helpers[i])
1368 helpers[i] = who = 0;
1369 }
1370
1371
1372 //______________________________________________________________________________
1373 void THtml::Convert(const char *filename, const char *title,
1374 const char *dirname /*= ""*/, const char *relpath /*= "../"*/,
1375 Int_t includeOutput /* = kNoOutput */,
1376 const char* context /* = "" */)
1377 {
1378 // It converts a single text file to HTML
1379 //
1380 //
1381 // Input: filename - name of the file to convert
1382 // title - title which will be placed at the top of the HTML file
1383 // dirname - optional parameter, if it's not specified, output will
1384 // be placed in htmldoc/examples directory.
1385 // relpath - optional parameter pointing to the THtml generated doc
1386 // on the server, relative to the current page.
1387 // includeOutput - if != kNoOutput, run the script passed as filename and
1388 // store all created canvases in PNG files that are
1389 // shown next to the converted source. Bitwise-ORing with
1390 // re-runs the script even if output PNGs exist that are newer
1391 // than the script. If kCompiledOutput is passed, the script is
1392 // run through ACLiC (.x filename+)
1393 // context - line shown verbatim at the top of the page; e.g. for links.
1394 // If context is non-empty it is expected to also print the
1395 // title.
1396 //
1397 // NOTE: Output file name is the same as filename, but with extension .html
1398 //
1399
1400 gROOT->GetListOfGlobals(kTRUE); // force update of this list
1401 CreateListOfClasses("*");
1402
1403 const char *dir;
1404
1405 // if it's not defined, make the "examples" as a default directory
1406 if (!*dirname) {
1407 gSystem->ExpandPathName(fPathInfo.fOutputDir);
1408 dir = gSystem->ConcatFileName(fPathInfo.fOutputDir, "examples");
1409 } else
1410 dir = dirname;
1411
1412 // create directory if necessary
1413 if (gSystem->AccessPathName(dir))
1414 gSystem->MakeDirectory(dir);
1415
1416 // find a file
1417 char *cRealFilename =
1418 gSystem->Which(fPathInfo.fInputPath, filename, kReadPermission);
1419
1420 if (!cRealFilename) {
1421 Error("Convert", "Can't find file '%s' !", filename);
1422 return;
1423 }
1424
1425 TString realFilename(cRealFilename);
1426 delete[] cRealFilename;
1427 cRealFilename = 0;
1428
1429 // open source file
1430 ifstream sourceFile;
1431 sourceFile.open(realFilename, ios::in);
1432
1433 if (!sourceFile.good()) {
1434 Error("Convert", "Can't open file '%s' !", realFilename.Data());
1435 return;
1436 }
1437
1438 if (gSystem->AccessPathName(dir)) {
1439 Error("Convert",
1440 "Directory '%s' doesn't exist, or it's write protected !", dir);
1441 return;
1442 }
1443 char *tmp1 =
1444 gSystem->ConcatFileName(dir, gSystem->BaseName(filename));
1445
1446 TDocOutput output(*this);
1447 if (!fGClient)
1448 gROOT->ProcessLine(TString::Format("*((TGClient**)0x%lx) = gClient;",
1449 (ULong_t)&fGClient));
1450 if (includeOutput && !fGClient)
1451 Warning("Convert", "Output requested but cannot initialize graphics: GUI and GL windows not be available");
1452 output.Convert(sourceFile, realFilename, tmp1, title, relpath, includeOutput, context, fGClient);
1453
1454 if (tmp1)
1455 delete[]tmp1;
1456 tmp1 = 0;
1457 }
1458
1459 //______________________________________________________________________________
1460 void THtml::GetModuleNameForClass(TString& module, TClass* cl) const
1461 {
1462 // Return the module name for a given class.
1463 // Use the cached information from fDocEntityInfo.fClasses.
1464
1465 module = "(UNKNOWN)";
1466 TClassDocInfo* cdi = (TClassDocInfo*)fDocEntityInfo.fClasses.FindObject(cl->GetName());
1467 if (!cdi || !cdi->GetModule())
1468 return;
1469 module = cdi->GetModule()->GetName();
1470 }
1471
1472
1473 //______________________________________________________________________________
1474 void THtml::CreateListOfClasses(const char* filter)
1475 {
1476 // Create the list of all known classes
1477
1478 if (fDocEntityInfo.fClasses.GetSize() && fDocEntityInfo.fClassFilter == filter)
1479 return;
1480
1481 Info("CreateListOfClasses", "Initializing - this might take a while...");
1482 // get total number of classes
1483 Int_t totalNumberOfClasses = gClassTable->Classes();
1484
1485 // allocate memory
1486 fDocEntityInfo.fClasses.Clear();
1487 fDocEntityInfo.fModules.Clear();
1488
1489 fDocEntityInfo.fClassFilter = filter;
1490
1491 // start from begining
1492 gClassTable->Init();
1493 if (filter && (!filter[0] || !strcmp(filter, "*")))
1494 filter = ".*";
1495 TString reg = filter;
1496 TPMERegexp re(reg);
1497
1498 for (Int_t i = 0; i < totalNumberOfClasses; i++) {
1499
1500 // get class name
1501 const char *cname = gClassTable->Next();
1502 TString s = cname;
1503
1504 // This is a hack for until after Cint and Reflex are one.
1505 if (strstr(cname, "__gnu_cxx::")) continue;
1506
1507 // get class & filename - use TROOT::GetClass, as we also
1508 // want those classes without decl file name!
1509 TClass *classPtr = TClass::GetClass((const char *) cname, kTRUE);
1510 if (!classPtr) continue;
1511
1512 Bool_t matchesSelection = re.Match(s);
1513
1514 TString hdr;
1515 TString hdrFS;
1516 TString src;
1517 TString srcFS;
1518 TString htmlfilename;
1519
1520 TClassDocInfo* cdi = (TClassDocInfo*) fDocEntityInfo.fClasses.FindObject(cname);
1521 if (cdi) {
1522 hdr = cdi->GetDeclFileName();
1523 hdrFS = cdi->GetDeclFileSysName();
1524 src = cdi->GetImplFileName();
1525 srcFS = cdi->GetImplFileSysName();
1526 htmlfilename = cdi->GetHtmlFileName();
1527 }
1528
1529 if (!hdrFS.Length()) {
1530 if (!GetFileDefinition().GetDeclFileName(classPtr, hdr, hdrFS)) {
1531 // we don't even know where the class is defined;
1532 // just skip. Silence if it doesn't match the selection anyway
1533 if (matchesSelection && (!classPtr->GetDeclFileName() || !strstr(classPtr->GetDeclFileName(),"prec_stl/")))
1534 Warning("CreateListOfClasses",
1535 "Cannot determine declaration file name for %s!", cname);
1536 continue;
1537 }
1538 }
1539
1540 Bool_t haveSource = (srcFS.Length());
1541 if (!haveSource)
1542 haveSource = GetFileDefinition().GetImplFileName(classPtr, src, srcFS);
1543
1544 if (!haveSource && gDebug > 3) {
1545 Info("CreateListOfClasses",
1546 "Cannot determine implementation file name for %s!", cname);
1547 }
1548
1549 if (!htmlfilename.Length())
1550 GetHtmlFileName(classPtr, htmlfilename);
1551
1552 if (!cdi) {
1553 cdi = new TClassDocInfo(classPtr, htmlfilename, hdrFS, srcFS, hdr, src);
1554 fDocEntityInfo.fClasses.Add(cdi);
1555 } else {
1556 cdi->SetDeclFileName(hdr);
1557 cdi->SetImplFileName(src);
1558 cdi->SetDeclFileSysName(hdrFS);
1559 cdi->SetImplFileSysName(srcFS);
1560 cdi->SetHtmlFileName(htmlfilename);
1561 }
1562
1563 cdi->SetSelected(matchesSelection);
1564
1565 TString modulename;
1566 GetModuleDefinition().GetModule(classPtr, modulename);
1567 if (!modulename.Length() || modulename == "USER")
1568 GetModuleNameForClass(modulename, classPtr);
1569
1570 TModuleDocInfo* module = (TModuleDocInfo*) fDocEntityInfo.fModules.FindObject(modulename);
1571 if (!module) {
1572 bool moduleSelected = cdi->IsSelected();
1573
1574 TString parentModuleName(gSystem->DirName(modulename));
1575 TModuleDocInfo* super = 0;
1576 if (parentModuleName.Length() && parentModuleName != ".") {
1577 super = (TModuleDocInfo*) fDocEntityInfo.fModules.FindObject(parentModuleName);
1578 if (!super) {
1579 // create parents:
1580 TString token;
1581 Ssiz_t pos = 0;
1582 while (parentModuleName.Tokenize(token, pos, "/")) {
1583 if (!token.Length() || token == ".") continue;
1584 super = new TModuleDocInfo(token, super);
1585 super->SetSelected(moduleSelected);
1586 fDocEntityInfo.fModules.Add(super);
1587 }
1588 }
1589 }
1590 module = new TModuleDocInfo(modulename, super);
1591 module->SetSelected(moduleSelected);
1592 fDocEntityInfo.fModules.Add(module);
1593 }
1594
1595 if (module) {
1596 module->AddClass(cdi);
1597 cdi->SetModule(module);
1598 if (cdi->HaveSource() && cdi->IsSelected())
1599 module->SetSelected();
1600 }
1601
1602 // clear the typedefs; we fill them later
1603 cdi->GetListOfTypedefs().Clear();
1604
1605 if (gDebug > 0)
1606 Info("CreateListOfClasses", "Adding class %s, module %s (%sselected)",
1607 cdi->GetName(), module ? module->GetName() : "[UNKNOWN]",
1608 cdi->IsSelected() ? "" : "not ");
1609 }
1610
1611 // fill typedefs
1612 TIter iTypedef(gROOT->GetListOfTypes());
1613 TDataType* dt = 0;
1614 TDocOutput output(*this);
1615 while ((dt = (TDataType*) iTypedef())) {
1616 if (dt->GetType() != -1) continue;
1617 TClassDocInfo* cdi = (TClassDocInfo*) fDocEntityInfo.fClasses.FindObject(dt->GetFullTypeName());
1618 if (cdi) {
1619 cdi->GetListOfTypedefs().Add(dt);
1620 if (gDebug > 1)
1621 Info("CreateListOfClasses", "Adding typedef %s to class %s",
1622 dt->GetName(), cdi->GetName());
1623
1624 bool inNamespace = true;
1625 TString surroundingNamespace(dt->GetName());
1626 Ssiz_t posTemplate = surroundingNamespace.Last('>');
1627 inNamespace = inNamespace && (posTemplate == kNPOS);
1628 if (inNamespace) {
1629 Ssiz_t posColumn = surroundingNamespace.Last(':');
1630 if (posColumn != kNPOS) {
1631 surroundingNamespace.Remove(posColumn - 1);
1632 TClass* clSurrounding = GetClass(surroundingNamespace);
1633 inNamespace = inNamespace && (!clSurrounding || IsNamespace(clSurrounding));
1634 }
1635 }
1636 if (inNamespace && cdi->GetModule()) {
1637 TString htmlfilename(dt->GetName());
1638 output.NameSpace2FileName(htmlfilename);
1639 htmlfilename += ".html";
1640 TClassDocInfo* cdiTD = new TClassDocInfo(dt, htmlfilename);
1641 cdiTD->SetModule(cdi->GetModule());
1642 cdiTD->SetSelected(cdi->IsSelected());
1643 cdi->GetModule()->AddClass(cdiTD);
1644 }
1645 }
1646 }
1647
1648 fDocEntityInfo.fClasses.Sort();
1649 fDocEntityInfo.fModules.Sort();
1650 TIter iterModule(&fDocEntityInfo.fModules);
1651 TModuleDocInfo* mdi = 0;
1652 while ((mdi = (TModuleDocInfo*) iterModule()))
1653 mdi->GetClasses()->Sort();
1654
1655 if (fProductName == "(UNKNOWN PRODUCT)"
1656 && fDocEntityInfo.fModules.FindObject("core/base")
1657 && fDocEntityInfo.fModules.FindObject("core/cont")
1658 && fDocEntityInfo.fModules.FindObject("core/rint")
1659 && gProgName && strstr(gProgName, "root"))
1660 // if we have these modules we're probably building the root doc
1661 fProductName = "ROOT";
1662
1663 if (fProductName == "(UNKNOWN PRODUCT)")
1664 Warning("CreateListOfClasses", "Product not set. You should call THtml->SetProduct(\"MyProductName\");");
1665
1666 if (fDocEntityInfo.fModules.GetEntries() == 1
1667 && fDocEntityInfo.fModules.At(0)->GetName()
1668 && !strcmp(fDocEntityInfo.fModules.At(0)->GetName(), "(UNKNOWN)"))
1669 // Only one module, and its name is not known.
1670 // Let's call it "MAIN":
1671 ((TModuleDocInfo*) fDocEntityInfo.fModules.At(0))->SetName("MAIN");
1672
1673 Info("CreateListOfClasses", "Initializing - DONE.");
1674 }
1675
1676
1677 //______________________________________________________________________________
1678 void THtml::CreateListOfTypes()
1679 {
1680 // Create index of all data types and a page for each typedef-to-class
1681
1682 TDocOutput output(*this);
1683 output.CreateTypeIndex();
1684 output.CreateClassTypeDefs();
1685 }
1686
1687 //______________________________________________________________________________
1688 Bool_t THtml::CopyFileFromEtcDir(const char* filename) const {
1689 // Copy a file from $ROOTSYS/etc/html into GetOutputDir()
1690
1691 R__LOCKGUARD(GetMakeClassMutex());
1692
1693 TString outFile(filename);
1694
1695 TString inFile(outFile);
1696 gSystem->PrependPathName(GetEtcDir(), inFile);
1697
1698 gSystem->PrependPathName(GetOutputDir(), outFile);
1699
1700 if (gSystem->CopyFile(inFile, outFile, kTRUE) != 0) {
1701 Warning("CopyFileFromEtcDir", "Could not copy %s to %s", inFile.Data(), outFile.Data());
1702 return kFALSE;
1703 }
1704
1705 return kTRUE;
1706 }
1707
1708 //______________________________________________________________________________
1709 void THtml::CreateHierarchy()
1710 {
1711 // Create the inheritance hierarchy diagram for all classes
1712 TDocOutput output(*this);
1713 output.CreateHierarchy();
1714 }
1715
1716 //______________________________________________________________________________
1717 void THtml::CreateJavascript() const {
1718 // Write the default ROOT style sheet.
1719 CopyFileFromEtcDir("ROOT.js");
1720 }
1721
1722 //______________________________________________________________________________
1723 void THtml::CreateStyleSheet() const {
1724 // Write the default ROOT style sheet.
1725 CopyFileFromEtcDir("ROOT.css");
1726 CopyFileFromEtcDir("shadowAlpha.png");
1727 CopyFileFromEtcDir("shadow.gif");
1728 }
1729
1730
1731
1732 //______________________________________________________________________________
1733 void THtml::GetDerivedClasses(TClass* cl, std::map<TClass*, Int_t>& derived) const
1734 {
1735 // fill derived with all classes inheriting from cl and their inheritance
1736 // distance to cl
1737
1738 TIter iClass(&fDocEntityInfo.fClasses);
1739 TClassDocInfo* cdi = 0;
1740 while ((cdi = (TClassDocInfo*) iClass())) {
1741 TClass* candidate = dynamic_cast<TClass*>(cdi->GetClass());
1742 if (!candidate) continue;
1743 if (candidate != cl && candidate->InheritsFrom(cl)) {
1744 Int_t level = 0;
1745 TClass* currentBaseOfCandidate = candidate;
1746 while (currentBaseOfCandidate != cl) {
1747 TList* bases = currentBaseOfCandidate->GetListOfBases();
1748 if (!bases) continue;
1749 TIter iBase(bases);
1750 TBaseClass* base = 0;
1751 while ((base = (TBaseClass*) iBase())) {
1752 TClass* clBase = base->GetClassPointer();
1753 if (clBase && clBase->InheritsFrom(cl)) {
1754 ++level;
1755 currentBaseOfCandidate = clBase;
1756 }
1757 }
1758 }
1759 derived[candidate] = level;
1760 }
1761 }
1762 }
1763
1764 //______________________________________________________________________________
1765 void THtml::GetHtmlFileName(TClass * classPtr, TString& filename) const
1766 {
1767 // Return real HTML filename
1768 //
1769 //
1770 // Input: classPtr - pointer to a class
1771 // filename - string containing a full name
1772 // of the corresponding HTML file after the function returns.
1773 //
1774
1775 filename.Remove(0);
1776 if (!classPtr) return;
1777
1778 TString cFilename;
1779 if (!GetImplFileName(classPtr, kFALSE, cFilename))
1780 GetDeclFileName(classPtr, kFALSE, cFilename);
1781
1782 // classes without Impl/DeclFileName don't have docs,
1783 // and classes without docs don't have output file names
1784 if (!cFilename.Length())
1785 return;
1786
1787 TString libName;
1788 const char *colon = strchr(cFilename, ':');
1789 if (colon)
1790 // old version, where source file name is prepended by "TAG:"
1791 libName = TString(cFilename, colon - cFilename);
1792 else
1793 // New version, check class's libname.
1794 // If libname is dir/libMyLib.so, check Root.Html.MyLib
1795 // If libname is myOtherLib.so.2.3, check Root.Html.myOtherLib
1796 // (i.e. remove directories, "lib" prefix, and any "extension")
1797 if (classPtr->GetSharedLibs()) {
1798 // first one is the class's lib
1799 TString libname(classPtr->GetSharedLibs());
1800 Ssiz_t posSpace = libname.First(' ');
1801 if (posSpace != kNPOS)
1802 libname.Remove(posSpace, libname.Length());
1803 TString libnameBase = gSystem->BaseName(libname);
1804 if (libnameBase.BeginsWith("lib"))
1805 libnameBase.Remove(0, 3);
1806 Ssiz_t posExt = libnameBase.First('.');
1807 if (posExt != '.')
1808 libnameBase.Remove(posExt, libnameBase.Length());
1809 if (libnameBase.Length())
1810 libName = libnameBase;
1811 }
1812
1813 filename = cFilename;
1814 TString htmlFileName;
1815 if (!filename.Length() ||
1816 !gSystem->FindFile(fPathInfo.fInputPath, filename, kReadPermission)) {
1817 htmlFileName = GetURL(libName);
1818 } else
1819 htmlFileName = "./";
1820
1821 if (htmlFileName.Length()) {
1822 filename = htmlFileName;
1823 TString className(classPtr->GetName());
1824 TDocOutput output(*const_cast<THtml*>(this));
1825 output.NameSpace2FileName(className);
1826 gSystem->PrependPathName(filename, className);
1827 filename = className;
1828 filename.ReplaceAll("\\", "/");
1829 filename += ".html";
1830 } else filename.Remove(0);
1831 }
1832
1833 //______________________________________________________________________________
1834 const char* THtml::GetHtmlFileName(const char* classname) const
1835 {
1836 // Get the html file name for a class named classname.
1837 // Returns 0 if the class is not documented.
1838 TClassDocInfo* cdi = (TClassDocInfo*) fDocEntityInfo.fClasses.FindObject(classname);
1839 if (cdi)
1840 return cdi->GetHtmlFileName();
1841 return 0;
1842 }
1843
1844 //______________________________________________________________________________
1845 TClass *THtml::GetClass(const char *name1) const
1846 {
1847 //*-*-*-*-*Return pointer to class with name*-*-*-*-*-*-*-*-*-*-*-*-*
1848 //*-* =================================
1849 if(!name1 || !name1[0]) return 0;
1850 // no doc for internal classes
1851 if (strstr(name1,"ROOT::")==name1) {
1852 Bool_t ret = kTRUE;
1853 if (!strncmp(name1 + 6,"Math", 4)) ret = kFALSE;
1854 if (!strncmp(name1 + 6,"Reflex", 6)) ret = kFALSE;
1855 if (!strncmp(name1 + 6,"Cintex", 6)) ret = kFALSE;
1856 if (ret) return 0;
1857 }
1858
1859 TClassDocInfo* cdi = (TClassDocInfo*)fDocEntityInfo.fClasses.FindObject(name1);
1860 if (!cdi) return 0;
1861 TClass *cl = dynamic_cast<TClass*>(cdi->GetClass());
1862 // hack to get rid of prec_stl types
1863 // TClassEdit checks are far too slow...
1864 /*
1865 if (cl && GetDeclFileName(cl) &&
1866 strstr(GetDeclFileName(cl),"prec_stl/"))
1867 cl = 0;
1868 */
1869 TString declFileName;
1870 if (cl && GetDeclFileName(cl, kFALSE, declFileName))
1871 return cl;
1872 return 0;
1873 }
1874
1875 //______________________________________________________________________________
1876 bool THtml::GetDeclFileName(TClass * cl, Bool_t filesys, TString& out_name) const
1877 {
1878 // Return declaration file name; return the full path if filesys is true.
1879 return GetDeclImplFileName(cl, filesys, true, out_name);
1880 }
1881
1882 //______________________________________________________________________________
1883 bool THtml::GetImplFileName(TClass * cl, Bool_t filesys, TString& out_name) const
1884 {
1885 // Return implementation file name
1886 return GetDeclImplFileName(cl, filesys, false, out_name);
1887 }
1888
1889 //______________________________________________________________________________
1890 bool THtml::GetDeclImplFileName(TClass * cl, bool filesys, bool decl, TString& out_name) const
1891 {
1892 // Combined implementation for GetDeclFileName(), GetImplFileName():
1893 // Return declaration / implementation file name (depending on decl);
1894 // return the full path if filesys is true.
1895
1896 out_name = "";
1897
1898 R__LOCKGUARD(GetMakeClassMutex());
1899 TClassDocInfo* cdi = (TClassDocInfo*) fDocEntityInfo.fClasses.FindObject(cl->GetName());
1900 // whether we need to determine the fil name
1901 bool determine = (!cdi); // no cdi
1902 if (!determine) determine |= decl && filesys && !cdi->GetDeclFileSysName()[0];
1903 if (!determine) determine |= decl && !filesys && !cdi->GetDeclFileName()[0];
1904 if (!determine) determine |= !decl && filesys && !cdi->GetImplFileSysName()[0];
1905 if (!determine) determine |= !decl && !filesys && !cdi->GetImplFileName()[0];
1906 if (determine) {
1907 TString name;
1908 TString sysname;
1909 if (decl) {
1910 if (!GetFileDefinition().GetDeclFileName(cl, name, sysname))
1911 return false;
1912 } else {
1913 if (!GetFileDefinition().GetImplFileName(cl, name, sysname))
1914 return false;
1915 }
1916 if (cdi) {
1917 if (decl) {
1918 if (!cdi->GetDeclFileName() || !cdi->GetDeclFileName()[0])
1919 cdi->SetDeclFileName(name);
1920 if (!cdi->GetDeclFileSysName() || !cdi->GetDeclFileSysName()[0])
1921 cdi->SetDeclFileSysName(sysname);
1922 } else {
1923 if (!cdi->GetImplFileName() || !cdi->GetImplFileName()[0])
1924 cdi->SetImplFileName(name);
1925 if (!cdi->GetImplFileSysName() || !cdi->GetImplFileSysName()[0])
1926 cdi->SetImplFileSysName(sysname);
1927 }
1928 }
1929
1930 if (filesys) out_name = sysname;
1931 else out_name = name;
1932 return true;
1933 }
1934 if (filesys) {
1935 if (decl) out_name = cdi->GetDeclFileSysName();
1936 else out_name = cdi->GetImplFileSysName();
1937 } else {
1938 if (decl) out_name = cdi->GetDeclFileName();
1939 else out_name = cdi->GetImplFileName();
1940 }
1941 return true;
1942 }
1943
1944 //______________________________________________________________________________
1945 const TString& THtml::GetOutputDir(Bool_t createDir /*= kTRUE*/) const
1946 {
1947 // Return the output directory as set by SetOutputDir().
1948 // Create it if it doesn't exist and if createDir is kTRUE.
1949
1950 if (createDir) {
1951 R__LOCKGUARD(GetMakeClassMutex());
1952
1953 gSystem->ExpandPathName(const_cast<THtml*>(this)->fPathInfo.fOutputDir);
1954 Long64_t sSize;
1955 Long_t sId, sFlags, sModtime;
1956 Int_t st = gSystem->GetPathInfo(fPathInfo.fOutputDir, &sId, &sSize, &sFlags, &sModtime);
1957 if (st || !(sFlags & 2)) {
1958 if (st == 0)
1959 Error("GetOutputDir", "output directory %s is an existing file",
1960 fPathInfo.fOutputDir.Data());
1961 else if (gSystem->MakeDirectory(fPathInfo.fOutputDir) == -1)
1962 Error("GetOutputDir", "output directory %s does not exist and can't create it", fPathInfo.fOutputDir.Data());
1963 }
1964 }
1965 return fPathInfo.fOutputDir;
1966 }
1967
1968 //______________________________________________________________________________
1969 Bool_t THtml::IsNamespace(const TClass*cl)
1970 {
1971 // Check whether cl is a namespace
1972 return (cl->Property() & kIsNamespace);
1973 }
1974
1975 //______________________________________________________________________________
1976 void THtml::LoadAllLibs()
1977 {
1978 // Load all libraries known to ROOT via the rootmap system.
1979
1980 TEnv* mapfile = gInterpreter->GetMapfile();
1981 if (!mapfile || !mapfile->GetTable()) return;
1982
1983 std::set<std::string> loadedlibs;
1984 std::set<std::string> failedlibs;
1985
1986 TEnvRec* rec = 0;
1987 TIter iEnvRec(mapfile->GetTable());
1988 while ((rec = (TEnvRec*) iEnvRec())) {
1989 TString libs = rec->GetValue();
1990 TString lib;
1991 Ssiz_t pos = 0;
1992 while (libs.Tokenize(lib, pos)) {
1993 // check that none of the libs failed to load
1994 if (failedlibs.find(lib.Data()) != failedlibs.end()) {
1995 // don't load it or any of its dependencies
1996 libs = "";
1997 break;
1998 }
1999 }
2000 pos = 0;
2001 while (libs.Tokenize(lib, pos)) {
2002 // ignore libCore - it's already loaded
2003 if (lib.BeginsWith("libCore"))
2004 continue;
2005
2006 if (loadedlibs.find(lib.Data()) == loadedlibs.end()) {
2007 // just load the first library - TSystem will do the rest.
2008 gSystem->Load(lib);
2009 loadedlibs.insert(lib.Data());
2010 }
2011 }
2012 }
2013 }
2014
2015
2016 //______________________________________________________________________________
2017 void THtml::MakeAll(Bool_t force, const char *filter, int numthreads /*= -1*/)
2018 {
2019 // Produce documentation for all the classes specified in the filter (by default "*")
2020 // To process all classes having a name starting with XX, do:
2021 // html.MakeAll(kFALSE,"XX*");
2022 // If force=kFALSE (default), only the classes that have been modified since
2023 // the previous call to this function will be generated.
2024 // If force=kTRUE, all classes passing the filter will be processed.
2025 // If numthreads is != -1, use numthreads threads, else decide automatically
2026 // based on the number of CPUs.
2027
2028 MakeIndex(filter);
2029
2030 if (numthreads == 1) {
2031 // CreateListOfClasses(filter); already done by MakeIndex
2032 TClassDocInfo* classinfo = 0;
2033 TIter iClassInfo(&fDocEntityInfo.fClasses);
2034 UInt_t count = 0;
2035
2036 while ((classinfo = (TClassDocInfo*)iClassInfo())) {
2037 if (!classinfo->IsSelected())
2038 continue;
2039 fCounter.Form("%5d", fDocEntityInfo.fClasses.GetSize() - count++);
2040 MakeClass(classinfo, force);
2041 }
2042 } else {
2043 if (numthreads == -1) {
2044 SysInfo_t sysinfo;
2045 gSystem->GetSysInfo(&sysinfo);
2046 numthreads = sysinfo.fCpus;
2047 if (numthreads < 1)
2048 numthreads = 2;
2049 }
2050 fThreadedClassCount = 0;
2051 fThreadedClassIter = new TIter(&fDocEntityInfo.fClasses);
2052 THtmlThreadInfo hti(this, force);
2053 if (!fMakeClassMutex && gGlobalMutex) {
2054 gGlobalMutex->Lock();
2055 fMakeClassMutex = gGlobalMutex->Factory(kTRUE);
2056 gGlobalMutex->UnLock();
2057 }
2058
2059 TList threads;
2060 gSystem->Load("libThread");
2061 while (--numthreads >= 0) {
2062 TThread* thread = new TThread(MakeClassThreaded, &hti);
2063 thread->Run();
2064 threads.Add(thread);
2065 }
2066
2067 TIter iThread(&threads);
2068 TThread* thread = 0;
2069 Bool_t wait = kTRUE;
2070 while (wait) {
2071 while (wait && (thread = (TThread*) iThread()))
2072 wait &= (thread->GetState() == TThread::kRunningState);
2073 gSystem->ProcessEvents();
2074 gSystem->Sleep(500);
2075 }
2076
2077 iThread.Reset();
2078 while ((thread = (TThread*) iThread()))
2079 thread->Join();
2080 }
2081 fCounter.Remove(0);
2082 }
2083
2084
2085 //______________________________________________________________________________
2086 void THtml::MakeClass(const char *className, Bool_t force)
2087 {
2088 // Make HTML files for a single class
2089 //
2090 //
2091 // Input: className - name of the class to process
2092 //
2093 CreateListOfClasses("*");
2094
2095 TClassDocInfo* cdi = (TClassDocInfo*)fDocEntityInfo.fClasses.FindObject(className);
2096 if (!cdi) {
2097 if (!TClassEdit::IsStdClass(className)) // stl classes won't be available, so no warning
2098 Error("MakeClass", "Unknown class '%s'!", className);
2099 return;
2100 }
2101
2102 MakeClass(cdi, force);
2103 }
2104
2105 //______________________________________________________________________________
2106 void THtml::MakeClass(void *cdi_void, Bool_t force)
2107 {
2108 // Make HTML files for a single class
2109 //
2110 //
2111 // Input: cdi - doc info for class to process
2112 //
2113 if (!fDocEntityInfo.fClasses.GetSize())
2114 CreateListOfClasses("*");
2115
2116 TClassDocInfo* cdi = (TClassDocInfo*) cdi_void;
2117 TClass* currentClass = dynamic_cast<TClass*>(cdi->GetClass());
2118
2119 if (!currentClass) {
2120 if (!cdi->GetClass() &&
2121 !TClassEdit::IsStdClass(cdi->GetName())) // stl classes won't be available, so no warning
2122 Error("MakeClass", "Class '%s' is known, but I cannot find its TClass object!", cdi->GetName());
2123 return;
2124 }
2125 TString htmlFile(cdi->GetHtmlFileName());
2126 if (htmlFile.Length()
2127 && (htmlFile.BeginsWith("http://")
2128 || htmlFile.BeginsWith("https://")
2129 || gSystem->IsAbsoluteFileName(htmlFile))
2130 ) {
2131 htmlFile.Remove(0);
2132 }
2133 if (htmlFile.Length()) {
2134 TClassDocOutput cdo(*this, currentClass, &cdi->GetListOfTypedefs());
2135 cdo.Class2Html(force);
2136 cdo.MakeTree(force);
2137 } else {
2138 TString what(cdi->GetName());
2139 what += " (sources not found)";
2140 Printf(fCounterFormat.Data(), "-skipped-", fCounter.Data(), what.Data());
2141 }
2142 }
2143
2144
2145 //______________________________________________________________________________
2146 void* THtml::MakeClassThreaded(void* info) {
2147 // Entry point of worker threads for multi-threaded MakeAll().
2148 // info points to an (internal) THtmlThreadInfo object containing the current
2149 // THtml object, and whether "force" was passed to MakeAll().
2150 // The thread will poll GetNextClass() until no further class is available.
2151
2152 const THtmlThreadInfo* hti = (const THtmlThreadInfo*)info;
2153 if (!hti) return 0;
2154 TClassDocInfo* classinfo = 0;
2155 while ((classinfo = hti->GetHtml()->GetNextClass()))
2156 hti->GetHtml()->MakeClass(classinfo, hti->GetForce());
2157
2158 return 0;
2159 }
2160
2161 //______________________________________________________________________________
2162 void THtml::MakeIndex(const char *filter)
2163 {
2164 // Create the index files for the product, modules, all types, etc.
2165 // By default all classes are indexed (if filter="*");
2166 // to generate an index for all classes starting with "XX", do
2167 // html.MakeIndex("XX*");
2168
2169 CreateListOfClasses(filter);
2170
2171 TDocOutput output(*this);
2172 // create indices
2173 output.CreateTypeIndex();
2174 output.CreateClassTypeDefs();
2175 output.CreateModuleIndex();
2176 output.CreateClassIndex();
2177 output.CreateProductIndex();
2178
2179 // create a class hierarchy
2180 output.CreateHierarchy();
2181 }
2182
2183
2184 //______________________________________________________________________________
2185 void THtml::MakeTree(const char *className, Bool_t force)
2186 {
2187 // Make an inheritance tree
2188 //
2189 //
2190 // Input: className - name of the class to process
2191 //
2192
2193 // create canvas & set fill color
2194 TClass *classPtr = GetClass(className);
2195
2196 if (!classPtr) {
2197 Error("MakeTree", "Unknown class '%s' !", className);
2198 return;
2199 }
2200
2201 TClassDocOutput cdo(*this, classPtr, 0);
2202 cdo.MakeTree(force);
2203 }
2204
2205 //______________________________________________________________________________
2206 void THtml::SetFoundDot(Bool_t found) {
2207 // Set whether "dot" (a GraphViz utility) is avaliable
2208 if (found) fPathInfo.fFoundDot = PathInfo_t::kDotFound;
2209 else fPathInfo.fFoundDot = PathInfo_t::kDotNotFound;
2210 }
2211
2212 //______________________________________________________________________________
2213 void THtml::SetLocalFiles() const
2214 {
2215 // Fill the files available in the file system below fPathInfo.fInputPath
2216 if (fLocalFiles) delete fLocalFiles;
2217 fLocalFiles = new TFileSysDB(fPathInfo.fInputPath, fPathInfo.fIgnorePath + "|(\\b" + GetOutputDir(kFALSE) + "\\b)" , 6);
2218 }
2219
2220 //______________________________________________________________________________
2221 void THtml::SetModuleDefinition(const TModuleDefinition& md)
2222 {
2223 // Set the module defining object to be used; can also be a user derived
2224 // object (a la traits).
2225 delete fModuleDef;
2226 fModuleDef = (TModuleDefinition*) md.Clone();
2227 fModuleDef->SetOwner(const_cast<THtml*>(this));
2228 }
2229
2230
2231 //______________________________________________________________________________
2232 void THtml::SetFileDefinition(const TFileDefinition& md)
2233 {
2234 // Set the file defining object to be used; can also be a user derived
2235 // object (a la traits).
2236 delete fFileDef;
2237 fFileDef = (TFileDefinition*) md.Clone();
2238 fFileDef->SetOwner(const_cast<THtml*>(this));
2239 }
2240
2241
2242 //______________________________________________________________________________
2243 void THtml::SetPathDefinition(const TPathDefinition& md)
2244 {
2245 // Set the path defining object to be used; can also be a user derived
2246 // object (a la traits).
2247 delete fPathDef;
2248 fPathDef = (TPathDefinition*) md.Clone();
2249 fPathDef->SetOwner(const_cast<THtml*>(this));
2250 }
2251
2252
2253 //______________________________________________________________________________
2254 void THtml::SetInputDir(const char *dir)
2255 {
2256 // Set the directory containing the source files.
2257 // The source file for a class MyClass will be searched
2258 // by prepending dir to the value of
2259 // MyClass::Class()->GetImplFileName() - which can contain
2260 // directory information!
2261 // Also resets the class structure, in case new files can
2262 // be found after this call.
2263
2264 fPathInfo.fInputPath = dir;
2265 gSystem->ExpandPathName(fPathInfo.fInputPath);
2266
2267 // reset class table
2268 fDocEntityInfo.fClasses.Clear();
2269 fDocEntityInfo.fModules.Clear();
2270 }
2271
2272 //______________________________________________________________________________
2273 void THtml::SetDeclFileName(TClass* cl, const char* filename)
2274 {
2275 // Explicitly set a decl file name for TClass cl.
2276 TClassDocInfo* cdi = (TClassDocInfo*) fDocEntityInfo.fClasses.FindObject(cl->GetName());
2277 if (!cdi) {
2278 cdi = new TClassDocInfo(cl, "" /*html*/, "" /*fsdecl*/, "" /*fsimpl*/, filename);
2279 fDocEntityInfo.fClasses.Add(cdi);
2280 } else
2281 cdi->SetDeclFileName(filename);
2282 }
2283
2284 //______________________________________________________________________________
2285 void THtml::SetImplFileName(TClass* cl, const char* filename)
2286 {
2287 // Explicitly set a impl file name for TClass cl.
2288 TClassDocInfo* cdi = (TClassDocInfo*) fDocEntityInfo.fClasses.FindObject(cl->GetName());
2289 if (!cdi) {
2290 cdi = new TClassDocInfo(cl, "" /*html*/, "" /*fsdecl*/, "" /*fsimpl*/, 0 /*decl*/, filename);
2291 fDocEntityInfo.fClasses.Add(cdi);
2292 } else
2293 cdi->SetImplFileName(filename);
2294 }