// @(#)root/html:$Id$ // Author: Axel Naumann 2007-01-09 /************************************************************************* * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #include "TClassDocOutput.h" #include "TBaseClass.h" #include "TClassEdit.h" #include "TDataMember.h" #include "TMethodArg.h" #include "TDataType.h" #include "TDocInfo.h" #include "TDocParser.h" #include "TEnv.h" #include "TError.h" #include "THtml.h" #include "TMethod.h" #include "TROOT.h" #include "TSystem.h" #include "TVirtualPad.h" #include "TVirtualMutex.h" #include "Riostream.h" #include //______________________________________________________________________________ // // Write the documentation for a class or namespace. The documentation is // parsed by TDocParser and then passed to TClassDocOutput to generate // the class doc header, the class description, members overview, and method // documentation. All generic output functionality is in TDocOutput; it is // re-used in this derived class. // // You usually do not use this class yourself; it is invoked indirectly by // THtml. Customization of the output should happen via the interfaces defined // by THtml. //______________________________________________________________________________ ClassImp(TClassDocOutput); //______________________________________________________________________________ TClassDocOutput::TClassDocOutput(THtml& html, TClass* cl, TList* typedefs): TDocOutput(html), fHierarchyLines(0), fCurrentClass(cl), fCurrentClassesTypedefs(typedefs), fParser(0) { // Create an object given the invoking THtml object, and the TClass // object that we will generate output for. fParser = new TDocParser(*this, fCurrentClass); } //______________________________________________________________________________ TClassDocOutput::~TClassDocOutput() { // Destructor, deletes fParser delete fParser; } //______________________________________________________________________________ void TClassDocOutput::Class2Html(Bool_t force) { // Create HTML files for a single class. // gROOT->GetListOfGlobals(kTRUE); // create a filename TString filename(fCurrentClass->GetName()); NameSpace2FileName(filename); gSystem->PrependPathName(fHtml->GetOutputDir(), filename); filename += ".html"; if (!force && !IsModified(fCurrentClass, kSource) && !IsModified(fCurrentClass, kDoc)) { Printf(fHtml->GetCounterFormat(), "-no change-", fHtml->GetCounter(), filename.Data()); return; } // open class file std::ofstream classFile(filename); if (!classFile.good()) { Error("Make", "Can't open file '%s' !", filename.Data()); return; } Printf(fHtml->GetCounterFormat(), "", fHtml->GetCounter(), filename.Data()); // write a HTML header for the classFile file WriteHtmlHeader(classFile, fCurrentClass->GetName(), "", fCurrentClass); WriteClassDocHeader(classFile); // copy .h file to the Html output directory TString declf; if (fHtml->GetDeclFileName(fCurrentClass, kTRUE, declf)) CopyHtmlFile(declf); // process a '.cxx' file fParser->Parse(classFile); // write classFile footer WriteHtmlFooter(classFile, "", fParser->GetSourceInfo(TDocParser::kInfoLastUpdate), fParser->GetSourceInfo(TDocParser::kInfoAuthor), fParser->GetSourceInfo(TDocParser::kInfoCopyright)); } //______________________________________________________________________________ void TClassDocOutput::ListFunctions(std::ostream& classFile) { // Write the list of functions // loop to get a pointers to method names classFile << endl << "
" << endl; TString mangled(fCurrentClass->GetName()); NameSpace2FileName(mangled); classFile << "

Function Members (Methods)

" << endl; const char* tab4nbsp="    "; TString declFile; fHtml->GetDeclFileName(fCurrentClass, kFALSE, declFile); if (fCurrentClass->Property() & kIsAbstract) classFile << " 
" << tab4nbsp << "This is an abstract class, constructors will not be documented.
" << endl << tab4nbsp << "Look at the BaseName(declFile) << "\">header to check for available constructors.

" << endl; Int_t minAccess = 0; if (fHtml->IsNamespace(fCurrentClass)) minAccess = TDocParser::kPublic; for (Int_t access = TDocParser::kPublic; access >= minAccess; --access) { const TList* methods = fParser->GetMethods((TDocParser::EAccess)access); if (methods->GetEntries() == 0) continue; classFile << "
" << accesstxt[access] << ":" << endl << "" << endl; TIter iMethWrap(methods); TDocMethodWrapper *methWrap = 0; while ((methWrap = (TDocMethodWrapper*) iMethWrap())) { const TMethod* method = methWrap->GetMethod(); // it's a c'tor - Cint stores the class name as return type Bool_t isctor = (!strcmp(method->GetName(), method->GetReturnTypeName())); // it's a d'tor - Cint stores "void" as return type Bool_t isdtor = (!isctor && method->GetName()[0] == '~'); classFile << "GetClass() != fCurrentClass) classFile << "inh"; classFile << "\">" << endl; } classFile << endl << "
"; if (kIsVirtual & method->Property()) { if (!isdtor) classFile << "virtual "; else classFile << " virtual"; } if (kIsStatic & method->Property()) classFile << "static "; if (!isctor && !isdtor) fParser->DecorateKeywords(classFile, method->GetReturnTypeName()); TString mangledM(method->GetClass()->GetName()); NameSpace2FileName(mangledM); classFile << "GetClass() != fCurrentClass) { TString htmlFile; fHtml->GetHtmlFileName(method->GetClass(), htmlFile); classFile << htmlFile; } classFile << "#" << mangledM; classFile << ":"; mangledM = method->GetName(); NameSpace2FileName(mangledM); Int_t overloadIdx = methWrap->GetOverloadIdx(); if (overloadIdx) { mangledM += "@"; mangledM += overloadIdx; } classFile << mangledM << "\">"; if (method->GetClass() != fCurrentClass) { classFile << ""; ReplaceSpecialChars(classFile, method->GetClass()->GetName()); classFile << "::"; } ReplaceSpecialChars(classFile, method->GetName()); classFile << ""; fParser->DecorateKeywords(classFile, const_cast(method)->GetSignature()); bool propSignal = false; bool propMenu = false; bool propToggle = false; bool propGetter = false; if (method->GetTitle()) { propSignal = (strstr(method->GetTitle(), "*SIGNAL*")); propMenu = (strstr(method->GetTitle(), "*MENU*")); propToggle = (strstr(method->GetTitle(), "*TOGGLE*")); propGetter = (strstr(method->GetTitle(), "*GETTER")); if (propSignal || propMenu || propToggle || propGetter) { classFile << ""; if (propSignal) classFile << "SIGNAL "; if (propMenu) classFile << "MENU "; if (propToggle) classFile << "TOGGLE "; if (propGetter) { TString getter(method->GetTitle()); Ssiz_t posGetter = getter.Index("*GETTER="); getter.Remove(0, posGetter + 8); classFile << "GETTER "; } classFile << ""; } } classFile << "
" << endl; } classFile << "
" << endl; // class="functions" } //______________________________________________________________________________ void TClassDocOutput::ListDataMembers(std::ostream& classFile) { // Write the list of data members and enums // make a loop on data members Bool_t haveDataMembers = (fParser->GetDataMembers(TDocParser::kPrivate)->GetEntries() || fParser->GetDataMembers(TDocParser::kProtected)->GetEntries() || fParser->GetDataMembers(TDocParser::kPublic)->GetEntries() || fParser->GetEnums(TDocParser::kPublic)->GetEntries() || fParser->GetEnums(TDocParser::kProtected)->GetEntries() || fParser->GetEnums(TDocParser::kPrivate)->GetEntries()); if (!haveDataMembers) return; classFile << endl << "
" << endl; TString mangled(fCurrentClass->GetName()); NameSpace2FileName(mangled); classFile << "

Data Members

" << endl; for (Int_t access = 5; access >= 0 && !fHtml->IsNamespace(fCurrentClass); --access) { const TList* datamembers = 0; if (access > 2) datamembers = fParser->GetEnums((TDocParser::EAccess) (access - 3)); else datamembers = fParser->GetDataMembers((TDocParser::EAccess) access); if (datamembers->GetEntries() == 0) continue; classFile << "
2) what = "enum"; const char* accessID [] = {"priv", "prot", "publ"}; const char* accesstxt[] = {"private", "protected", "public"}; classFile << "id=\"" << what << accessID[access%3] << "\">" << accesstxt[access%3] << ":" << endl << "" << endl; TIter iDM(datamembers); TDataMember *member = 0; TString prevEnumName; Bool_t prevIsInh = kTRUE; while ((member = (TDataMember*) iDM())) { Bool_t haveNewEnum = access > 2 && prevEnumName != member->GetTypeName(); if (haveNewEnum) { if (prevEnumName.Length()) { classFile << "" << endl; } prevEnumName = member->GetTypeName(); } classFile << "GetClass() != fCurrentClass); if (prevIsInh) classFile << "inh"; classFile << "\">"; if (member->GetTitle() && member->GetTitle()[0]) { classFile << "" << endl; } // for members if (prevEnumName.Length()) { classFile << "" << endl; } classFile << endl << "
};
"; if (haveNewEnum) { TString enumName(member->GetTypeName()); TString myScope(fCurrentClass->GetName()); myScope += "::"; enumName.ReplaceAll(myScope, ""); if (enumName.EndsWith("::")) enumName += "[unnamed]"; Ssiz_t startClassName = 0; if (!enumName.BeginsWith("enum ")) classFile << "enum "; else startClassName = 5; Ssiz_t endClassName = enumName.Last(':'); // need template handling here! if (endClassName != kNPOS && endClassName > 0 && enumName[endClassName - 1] == ':') { // TClass* cl = fHtml->GetClass(TString(enumName(startClassName, endClassName - startClassName - 1))); TSubString substr(enumName(startClassName, endClassName - startClassName + 1)); // if (cl) // ReferenceEntity(substr, cl); enumName.Insert(substr.Start() + substr.Length(), ""); enumName.Insert(substr.Start(), ""); } classFile << enumName << " { "; } else if (access < 3) { if (member->Property() & G__BIT_ISSTATIC) classFile << "static "; std::string shortTypeName(fHtml->ShortType(member->GetFullTypeName())); fParser->DecorateKeywords(classFile, shortTypeName.c_str()); } TString mangledM(member->GetClass()->GetName()); NameSpace2FileName(mangledM); classFile << "GetClass() != fCurrentClass) { classFile << "href=\""; TString htmlFile; fHtml->GetHtmlFileName(member->GetClass(), htmlFile); classFile << htmlFile << "#"; } else classFile << "name=\""; classFile << mangledM; classFile << ":"; mangledM = member->GetName(); NameSpace2FileName(mangledM); classFile << mangledM << "\">"; if (member->GetClass() == fCurrentClass) classFile << ""; if (access < 3 && member->GetClass() != fCurrentClass) { classFile << ""; ReplaceSpecialChars(classFile, member->GetClass()->GetName()); classFile << "::"; } ReplaceSpecialChars(classFile, member->GetName()); // Add the dimensions to "array" members for (Int_t indx = 0; indx < member->GetArrayDim(); ++indx) if (member->GetMaxIndex(indx) <= 0) break; else classFile << "[" << member->GetMaxIndex(indx) << "]"; if (member->GetClass() != fCurrentClass) classFile << ""; classFile << ""; ReplaceSpecialChars(classFile, member->GetTitle()); } else classFile << ""; classFile << "
};
" << endl; } // for access classFile << "
" << endl; // datamembers } //______________________________________________________________________________ Bool_t TClassDocOutput::ClassDotCharts(std::ostream& out) { // This function builds the class charts for one class in GraphViz/Dot format, // i.e. the inheritance diagram, the include dependencies, and the library // dependency. // // Input: out - output file stream if (!fHtml->HaveDot()) return kFALSE; TString title(fCurrentClass->GetName()); NameSpace2FileName(title); TString dir("inh"); gSystem->PrependPathName(fHtml->GetOutputDir(), dir); gSystem->MakeDirectory(dir); dir = "inhmem"; gSystem->PrependPathName(fHtml->GetOutputDir(), dir); gSystem->MakeDirectory(dir); dir = "incl"; gSystem->PrependPathName(fHtml->GetOutputDir(), dir); gSystem->MakeDirectory(dir); dir = "lib"; gSystem->PrependPathName(fHtml->GetOutputDir(), dir); gSystem->MakeDirectory(dir); TString filenameInh(title); gSystem->PrependPathName("inh", filenameInh); gSystem->PrependPathName(fHtml->GetOutputDir(), filenameInh); filenameInh += "_Inh"; if (!CreateDotClassChartInh(filenameInh + ".dot") || !RunDot(filenameInh, &out)) return kFALSE; TString filenameInhMem(title); gSystem->PrependPathName("inhmem", filenameInhMem); gSystem->PrependPathName(fHtml->GetOutputDir(), filenameInhMem); filenameInhMem += "_InhMem"; if (CreateDotClassChartInhMem(filenameInhMem + ".dot")) RunDot(filenameInhMem, &out); TString filenameIncl(title); gSystem->PrependPathName("incl", filenameIncl); gSystem->PrependPathName(fHtml->GetOutputDir(), filenameIncl); filenameIncl += "_Incl"; if (CreateDotClassChartIncl(filenameIncl + ".dot")) RunDot(filenameIncl, &out); TString filenameLib(title); gSystem->PrependPathName("lib", filenameLib); gSystem->PrependPathName(fHtml->GetOutputDir(), filenameLib); filenameLib += "_Lib"; if (CreateDotClassChartLib(filenameLib + ".dot")) RunDot(filenameLib, &out); out << "
" << endl << "Inheritance" << endl << "Inherited Members" << endl << "Includes" << endl << "Libraries
" << endl << "
" << endl << "\"Class
" << endl; return kTRUE; } //______________________________________________________________________________ void TClassDocOutput::ClassHtmlTree(std::ostream& out, TClass * classPtr, ETraverse dir, int depth) { // This function builds the class tree for one class in HTML // (inherited and succeeding classes, called recursively) // // // Input: out - output file stream // classPtr - pointer to the class // dir - direction to traverse tree: up, down or both // if (dir == kBoth) { out << "" << endl; // draw class tree into nested tables recursively out << ""; out << "
" << "Inheritance Chart:
"; out << "
" << endl; out << "" << endl; } else { out << "
"; } //////////////////////////////////////////////////////// // Loop up to mother classes if (dir == kUp || dir == kBoth) { // make a loop on base classes TBaseClass *inheritFrom; TIter nextBase(classPtr->GetListOfBases()); UInt_t bgcolor=255-depth*8; Bool_t first = kTRUE; while ((inheritFrom = (TBaseClass *) nextBase())) { if (first) { out << "" << endl; // put it in additional row in table out << ""; } } out << "" << endl; // put it in additional row in table //////////////////////////////////////////////////////// // Loop down to child classes if (dir == kDown || dir == kBoth) { // 1. make a list of class names // 2. use DescendHierarchy out << "" << endl; // free allocated memory } out << "
" << endl; first = kFALSE; } else out << "" << endl; out << ""<< endl; } if (!first) { out << "
" << endl; // get a class TClass *classInh = fHtml->GetClass((const char *) inheritFrom->GetName()); if (classInh) ClassHtmlTree(out, classInh, kUp, depth+1); else out << "" << (const char *) inheritFrom->GetName() << ""; out << "
" << endl; // put it in additional row in table //////////////////////////////////////////////////////// // Output Class Name const char *className = classPtr->GetName(); TString htmlFile; fHtml->GetHtmlFileName(classPtr, htmlFile); TString anchor(className); NameSpace2FileName(anchor); if (dir == kUp) { if (htmlFile) { out << "
"; ReplaceSpecialChars(out, className); out << "
" << endl; } else ReplaceSpecialChars(out, className); } if (dir == kBoth) { if (htmlFile.Length()) { out << "
"; ReplaceSpecialChars(out, className); out << "
" << endl; } else ReplaceSpecialChars(out, className); } out << "
" << endl; fHierarchyLines = 0; DescendHierarchy(out,classPtr,10); out << "
"; if (dir==kBoth && fHierarchyLines>=10) out << "
 [more...]"; out<<"
" << endl; if (dir == kBoth) out << "
"<GetName()); NameSpace2FileName(filename); gSystem->PrependPathName(fHtml->GetOutputDir(), filename); filename += "_Tree.pdf"; if (IsModified(fCurrentClass, kTree) || force) { // TCanvas already prints pdf being saved // Printf(fHtml->GetCounterFormat(), "", "", filename); fCurrentClass->Draw("same"); Int_t saveErrorIgnoreLevel = gErrorIgnoreLevel; gErrorIgnoreLevel = kWarning; psCanvas->SaveAs(filename); gErrorIgnoreLevel = saveErrorIgnoreLevel; } else Printf(fHtml->GetCounterFormat(), "-no change-", "", filename.Data()); } //______________________________________________________________________________ Bool_t TClassDocOutput::CreateDotClassChartInh(const char* filename) { // Build the class tree for one class in GraphViz/Dot format // // // Input: filename - output dot file incl. path std::ofstream outdot(filename); outdot << "strict digraph G {" << endl << "rankdir=RL;" << endl << "ranksep=2;" << endl << "nodesep=0;" << endl << "size=\"8,10\";" << endl << "ratio=auto;" << endl << "margin=0;" << endl << "node [shape=plaintext,fontsize=40,width=4,height=0.75];" << endl << "\"" << fCurrentClass->GetName() << "\" [shape=ellipse];" << endl; std::stringstream ssDep; std::list writeBasesFor; writeBasesFor.push_back(fCurrentClass); Bool_t haveBases = fCurrentClass->GetListOfBases() && fCurrentClass->GetListOfBases()->GetSize(); if (haveBases) { outdot << "{" << endl; while (!writeBasesFor.empty()) { TClass* cl = writeBasesFor.front(); writeBasesFor.pop_front(); if (cl != fCurrentClass) { outdot << " \"" << cl->GetName() << "\""; const char* htmlFileName = fHtml->GetHtmlFileName(cl->GetName()); if (htmlFileName) outdot << " [URL=\"" << htmlFileName << "\"]"; outdot << ";" << endl; } if (cl->GetListOfBases() && cl->GetListOfBases()->GetSize()) { ssDep << " \"" << cl->GetName() << "\" -> {"; TIter iBase(cl->GetListOfBases()); TBaseClass* base = 0; while ((base = (TBaseClass*)iBase())) { ssDep << " \"" << base->GetName() << "\";"; writeBasesFor.push_back(base->GetClassPointer()); } ssDep << "}" << endl; } } outdot << "}" << endl; // cluster } std::map derivesFromMe; std::map entriesPerDerived; std::set wroteNode; wroteNode.insert(fCurrentClass); static const unsigned int maxClassesPerDerived = 20; fHtml->GetDerivedClasses(fCurrentClass, derivesFromMe); outdot << "{" << endl; for (Int_t level = 1; kTRUE; ++level) { Bool_t levelExists = kFALSE; for (std::map::iterator iDerived = derivesFromMe.begin(); iDerived != derivesFromMe.end(); ++iDerived) { if (iDerived->second != level) continue; levelExists = kTRUE; TIter iBaseOfDerived(iDerived->first->GetListOfBases()); TBaseClass* baseDerived = 0; Bool_t writeNode = kFALSE; TClass* writeAndMoreFor = 0; while ((baseDerived = (TBaseClass*) iBaseOfDerived())) { TClass* clBaseDerived = baseDerived->GetClassPointer(); if (clBaseDerived->InheritsFrom(fCurrentClass) && wroteNode.find(clBaseDerived) != wroteNode.end()) { unsigned int& count = entriesPerDerived[clBaseDerived]; if (count < maxClassesPerDerived) { writeNode = kTRUE; ssDep << "\"" << iDerived->first->GetName() << "\" -> \"" << clBaseDerived->GetName() << "\";" << endl; ++count; } else if (count == maxClassesPerDerived) { writeAndMoreFor = clBaseDerived; ssDep << "\"...andmore" << clBaseDerived->GetName() << "\"-> \"" << clBaseDerived->GetName() << "\";" << endl; ++count; } } } if (writeNode) { wroteNode.insert(iDerived->first); outdot << " \"" << iDerived->first->GetName() << "\""; const char* htmlFileName = fHtml->GetHtmlFileName(iDerived->first->GetName()); if (htmlFileName) outdot << " [URL=\"" << htmlFileName << "\"]"; outdot << ";" << endl; } else if (writeAndMoreFor) { outdot << " \"...andmore" << writeAndMoreFor->GetName() << "\" [label=\"...and more\",fontname=\"Times-Italic\",fillcolor=lightgrey,style=filled];" << endl; } } if (!levelExists) break; } outdot << "}" << endl; // cluster outdot << ssDep.str(); outdot << "}" << endl; // digraph return kTRUE; } //______________________________________________________________________________ Bool_t TClassDocOutput::CreateDotClassChartInhMem(const char* filename) { // Build the class tree of inherited members for one class in GraphViz/Dot format // // Input: filename - output dot file incl. path std::ofstream outdot(filename); outdot << "strict digraph G {" << endl << "ratio=auto;" << endl << "rankdir=RL;" << endl << "compound=true;" << endl << "constraint=false;" << endl << "ranksep=0.1;" << endl << "nodesep=0;" << endl << "margin=0;" << endl; outdot << " node [style=filled,width=0.7,height=0.15,fixedsize=true,shape=plaintext,fontsize=10];" << endl; std::stringstream ssDep; const int numColumns = 3; std::list writeBasesFor; writeBasesFor.push_back(fCurrentClass); while (!writeBasesFor.empty()) { TClass* cl = writeBasesFor.front(); writeBasesFor.pop_front(); const char* htmlFileName = fHtml->GetHtmlFileName(cl->GetName()); outdot << "subgraph \"cluster" << cl->GetName() << "\" {" << endl << " color=lightgray;" << endl << " label=\"" << cl->GetName() << "\";" << endl; if (cl != fCurrentClass && htmlFileName) outdot << " URL=\"" << htmlFileName << "\"" << endl; //Bool_t haveMembers = (cl->GetListOfDataMembers() && cl->GetListOfDataMembers()->GetSize()); Bool_t haveFuncs = cl->GetListOfMethods() && cl->GetListOfMethods()->GetSize(); // DATA MEMBERS { // make sure each member name is listed only once // that's useless for data members, but symmetric to what we have for methods std::map dmMap; { TIter iDM(cl->GetListOfDataMembers()); TDataMember* dm = 0; while ((dm = (TDataMember*) iDM())) dmMap[dm->GetName()] = dm; } outdot << "subgraph \"clusterData0" << cl->GetName() << "\" {" << endl << " color=white;" << endl << " label=\"\";" << endl << " \"clusterNode0" << cl->GetName() << "\" [height=0,width=0,style=invis];" << endl; TString prevColumnNode; Int_t pos = dmMap.size(); Int_t column = 0; Int_t newColumnEvery = (pos + numColumns - 1) / numColumns; for (std::map::iterator iDM = dmMap.begin(); iDM != dmMap.end(); ++iDM, --pos) { TDataMember* dm = iDM->second; TString nodeName(cl->GetName()); nodeName += "::"; nodeName += dm->GetName(); if (iDM == dmMap.begin()) prevColumnNode = nodeName; outdot << "\"" << nodeName << "\" [label=\"" << dm->GetName() << "\""; if (dm->Property() & kIsPrivate) outdot << ",color=\"#FFCCCC\""; else if (dm->Property() & kIsProtected) outdot << ",color=\"#FFFF77\""; else outdot << ",color=\"#CCFFCC\""; outdot << "];" << endl; if (pos % newColumnEvery == 1) { ++column; outdot << "};" << endl // end dataR << "subgraph \"clusterData" << column << cl->GetName() << "\" {" << endl << " color=white;" << endl << " label=\"\";" << endl << " \"clusterNode" << column << cl->GetName() << "\" [height=0,width=0,style=invis];" << endl; } else if (iDM != dmMap.begin() && pos % newColumnEvery == 0) { ssDep << "\"" << prevColumnNode << "\" -> \"" << nodeName << "\""<< " [style=invis,weight=100];" << endl; prevColumnNode = nodeName; } } while (column < numColumns - 1) { ++column; outdot << " \"clusterNode" << column << cl->GetName() << "\" [height=0,width=0,style=invis];" << endl; } outdot << "};" << endl; // subgraph dataL/R } // DATA MEMBERS // FUNCTION MEMBERS if (haveFuncs) { // make sure each member name is listed only once std::map methMap; { TIter iMeth(cl->GetListOfMethods()); TMethod* meth = 0; while ((meth = (TMethod*) iMeth())) methMap[meth->GetName()] = meth; } outdot << "subgraph \"clusterFunc0" << cl->GetName() << "\" {" << endl << " color=white;" << endl << " label=\"\";" << endl << " \"clusterNode0" << cl->GetName() << "\" [height=0,width=0,style=invis];" << endl; TString prevColumnNodeFunc; Int_t pos = methMap.size(); Int_t column = 0; Int_t newColumnEvery = (pos + numColumns - 1) / numColumns; for (std::map::iterator iMeth = methMap.begin(); iMeth != methMap.end(); ++iMeth, --pos) { TMethod* meth = iMeth->second; TString nodeName(cl->GetName()); nodeName += "::"; nodeName += meth->GetName(); if (iMeth == methMap.begin()) prevColumnNodeFunc = nodeName; outdot << "\"" << nodeName << "\" [label=\"" << meth->GetName() << "\""; if (cl != fCurrentClass && fCurrentClass->GetMethodAny(meth->GetName())) outdot << ",color=\"#777777\""; else if (meth->Property() & kIsPrivate) outdot << ",color=\"#FFCCCC\""; else if (meth->Property() & kIsProtected) outdot << ",color=\"#FFFF77\""; else outdot << ",color=\"#CCFFCC\""; outdot << "];" << endl; if (pos % newColumnEvery == 1) { ++column; outdot << "};" << endl // end funcR << "subgraph \"clusterFunc" << column << cl->GetName() << "\" {" << endl << " color=white;" << endl << " label=\"\";" << endl; } else if (iMeth != methMap.begin() && pos % newColumnEvery == 0) { ssDep << "\"" << prevColumnNodeFunc << "\" -> \"" << nodeName << "\""<< " [style=invis,weight=100];" << endl; prevColumnNodeFunc = nodeName; } } outdot << "};" << endl; // subgraph funcL/R } outdot << "}" << endl; // cluster class for (Int_t pos = 0; pos < numColumns - 1; ++pos) ssDep << "\"clusterNode" << pos << cl->GetName() << "\" -> \"clusterNode" << pos + 1 << cl->GetName() << "\" [style=invis];" << endl; if (cl->GetListOfBases() && cl->GetListOfBases()->GetSize()) { TIter iBase(cl->GetListOfBases()); TBaseClass* base = 0; while ((base = (TBaseClass*)iBase())) { ssDep << " \"clusterNode" << numColumns - 1 << cl->GetName() << "\" -> " << " \"clusterNode0" << base->GetName() << "\" [ltail=\"cluster" << cl->GetName() << "\",lhead=\"cluster" << base->GetName() << "\""; if (base != cl->GetListOfBases()->First()) ssDep << ",weight=0"; ssDep << "];" << endl; writeBasesFor.push_back(base->GetClassPointer()); } } } outdot << ssDep.str(); outdot << "}" << endl; // digraph return kTRUE; } //______________________________________________________________________________ Bool_t TClassDocOutput::CreateDotClassChartIncl(const char* filename) { // Build the include dependency graph for one class in // GraphViz/Dot format // // Input: filename - output dot file incl. path R__LOCKGUARD(GetHtml()->GetMakeClassMutex()); std::map filesToParse; std::list listFilesToParse; TString declFileName; TString implFileName; fHtml->GetImplFileName(fCurrentClass, kFALSE, implFileName); if (fHtml->GetDeclFileName(fCurrentClass, kFALSE, declFileName)) { TString real; if (fHtml->GetDeclFileName(fCurrentClass, kTRUE, real)) { filesToParse[declFileName.Data()] = real.Data(); listFilesToParse.push_back(declFileName.Data()); } } /* do it only for the header if (implFileName && strlen(implFileName)) { char* real = gSystem->Which(fHtml->GetInputPath(), implFileName, kReadPermission); if (real) { filesToParse[implFileName] = real; listFilesToParse.push_back(implFileName); delete real; } } */ std::ofstream outdot(filename); outdot << "strict digraph G {" << endl << "ratio=compress;" << endl << "rankdir=TB;" << endl << "concentrate=true;" << endl << "ranksep=0;" << endl << "nodesep=0;" << endl << "size=\"8,10\";" << endl << "node [fontsize=20,shape=plaintext];" << endl; for (std::list::iterator iFile = listFilesToParse.begin(); iFile != listFilesToParse.end(); ++iFile) { ifstream in(filesToParse[*iFile].c_str()); std::string line; while (in && !in.eof()) { std::getline(in, line); size_t pos = 0; while (line[pos] == ' ' || line[pos] == '\t') ++pos; if (line[pos] != '#') continue; ++pos; while (line[pos] == ' ' || line[pos] == '\t') ++pos; if (line.compare(pos, 8, "include ") != 0) continue; pos += 8; while (line[pos] == ' ' || line[pos] == '\t') ++pos; if (line[pos] != '"' && line[pos] != '<') continue; char delim = line[pos]; if (delim == '<') delim = '>'; ++pos; line.erase(0, pos); pos = 0; pos = line.find(delim); if (pos == std::string::npos) continue; line.erase(pos); if (filesToParse.find(line) == filesToParse.end()) { TString sysfilename; if (!GetHtml()->GetPathDefinition().GetFileNameFromInclude(line.c_str(), sysfilename)) continue; listFilesToParse.push_back(line); filesToParse[line] = sysfilename; if (*iFile == implFileName.Data() || *iFile == declFileName.Data()) outdot << "\"" << *iFile << "\" [style=filled,fillcolor=lightgray];" << endl; } outdot << "\"" << *iFile << "\" -> \"" << line << "\";" << endl; } } outdot << "}" << endl; // digraph return kTRUE; } //______________________________________________________________________________ Bool_t TClassDocOutput::CreateDotClassChartLib(const char* filename) { // Build the library dependency graph for one class in // GraphViz/Dot format // // Input: filename - output dot file incl. path std::ofstream outdot(filename); outdot << "strict digraph G {" << endl << "ratio=auto;" << endl << "rankdir=RL;" << endl << "compound=true;" << endl << "constraint=false;" << endl << "ranksep=0.7;" << endl << "nodesep=0.3;" << endl << "size=\"8,8\";" << endl << "ratio=compress;" << endl; TString libs(fCurrentClass->GetSharedLibs()); outdot << "\"All Libraries\" [URL=\"LibraryDependencies.html\",shape=box,rank=max,fillcolor=lightgray,style=filled];" << endl; if (libs.Length()) { TString firstLib(libs); Ssiz_t end = firstLib.Index(' '); if (end != kNPOS) { firstLib.Remove(end, firstLib.Length()); libs.Remove(0, end + 1); } else libs = ""; { Ssiz_t posExt = firstLib.First("."); if (posExt != kNPOS) firstLib.Remove(posExt, firstLib.Length()); } outdot << "\"All Libraries\" -> \"" << firstLib << "\" [style=invis];" << endl; outdot << "\"" << firstLib << "\" -> {" << endl; if (firstLib != "libCore") libs += " libCore"; if (firstLib != "libCint") libs += " libCint"; TString thisLib; for (Ssiz_t pos = 0; pos < libs.Length(); ++pos) if (libs[pos] != ' ') thisLib += libs[pos]; else if (thisLib.Length()) { Ssiz_t posExt = thisLib.First("."); if (posExt != kNPOS) thisLib.Remove(posExt, thisLib.Length()); outdot << " \"" << thisLib << "\";"; thisLib = ""; } // remaining lib if (thisLib.Length()) { Ssiz_t posExt = thisLib.First("."); if (posExt != kNPOS) thisLib.Remove(posExt, thisLib.Length()); outdot << " \"" << thisLib << "\";"; thisLib = ""; } outdot << "}" << endl; // dependencies } else outdot << "\"No rlibmap information available.\"" << endl; outdot << "}" << endl; // digraph return kTRUE; } //______________________________________________________________________________ void TClassDocOutput::CreateClassHierarchy(std::ostream& out, const char* docFileName) { // Create the hierarchical class list part for the current class's // base classes. docFileName contains doc for fCurrentClass. // // Find basic base classes TList *bases = fCurrentClass->GetListOfBases(); if (!bases || bases->IsEmpty()) return; out << "
" << endl; out << ""; fHierarchyLines = 0; DescendHierarchy(out, fCurrentClass); out << "
" << endl; } //______________________________________________________________________________ Bool_t TClassDocOutput::CreateHierarchyDot() { // Create a hierarchical class list // The algorithm descends from the base classes and branches into // all derived classes. Mixing classes are displayed several times. // // const char* title = "ClassHierarchy"; TString filename(title); gSystem->PrependPathName(fHtml->GetOutputDir(), filename); // open out file std::ofstream dotout(filename + ".dot"); if (!dotout.good()) { Error("CreateHierarchy", "Can't open file '%s.dot' !", filename.Data()); return kFALSE; } dotout << "digraph G {" << endl << "ratio=auto;" << endl << "rankdir=RL;" << endl; // loop on all classes TClassDocInfo* cdi = 0; TIter iClass(fHtml->GetListOfClasses()); while ((cdi = (TClassDocInfo*)iClass())) { TDictionary *dict = cdi->GetClass(); TClass *cl = dynamic_cast(dict); if (cl == 0) { if (!dict) Warning("THtml::CreateHierarchy", "skipping class %s\n", cdi->GetName()); continue; } // Find immediate base classes TList *bases = cl->GetListOfBases(); if (bases && !bases->IsEmpty()) { dotout << "\"" << cdi->GetName() << "\" -> { "; TIter iBase(bases); TBaseClass* base = 0; while ((base = (TBaseClass*) iBase())) { // write out current class if (base != bases->First()) dotout << "; "; dotout << "\"" << base->GetName() << "\""; } dotout << "};" << endl; } else // write out current class - no bases dotout << "\"" << cdi->GetName() << "\";" << endl; } dotout << "}"; dotout.close(); std::ofstream out(filename + ".html"); if (!out.good()) { Error("CreateHierarchy", "Can't open file '%s.html' !", filename.Data()); return kFALSE; } Printf(fHtml->GetCounterFormat(), "", fHtml->GetCounter(), (filename + ".html").Data()); // write out header WriteHtmlHeader(out, "Class Hierarchy"); out << "

Class Hierarchy

" << endl; WriteSearch(out); RunDot(filename, &out); out << "" << endl; // write out footer WriteHtmlFooter(out); return kTRUE; } //______________________________________________________________________________ void TClassDocOutput::CreateSourceOutputStream(std::ostream& out, const char* extension, TString& sourceHtmlFileName) { // Open a Class.cxx.html file, where Class is defined by classPtr, and .cxx.html by extension // It's created in fHtml->GetOutputDir()/src. If successful, the HTML header is written to out. TString sourceHtmlDir("src"); gSystem->PrependPathName(fHtml->GetOutputDir(), sourceHtmlDir); // create directory if necessary { R__LOCKGUARD(GetHtml()->GetMakeClassMutex()); if (gSystem->AccessPathName(sourceHtmlDir)) gSystem->MakeDirectory(sourceHtmlDir); } sourceHtmlFileName = fCurrentClass->GetName(); NameSpace2FileName(sourceHtmlFileName); gSystem->PrependPathName(sourceHtmlDir, sourceHtmlFileName); sourceHtmlFileName += extension; dynamic_cast(out).open(sourceHtmlFileName); if (!out) { Warning("LocateMethodsInSource", "Can't open beautified source file '%s' for writing!", sourceHtmlFileName.Data()); sourceHtmlFileName.Remove(0); return; } // write a HTML header TString title(fCurrentClass->GetName()); title += " - source file"; WriteHtmlHeader(out, title, "../", fCurrentClass); out << "
" << std::endl;
}

//______________________________________________________________________________
void TClassDocOutput::DescendHierarchy(std::ostream& out, TClass* basePtr, Int_t maxLines, Int_t depth)
{
// Descend hierarchy recursively
// loop over all classes and look for classes with base class basePtr

   if (maxLines)
      if (fHierarchyLines >= maxLines) {
         out << "" << endl;
         return;
      }

   UInt_t numClasses = 0;

   TClassDocInfo* cdi = 0;
   TIter iClass(fHtml->GetListOfClasses());
   while ((cdi = (TClassDocInfo*)iClass()) && (!maxLines || fHierarchyLines(cdi->GetClass());
      if (!classPtr) continue;

      // find base classes with same name as basePtr
      TList* bases=classPtr->GetListOfBases();
      if (!bases) continue;

      TBaseClass *inheritFrom=(TBaseClass*)bases->FindObject(basePtr->GetName());
      if (!inheritFrom) continue;

      if (!numClasses)
         out << "←" << endl;
      else
         out << ""<";
      out << "
" << endl; DescendHierarchy(out,classPtr,maxLines, depth+1); out << "
" << endl; TString htmlFile(cdi->GetHtmlFileName()); if (htmlFile.Length()) { out << "
GetName() << "\" href=\"" << htmlFile << "\">"; ReplaceSpecialChars(out, cdi->GetName()); out << "
"; } else { ReplaceSpecialChars(out, cdi->GetName()); } // write title // commented out for now because it reduces overview /* len = strlen(classNames[i]); for (Int_t w = 0; w < (maxLen - len + 2); w++) out << "."; out << " "; out << "GetName(); out << "\">"; ReplaceSpecialChars(out, classPtr->GetTitle()); out << "" << endl; */ out << "
" << endl; } // loop over all classes if (numClasses) out << "" << endl; else out << "" << endl; } //______________________________________________________________________________ void TClassDocOutput::MakeTree(Bool_t force /*= kFALSE*/) { // Create an output file with a graphical representation of the class // inheritance. If force, replace existing output file. // This routine does nothing if fHtml->HaveDot() is true - use // ClassDotCharts() instead! // class tree only if no dot, otherwise it's part of charts if (!fCurrentClass || fHtml->HaveDot()) return; TString htmlFile; fHtml->GetHtmlFileName(fCurrentClass, htmlFile); if (htmlFile.Length() && (htmlFile.BeginsWith("http://") || htmlFile.BeginsWith("https://") || gSystem->IsAbsoluteFileName(htmlFile)) ) { htmlFile.Remove(0); } if (!htmlFile.Length()) { TString what(fCurrentClass->GetName()); what += " (source not found)"; Printf(fHtml->GetCounterFormat(), "-skipped-", "", what.Data()); return; } R__LOCKGUARD(GetHtml()->GetMakeClassMutex()); // Create a canvas without linking against GUI libs Bool_t wasBatch = gROOT->IsBatch(); if (!wasBatch) gROOT->SetBatch(); TVirtualPad *psCanvas = (TVirtualPad*)gROOT->ProcessLineFast("new TCanvas(\"R__THtml\",\"psCanvas\",0,0,1000,1200);"); if (!wasBatch) gROOT->SetBatch(kFALSE); if (!psCanvas) { Error("MakeTree", "Cannot create a TCanvas!"); return; } // make a class tree ClassTree(psCanvas, force); psCanvas->Close(); delete psCanvas; } //______________________________________________________________________________ void TClassDocOutput::WriteClassDescription(std::ostream& out, const TString& description) { // Called by TDocParser::LocateMethods(), this hook writes out the class description // found by TDocParser. It's even called if none is found, i.e. if the first method // has occurred before a class description is found, so missing class descriptions // can be handled. // For HTML, its creates the description block, the list of functions and data // members, and the inheritance tree or, if Graphviz's dot is found, the class charts. // Class Description Title out << "
"; TString anchor(fCurrentClass->GetName()); NameSpace2FileName(anchor); out << "

"; if (fHtml->IsNamespace(fCurrentClass)) out << "namespace "; else out << "class "; ReplaceSpecialChars(out, fCurrentClass->GetName()); // make a loop on base classes Bool_t first = kTRUE; TBaseClass *inheritFrom; TIter nextBase(fCurrentClass->GetListOfBases()); while ((inheritFrom = (TBaseClass *) nextBase())) { if (first) { out << ": "; first = kFALSE; } else out << ", "; Long_t property = inheritFrom->Property(); if (property & kIsPrivate) out << "private "; else if (property & kIsProtected) out << "protected "; else out << "public "; // get a class TClass *classInh = fHtml->GetClass(inheritFrom->GetName()); TString htmlFile; fHtml->GetHtmlFileName(classInh, htmlFile); if (htmlFile.Length()) { // make a link to the base class out << ""; ReplaceSpecialChars(out, inheritFrom->GetName()); out << ""; } else ReplaceSpecialChars(out, inheritFrom->GetName()); } out << "

" << endl; out << "
" << endl; if (description.Length()) out << "
" << description << "
"; // typedefs pointing to this class: if (fCurrentClassesTypedefs && !fCurrentClassesTypedefs->IsEmpty()) { out << "

This class is also known as (typedefs to this class)

"; TIter iTD(fCurrentClassesTypedefs); bool firsttd = true; TDataType* dt = 0; while ((dt = (TDataType*) iTD())) { if (!firsttd) out << ", "; else firsttd = false; fParser->DecorateKeywords(out, dt->GetName()); } } out << "
" << std::endl << "
" << std::endl; ListFunctions(out); ListDataMembers(out); // create dot class charts or an html inheritance tree out << "

Class Charts

" << endl; if (!fHtml->IsNamespace(fCurrentClass)) if (!ClassDotCharts(out)) ClassHtmlTree(out, fCurrentClass); // header for the following function docs: out << "

Function documentation

" << endl; } //______________________________________________________________________________ void TClassDocOutput::WriteClassDocHeader(std::ostream& classFile) { // Write out the introduction of a class description (shortcuts and links) classFile << "" << endl; // show box with lib, include // needs to go first to allow title on the left TString sTitle(fCurrentClass->GetName()); ReplaceSpecialChars(sTitle); if (fHtml->IsNamespace(fCurrentClass)) sTitle.Prepend("namespace "); else sTitle.Prepend("class "); TString sInclude; TString sLib; const char* lib=fCurrentClass->GetSharedLibs(); GetHtml()->GetPathDefinition().GetIncludeAs(fCurrentClass, sInclude); if (lib) { char* libDup=StrDup(lib); char* libDupSpace=strchr(libDup,' '); if (libDupSpace) *libDupSpace=0; char* libDupEnd=libDup+strlen(libDup); while (libDupEnd!=libDup) if (*(--libDupEnd)=='.') { *libDupEnd=0; break; } sLib = libDup; delete[] libDup; } classFile << "" << endl; TString modulename; fHtml->GetModuleNameForClass(modulename, fCurrentClass); TModuleDocInfo* module = (TModuleDocInfo*) fHtml->GetListOfModules()->FindObject(modulename); WriteTopLinks(classFile, module, fCurrentClass->GetName(), kFALSE); classFile << "
" << endl // descrhead line 3 << "Source:" << endl; // make a link to the '.cxx' file TString classFileName(fCurrentClass->GetName()); NameSpace2FileName(classFileName); TString headerFileName; fHtml->GetDeclFileName(fCurrentClass, kFALSE, headerFileName); TString sourceFileName; fHtml->GetImplFileName(fCurrentClass, kFALSE, sourceFileName); if (headerFileName.Length()) classFile << "header file" << endl; else classFile << " " << endl; if (sourceFileName.Length()) classFile << "source file" << endl; else classFile << " " << endl; if (!fHtml->IsNamespace(fCurrentClass) && !fHtml->HaveDot()) { // make a link to the inheritance tree (postscript) classFile << "inheritance tree (.pdf) "; } const TString& viewCVSLink = GetHtml()->GetViewCVS(); Bool_t mustReplace = viewCVSLink.Contains("%f"); if (viewCVSLink.Length()) { if (headerFileName.Length()) { TString link(viewCVSLink); TString sHeader(headerFileName); if (GetHtml()->GetProductName() && !strcmp(GetHtml()->GetProductName(), "ROOT")) { Ssiz_t posInclude = sHeader.Index("/include/"); if (posInclude != kNPOS) { // Cut off ".../include", i.e. keep leading '/' sHeader.Remove(0, posInclude + 8); } else { // no /include/; maybe /inc? posInclude = sHeader.Index("/inc/"); if (posInclude != kNPOS) { sHeader = "/"; sHeader += sInclude; } } if (sourceFileName && strstr(sourceFileName, "src")) { TString src(sourceFileName); src.Remove(src.Index("src"), src.Length()); src += "inc"; sHeader.Prepend(src); } else { TString src(fCurrentClass->GetSharedLibs()); Ssiz_t posEndLib = src.Index(' '); if (posEndLib != kNPOS) src.Remove(posEndLib, src.Length()); if (src.BeginsWith("lib")) src.Remove(0, 3); posEndLib = src.Index('.'); if (posEndLib != kNPOS) src.Remove(posEndLib, src.Length()); src.ToLower(); src += "/inc"; sHeader.Prepend(src); } if (sHeader.BeginsWith("tmva/inc/TMVA")) sHeader.Remove(8, 5); } if (mustReplace) link.ReplaceAll("%f", sHeader); else link += sHeader; classFile << "viewVC header "; } else classFile << " "; if (sourceFileName.Length()) { TString link(viewCVSLink); if (mustReplace) link.ReplaceAll("%f", sourceFileName); else link += sourceFileName; classFile << "viewVC source "; } else classFile << " "; } TString currClassNameMangled(fCurrentClass->GetName()); NameSpace2FileName(currClassNameMangled); TString wikiLink = GetHtml()->GetWikiURL(); if (wikiLink.Length()) { if (wikiLink.Contains("%c")) wikiLink.ReplaceAll("%c", currClassNameMangled); else wikiLink += currClassNameMangled; classFile << "wiki "; } classFile << endl << "
" << endl; // descrhead line 3 classFile << "
" << endl // descrhead line 4 << "Sections:" << endl << "IsNamespace(fCurrentClass)) classFile << ":description\">namespace description "; else classFile << ":description\">class description "; classFile << endl << "function members" << endl << "data members" << endl << "class charts" << endl << "
" << endl // descrhead line 4 << "
" << endl; // toplinks, from TDocOutput::WriteTopLinks WriteLocation(classFile, module, fCurrentClass->GetName()); } //______________________________________________________________________________ void TClassDocOutput::WriteMethod(std::ostream& out, TString& ret, TString& name, TString& params, const char* filename, TString& anchor, TString& comment, TString& codeOneLiner, TDocMethodWrapper* guessedMethod) { // Write method name with return type ret and parameters param to out. // Build a link using file and anchor. Cooment it with comment, and // show the code codeOneLiner (set if the func consists of only one line // of code, immediately surrounded by "{","}"). Also updates fMethodNames's // count of method names. fParser->DecorateKeywords(ret); out << "
" << ret << " GetName()); NameSpace2FileName(mangled); out << mangled << ":"; mangled = name; NameSpace2FileName(mangled); if (guessedMethod && guessedMethod->GetOverloadIdx()) { mangled += "@"; mangled += guessedMethod->GetOverloadIdx(); } out << mangled << "\" href=\"src/" << filename; if (anchor.Length()) out << "#" << anchor; out << "\">"; ReplaceSpecialChars(out, name); out << ""; if (guessedMethod) { out << "("; TMethodArg* arg; TIter iParam(guessedMethod->GetMethod()->GetListOfMethodArgs()); Bool_t first = kTRUE; while ((arg = (TMethodArg*) iParam())) { if (!first) out << ", "; else first = kFALSE; TString paramGuessed(arg->GetFullTypeName()); paramGuessed += " "; paramGuessed += arg->GetName(); if (arg->GetDefault() && strlen(arg->GetDefault())) { paramGuessed += " = "; paramGuessed += arg->GetDefault(); } fParser->DecorateKeywords(paramGuessed); out << paramGuessed; } out << ")"; if (guessedMethod->GetMethod()->Property() & kIsMethConst) out << " const"; } else { fParser->DecorateKeywords(params); out << params; } out << "
" << std::endl; if (comment.Length()) out << "
" << comment << "
" << std::endl; if (codeOneLiner.Length()) { out << std::endl << "
" << codeOneLiner << "
" << std::endl << "
" << std::endl; codeOneLiner.Remove(0); } out << "
" << std::endl; }