/******************************************************************************
*
* Copyright (C) 1997-2008 by Dimitri van Heesch.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation under the terms of the GNU General Public License is hereby
* granted. No representations are made about the suitability of this software
* for any purpose. It is provided "as is" without express or implied warranty.
* See the GNU General Public License for more details.
*
* Documents produced by Doxygen are derivative works derived from the
* input used in their production; they are not affected by this license.
*
*/
#include "qtbc.h"
#include <qfileinfo.h>
#include <qfile.h>
#include <qdir.h>
#include <qdict.h>
#include <qregexp.h>
#include <qstrlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <qtextcodec.h>
#include <unistd.h>
#include <errno.h>
#include "version.h"
#include "doxygen.h"
#include "scanner.h"
#include "entry.h"
#include "index.h"
#include "logos.h"
#include "instdox.h"
#include "message.h"
#include "config.h"
#include "util.h"
#include "pre.h"
#include "tagreader.h"
#include "dot.h"
#include "docparser.h"
#include "dirdef.h"
#include "outputlist.h"
#include "declinfo.h"
#include "htmlgen.h"
#include "latexgen.h"
#include "mangen.h"
#include "language.h"
#include "debug.h"
#include "htmlhelp.h"
#include "qhp.h"
#include "indexlog.h"
#include "ftvhelp.h"
#include "defargs.h"
#include "rtfgen.h"
#include "xmlgen.h"
#include "xmlditagen.h"
#include "defgen.h"
#include "perlmodgen.h"
#include "reflist.h"
#include "pagedef.h"
#include "bufstr.h"
#include "commentcnv.h"
#include "cmdmapper.h"
#include "searchindex.h"
#include "parserintf.h"
#include "htags.h"
#include "pyscanner.h"
#include "fortranscanner.h"
#include "dbusxmlscanner.h"
#include "code.h"
#include "objcache.h"
#include "store.h"
#include "marshal.h"
#include "portable.h"
#include "vhdlscanner.h"
#include "vhdldocgen.h"
#include "eclipsehelp.h"
#include "layout.h"
#define RECURSE_ENTRYTREE(func,var) \
do { if (var->children()) { \
EntryNavListIterator eli(*var->children()); \
for (;eli.current();++eli) func(eli.current()); \
} } while(0)
#if !defined(_WIN32) || defined(__CYGWIN__)
#include <signal.h>
#define HAS_SIGNALS
#endif
// globally accessible variables
ClassSDict *Doxygen::classSDict = 0;
ClassSDict *Doxygen::hiddenClasses = 0;
NamespaceSDict *Doxygen::namespaceSDict = 0;
MemberNameSDict *Doxygen::memberNameSDict = 0;
MemberNameSDict *Doxygen::functionNameSDict = 0;
FileNameList *Doxygen::inputNameList = 0; // all input files
FileNameDict *Doxygen::inputNameDict = 0;
GroupSDict *Doxygen::groupSDict = 0;
FormulaList Doxygen::formulaList; // all formulas
FormulaDict Doxygen::formulaDict(1009); // all formulas
FormulaDict Doxygen::formulaNameDict(1009); // the label name of all formulas
PageSDict *Doxygen::pageSDict = 0;
PageSDict *Doxygen::exampleSDict = 0;
SectionDict Doxygen::sectionDict(257); // all page sections
StringDict Doxygen::aliasDict(257); // aliases
FileNameDict *Doxygen::includeNameDict = 0; // include names
FileNameDict *Doxygen::exampleNameDict = 0; // examples
FileNameDict *Doxygen::imageNameDict = 0; // images
FileNameDict *Doxygen::dotFileNameDict = 0; // dot files
StringDict Doxygen::namespaceAliasDict(257); // all namespace aliases
StringDict Doxygen::tagDestinationDict(257); // all tag locations
QDict<void> Doxygen::expandAsDefinedDict(257); // all macros that should be expanded
QIntDict<MemberGroupInfo> Doxygen::memGrpInfoDict(1009); // dictionary of the member groups heading
PageDef *Doxygen::mainPage = 0;
bool Doxygen::insideMainPage = FALSE; // are we generating docs for the main page?
QTextStream Doxygen::tagFile;
NamespaceDef *Doxygen::globalScope = 0;
QDict<RefList> *Doxygen::xrefLists = new QDict<RefList>; // dictionary of cross-referenced item lists
bool Doxygen::parseSourcesNeeded = FALSE;
QTime Doxygen::runningTime;
SearchIndex * Doxygen::searchIndex=0;
QDict<DefinitionIntf> *Doxygen::symbolMap;
bool Doxygen::outputToWizard=FALSE;
QDict<int> * Doxygen::htmlDirMap = 0;
QCache<LookupInfo> Doxygen::lookupCache(50000,50000);
DirSDict *Doxygen::directories;
SDict<DirRelation> Doxygen::dirRelations(257);
ParserManager *Doxygen::parserManager = 0;
QCString Doxygen::htmlFileExtension;
bool Doxygen::suppressDocWarnings = FALSE;
ObjCache *Doxygen::symbolCache = 0;
Store *Doxygen::symbolStorage;
QCString Doxygen::objDBFileName;
QCString Doxygen::entryDBFileName;
bool Doxygen::gatherDefines = TRUE;
IndexList Doxygen::indexList;
int Doxygen::subpageNestingLevel = 0;
bool Doxygen::userComments = FALSE;
QCString Doxygen::spaces;
// locally accessible globals
static QDict<EntryNav> g_classEntries(1009);
static StringList g_inputFiles;
static QDict<void> g_compoundKeywordDict(7); // keywords recognised as compounds
static OutputList *g_outputList = 0; // list of output generating objects
static QDict<FileDef> g_usingDeclarations(1009); // used classes
static FileStorage *g_storage = 0;
static bool g_successfulRun = FALSE;
static bool g_dumpSymbolMap = FALSE;
static bool g_dumpConfigAsXML = FALSE;
void clearAll()
{
g_inputFiles.clear();
//g_excludeNameDict.clear();
//delete g_outputList; g_outputList=0;
Doxygen::classSDict->clear();
Doxygen::namespaceSDict->clear();
Doxygen::pageSDict->clear();
Doxygen::exampleSDict->clear();
Doxygen::inputNameList->clear();
Doxygen::formulaList.clear();
Doxygen::sectionDict.clear();
Doxygen::inputNameDict->clear();
Doxygen::includeNameDict->clear();
Doxygen::exampleNameDict->clear();
Doxygen::imageNameDict->clear();
Doxygen::dotFileNameDict->clear();
Doxygen::formulaDict.clear();
Doxygen::formulaNameDict.clear();
Doxygen::tagDestinationDict.clear();
delete Doxygen::mainPage; Doxygen::mainPage=0;
}
void statistics()
{
fprintf(stderr,"--- inputNameDict stats ----\n");
Doxygen::inputNameDict->statistics();
fprintf(stderr,"--- includeNameDict stats ----\n");
Doxygen::includeNameDict->statistics();
fprintf(stderr,"--- exampleNameDict stats ----\n");
Doxygen::exampleNameDict->statistics();
fprintf(stderr,"--- imageNameDict stats ----\n");
Doxygen::imageNameDict->statistics();
fprintf(stderr,"--- dotFileNameDict stats ----\n");
Doxygen::dotFileNameDict->statistics();
//fprintf(stderr,"--- g_excludeNameDict stats ----\n");
//g_excludeNameDict.statistics();
fprintf(stderr,"--- aliasDict stats ----\n");
Doxygen::aliasDict.statistics();
fprintf(stderr,"--- typedefDict stats ----\n");
fprintf(stderr,"--- namespaceAliasDict stats ----\n");
Doxygen::namespaceAliasDict.statistics();
fprintf(stderr,"--- formulaDict stats ----\n");
Doxygen::formulaDict.statistics();
fprintf(stderr,"--- formulaNameDict stats ----\n");
Doxygen::formulaNameDict.statistics();
fprintf(stderr,"--- tagDestinationDict stats ----\n");
Doxygen::tagDestinationDict.statistics();
fprintf(stderr,"--- g_compoundKeywordDict stats ----\n");
g_compoundKeywordDict.statistics();
fprintf(stderr,"--- expandAsDefinedDict stats ----\n");
Doxygen::expandAsDefinedDict.statistics();
fprintf(stderr,"--- memGrpInfoDict stats ----\n");
Doxygen::memGrpInfoDict.statistics();
}
static void addMemberDocs(EntryNav *rootNav,MemberDef *md, const char *funcDecl,
ArgumentList *al,bool over_load,NamespaceSDict *nl=0);
static void findMember(EntryNav *rootNav,
QCString funcDecl,
bool overloaded,
bool isFunc
);
struct STLInfo
{
const char *className;
const char *baseClass1;
const char *baseClass2;
const char *templType1;
const char *templName1;
const char *templType2;
const char *templName2;
bool virtualInheritance;
bool iterators;
};
static STLInfo g_stlinfo[] =
{
// className baseClass1 baseClass2 templType1 templName1 templType2 templName2 virtInheritance // iterators
{ "allocator", 0, 0, "T", "elements", 0, 0, FALSE, FALSE },
{ "auto_ptr", 0, 0, "T", "ptr", 0, 0, FALSE, FALSE },
{ "ios_base", 0, 0, 0, 0, 0, 0, FALSE, FALSE },
{ "basic_ios", "ios_base", 0, "Char", 0, 0, 0, FALSE, FALSE },
{ "basic_istream", "basic_ios<Char>", 0, "Char", 0, 0, 0, TRUE, FALSE },
{ "basic_ostream", "basic_ios<Char>", 0, "Char", 0, 0, 0, TRUE, FALSE },
{ "basic_iostream", "basic_istream<Char>", "basic_ostream<Char>", "Char", 0, 0, 0, FALSE, FALSE },
{ "basic_ifstream", "basic_istream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE },
{ "basic_ofstream", "basic_ostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE },
{ "basic_fstream", "basic_iostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE },
{ "basic_istringstream", "basic_istream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE },
{ "basic_ostringstream", "basic_ostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE },
{ "basic_stringstream", "basic_iostream<Char>", 0, "Char", 0, 0, 0, FALSE, FALSE },
{ "ios", "basic_ios<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wios", "basic_ios<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "istream", "basic_istream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wistream", "basic_istream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "ostream", "basic_ostream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wostream", "basic_ostream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "ifstream", "basic_ifstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wifstream", "basic_ifstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "ofstream", "basic_ofstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wofstream", "basic_ofstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "fstream", "basic_fstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wfstream", "basic_fstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "istringstream", "basic_istringstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wistringstream", "basic_istringstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "ostringstream", "basic_ostringstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wostringstream", "basic_ostringstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "stringstream", "basic_stringstream<char>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "wstringstream", "basic_stringstream<wchar_t>", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "basic_string", 0, 0, "Char", 0, 0, 0, FALSE, TRUE },
{ "string", "basic_string<char>", 0, 0, 0, 0, 0, FALSE, TRUE },
{ "wstring", "basic_string<wchar_t>", 0, 0, 0, 0, 0, FALSE, TRUE },
{ "complex", 0, 0, 0, 0, 0, 0, FALSE, FALSE },
{ "bitset", 0, 0, "Bits", 0, 0, 0, FALSE, FALSE },
{ "deque", 0, 0, "T", "elements", 0, 0, FALSE, TRUE },
{ "list", 0, 0, "T", "elements", 0, 0, FALSE, TRUE },
{ "map", 0, 0, "K", "keys", "T", "elements", FALSE, TRUE },
{ "multimap", 0, 0, "K", "keys", "T", "elements", FALSE, TRUE },
{ "set", 0, 0, "K", "keys", 0, 0, FALSE, TRUE },
{ "multiset", 0, 0, "K", "keys", 0, 0, FALSE, TRUE },
{ "vector", 0, 0, "T", "elements", 0, 0, FALSE, TRUE },
{ "queue", 0, 0, "T", "elements", 0, 0, FALSE, FALSE },
{ "priority_queue", 0, 0, "T", "elements", 0, 0, FALSE, FALSE },
{ "stack", 0, 0, "T", "elements", 0, 0, FALSE, FALSE },
{ "valarray", 0, 0, "T", "elements", 0, 0, FALSE, FALSE },
{ "exception", 0, 0, 0, 0, 0, 0, FALSE, FALSE },
{ "bad_alloc", "exception", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "bad_cast", "exception", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "bad_typeid", "exception", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "logic_error", "exception", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "ios_base::failure", "exception", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "runtime_error", "exception", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "bad_exception", "exception", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "domain_error", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "invalid_argument", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "length_error", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "out_of_range", "logic_error", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "range_error", "runtime_error", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "overflow_error", "runtime_error", 0, 0, 0, 0, 0, FALSE, FALSE },
{ "underflow_error", "runtime_error", 0, 0, 0, 0, 0, FALSE, FALSE },
{ 0, 0, 0, 0, 0, 0, 0, FALSE, FALSE }
};
static void addSTLMember(EntryNav *rootNav,const char *type,const char *name)
{
Entry *memEntry = new Entry;
memEntry->name = name;
memEntry->type = type;
memEntry->protection = Private;
memEntry->section = Entry::VARIABLE_SEC;
memEntry->brief = "STL member";
memEntry->hidden = FALSE;
memEntry->artificial = TRUE;
//memEntry->parent = root;
//root->addSubEntry(memEntry);
EntryNav *memEntryNav = new EntryNav(rootNav,memEntry);
memEntryNav->setEntry(memEntry);
rootNav->addChild(memEntryNav);
}
static void addSTLIterator(EntryNav *classEntryNav,const char *name)
{
Entry *iteratorClassEntry = new Entry;
iteratorClassEntry->fileName = "[STL]";
iteratorClassEntry->startLine = 1;
iteratorClassEntry->name = name;
iteratorClassEntry->section = Entry::CLASS_SEC;
iteratorClassEntry->brief = "STL iterator class";
iteratorClassEntry->hidden = FALSE;
iteratorClassEntry->artificial= TRUE;
EntryNav *iteratorClassEntryNav = new EntryNav(classEntryNav,iteratorClassEntry);
iteratorClassEntryNav->setEntry(iteratorClassEntry);
classEntryNav->addChild(iteratorClassEntryNav);
}
static void addSTLClasses(EntryNav *rootNav)
{
Entry *namespaceEntry = new Entry;
namespaceEntry->fileName = "[STL]";
namespaceEntry->startLine = 1;
//namespaceEntry->parent = rootNav->entry();
namespaceEntry->name = "std";
namespaceEntry->section = Entry::NAMESPACE_SEC;
namespaceEntry->brief = "STL namespace";
namespaceEntry->hidden = FALSE;
namespaceEntry->artificial= TRUE;
//root->addSubEntry(namespaceEntry);
EntryNav *namespaceEntryNav = new EntryNav(rootNav,namespaceEntry);
namespaceEntryNav->setEntry(namespaceEntry);
rootNav->addChild(namespaceEntryNav);
STLInfo *info = g_stlinfo;
while (info->className)
{
//printf("Adding STL class %s\n",info->className);
QCString fullName = info->className;
fullName.prepend("std::");
// add fake Entry for the class
Entry *classEntry = new Entry;
classEntry->fileName = "[STL]";
classEntry->startLine = 1;
classEntry->name = fullName;
//classEntry->parent = namespaceEntry;
classEntry->section = Entry::CLASS_SEC;
classEntry->brief = "STL class";
classEntry->hidden = FALSE;
classEntry->artificial= TRUE;
//namespaceEntry->addSubEntry(classEntry);
EntryNav *classEntryNav = new EntryNav(namespaceEntryNav,classEntry);
classEntryNav->setEntry(classEntry);
namespaceEntryNav->addChild(classEntryNav);
// add template arguments to class
if (info->templType1)
{
ArgumentList *al = new ArgumentList;
Argument *a=new Argument;
a->type="typename";
a->name=info->templType1;
al->append(a);
if (info->templType2) // another template argument
{
a=new Argument;
a->type="typename";
a->name=info->templType2;
al->append(a);
}
classEntry->tArgLists = new QList<ArgumentList>;
classEntry->tArgLists->setAutoDelete(TRUE);
classEntry->tArgLists->append(al);
}
// add member variables
if (info->templName1)
{
addSTLMember(classEntryNav,info->templType1,info->templName1);
}
if (info->templName2)
{
addSTLMember(classEntryNav,info->templType2,info->templName2);
}
if (info->baseClass1)
{
classEntry->extends->append(new BaseInfo(info->baseClass1,Public,info->virtualInheritance?Virtual:Normal));
}
if (info->baseClass2)
{
classEntry->extends->append(new BaseInfo(info->baseClass2,Public,info->virtualInheritance?Virtual:Normal));
}
if (info->iterators)
{
// add iterator class
addSTLIterator(classEntryNav,fullName+"::iterator");
addSTLIterator(classEntryNav,fullName+"::const_iterator");
addSTLIterator(classEntryNav,fullName+"::reverse_iterator");
addSTLIterator(classEntryNav,fullName+"::const_reverse_iterator");
}
info++;
}
}
//----------------------------------------------------------------------------
static Definition *findScopeFromQualifiedName(Definition *startScope,const QCString &n,
FileDef *fileScope=0);
static void addPageToContext(PageDef *pd,EntryNav *rootNav)
{
if (rootNav->parent()) // add the page to it's scope
{
QCString scope = rootNav->parent()->name();
if (rootNav->parent()->section()==Entry::PACKAGEDOC_SEC)
{
scope=substitute(scope,".","::");
}
scope = stripAnonymousNamespaceScope(scope);
scope+="::"+pd->name();
Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope);
if (d)
{
pd->setPageScope(d);
}
}
}
static void addRelatedPage(EntryNav *rootNav)
{
Entry *root = rootNav->entry();
GroupDef *gd=0;
QListIterator<Grouping> gli(*root->groups);
Grouping *g;
for (;(g=gli.current());++gli)
{
if (!g->groupname.isEmpty() && (gd=Doxygen::groupSDict->find(g->groupname))) break;
}
//printf("---> addRelatedPage() %s gd=%p\n",root->name.data(),gd);
QCString doc;
if (root->brief.isEmpty())
{
doc=root->doc+root->inbodyDocs;
}
else
{
doc=root->brief+"\n\n"+root->doc+root->inbodyDocs;
}
PageDef *pd = addRelatedPage(root->name,root->args,doc,root->anchors,
root->fileName,root->startLine,
root->sli,
gd,rootNav->tagInfo()
);
if (pd)
{
pd->addSectionsToDefinition(root->anchors);
addPageToContext(pd,rootNav);
}
}
static void buildGroupListFiltered(EntryNav *rootNav,bool additional)
{
if (rootNav->section()==Entry::GROUPDOC_SEC && !rootNav->name().isEmpty())
{
//printf("Found group %s title=`%s type=%d'\n",
// root->name.data(),root->type.data(),root->groupDocType);
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
(root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
{
GroupDef *gd;
if ((gd=Doxygen::groupSDict->find(root->name)))
{
if ( !gd->hasGroupTitle() )
{
gd->setGroupTitle( root->type );
}
else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
{
warn( root->fileName,root->startLine,
"group %s: ignoring title \"%s\" that does not match old title \"%s\"\n",
root->name.data(), root->type.data(), gd->groupTitle() );
}
gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
gd->setDocumentation( root->doc, root->docFile, root->docLine );
gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
gd->addSectionsToDefinition(root->anchors);
gd->setRefItems(root->sli);
}
else
{
if (rootNav->tagInfo())
{
gd = new GroupDef(root->fileName,root->startLine,root->name,root->type,rootNav->tagInfo()->fileName);
gd->setReference(rootNav->tagInfo()->tagName);
}
else
{
gd = new GroupDef(root->fileName,root->startLine,root->name,root->type);
}
gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
gd->setDocumentation(root->doc,root->docFile,root->docLine);
gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
gd->addSectionsToDefinition(root->anchors);
Doxygen::groupSDict->append(root->name,gd);
gd->setRefItems(root->sli);
}
}
rootNav->releaseEntry();
}
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
EntryNav *e;
for (;(e=eli.current());++eli)
{
buildGroupListFiltered(e,additional);
}
}
}
static void buildGroupList(EntryNav *rootNav)
{
// first process the @defgroups blocks
buildGroupListFiltered(rootNav,FALSE);
// then process the @addtogroup, @weakgroup blocks
buildGroupListFiltered(rootNav,TRUE);
}
static void findGroupScope(EntryNav *rootNav)
{
if (rootNav->section()==Entry::GROUPDOC_SEC && !rootNav->name().isEmpty() &&
rootNav->parent() && !rootNav->parent()->name().isEmpty())
{
GroupDef *gd;
if ((gd=Doxygen::groupSDict->find(rootNav->name())))
{
QCString scope = rootNav->parent()->name();
if (rootNav->parent()->section()==Entry::PACKAGEDOC_SEC)
{
scope=substitute(scope,".","::");
}
scope = stripAnonymousNamespaceScope(scope);
scope+="::"+gd->name();
Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope);
if (d)
{
gd->setGroupScope(d);
}
}
}
RECURSE_ENTRYTREE(findGroupScope,rootNav);
}
static void organizeSubGroupsFiltered(EntryNav *rootNav,bool additional)
{
if (rootNav->section()==Entry::GROUPDOC_SEC && !rootNav->name().isEmpty())
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
(root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
{
GroupDef *gd;
if ((gd=Doxygen::groupSDict->find(root->name)))
{
//printf("adding %s to group %s\n",root->name.data(),gd->name().data());
addGroupToGroups(root,gd);
}
}
rootNav->releaseEntry();
}
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
EntryNav *e;
for (;(e=eli.current());++eli)
{
organizeSubGroupsFiltered(e,additional);
}
}
}
static void organizeSubGroups(EntryNav *rootNav)
{
//printf("Defining groups\n");
// first process the @defgroups blocks
organizeSubGroupsFiltered(rootNav,FALSE);
//printf("Additional groups\n");
// then process the @addtogroup, @weakgroup blocks
organizeSubGroupsFiltered(rootNav,TRUE);
}
//----------------------------------------------------------------------
static void buildFileList(EntryNav *rootNav)
{
if (((rootNav->section()==Entry::FILEDOC_SEC) ||
((rootNav->section() & Entry::FILE_MASK) && Config_getBool("EXTRACT_ALL"))) &&
!rootNav->name().isEmpty() && !rootNav->tagInfo() // skip any file coming from tag files
)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
bool ambig;
FileDef *fd=findFileDef(Doxygen::inputNameDict,root->name,ambig);
//printf("**************** root->name=%s fd=%p\n",root->name.data(),fd);
if (fd && !ambig)
{
#if 0
if ((!root->doc.isEmpty() && !fd->documentation().isEmpty()) ||
(!root->brief.isEmpty() && !fd->briefDescription().isEmpty()))
{
warn(
root->fileName,root->startLine,
"Warning: file %s already documented. "
"Skipping documentation.",
root->name.data()
);
}
else
#endif
{
//printf("Adding documentation!\n");
// using FALSE in setDocumentation is small hack to make sure a file
// is documented even if a \file command is used without further
// documentation
fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
fd->addSectionsToDefinition(root->anchors);
fd->setRefItems(root->sli);
QListIterator<Grouping> gli(*root->groups);
Grouping *g;
for (;(g=gli.current());++gli)
{
GroupDef *gd=0;
if (!g->groupname.isEmpty() && (gd=Doxygen::groupSDict->find(g->groupname)))
{
gd->addFile(fd);
//printf("File %s: in group %s\n",fd->name().data(),s->data());
}
}
}
}
else
{
const char *fn = root->fileName.data();
QCString text;
text.sprintf("Warning: the name `%s' supplied as "
"the second argument in the \\file statement ",
root->name.data()
);
if (ambig) // name is ambigious
{
text+="matches the following input files:\n";
text+=showFileDefMatches(Doxygen::inputNameDict,root->name);
text+="Please use a more specific name by "
"including a (larger) part of the path!";
}
else // name is not an input file
{
text+="is not an input file";
}
warn(fn,root->startLine,text);
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(buildFileList,rootNav);
}
static void addIncludeFile(ClassDef *cd,FileDef *ifd,Entry *root)
{
if (
(!root->doc.stripWhiteSpace().isEmpty() ||
!root->brief.stripWhiteSpace().isEmpty() ||
Config_getBool("EXTRACT_ALL")
) && root->protection!=Private
)
{
//printf(">>>>>> includeFile=%s\n",root->includeFile.data());
bool local=Config_getBool("FORCE_LOCAL_INCLUDES");
QCString includeFile = root->includeFile;
if (!includeFile.isEmpty() && includeFile.at(0)=='"')
{
local = TRUE;
includeFile=includeFile.mid(1,includeFile.length()-2);
}
else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
{
local = FALSE;
includeFile=includeFile.mid(1,includeFile.length()-2);
}
bool ambig;
FileDef *fd=0;
// see if we need to include a verbatim copy of the header file
//printf("root->includeFile=%s\n",root->includeFile.data());
if (!includeFile.isEmpty() &&
(fd=findFileDef(Doxygen::inputNameDict,includeFile,ambig))==0
)
{ // explicit request
QCString text;
text.sprintf("Warning: the name `%s' supplied as "
"the argument of the \\class, \\struct, \\union, or \\include command ",
includeFile.data()
);
if (ambig) // name is ambigious
{
text+="matches the following input files:\n";
text+=showFileDefMatches(Doxygen::inputNameDict,root->includeFile);
text+="Please use a more specific name by "
"including a (larger) part of the path!";
}
else // name is not an input file
{
text+="is not an input file";
}
warn(root->fileName,root->startLine,text);
}
else if (includeFile.isEmpty() && ifd &&
// see if the file extension makes sense
guessSection(ifd->name())==Entry::HEADER_SEC)
{ // implicit assumption
fd=ifd;
}
// if a file is found, we mark it as a source file.
if (fd)
{
QCString iName = !root->includeName.isEmpty() ?
root->includeName : includeFile;
if (!iName.isEmpty()) // user specified include file
{
if (iName.at(0)=='<') local=FALSE; // explicit override
if (iName.at(0)=='"' || iName.at(0)=='<')
{
iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
}
if (iName.isEmpty())
{
iName=fd->name();
}
}
else if (!Config_getList("STRIP_FROM_INC_PATH").isEmpty())
{
iName=stripFromIncludePath(fd->absFilePath());
}
else // use name of the file containing the class definition
{
iName=fd->name();
}
if (fd->generateSourceFile()) // generate code for header
{
cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
}
else // put #include in the class documentation without link
{
cd->setIncludeFile(0,iName,local,TRUE);
}
}
}
}
#if 0
static bool addNamespace(Entry *root,ClassDef *cd)
{
// see if this class is defined inside a namespace
if (root->section & Entry::COMPOUND_MASK)
{
Entry *e = root->parent;
while (e)
{
if (e->section==Entry::NAMESPACE_SEC)
{
NamespaceDef *nd=0;
QCString nsName = stripAnonymousNamespaceScope(e->name);
//printf("addNameSpace() trying: %s\n",nsName.data());
if (!nsName.isEmpty() && nsName.at(0)!='@' &&
(nd=getResolvedNamespace(nsName))
)
{
cd->setNamespace(nd);
cd->setOuterScope(nd);
nd->insertClass(cd);
return TRUE;
}
}
e=e->parent;
}
}
return FALSE;
}
#endif
#if 0
static Definition *findScope(Entry *root,int level=0)
{
if (root==0) return 0;
//printf("start findScope name=%s\n",root->name.data());
Definition *result=0;
if (root->section&Entry::SCOPE_MASK)
{
result = findScope(root->parent,level+1); // traverse to the root of the tree
if (result)
{
//printf("Found %s inside %s at level %d\n",root->name.data(),result->name().data(),level);
// TODO: look at template arguments
result = result->findInnerCompound(root->name);
}
else // reached the global scope
{
// TODO: look at template arguments
result = Doxygen::globalScope->findInnerCompound(root->name);
//printf("Found in globalScope %s at level %d\n",result->name().data(),level);
}
}
//printf("end findScope(%s,%d)=%s\n",root->name.data(),
// level,result==0 ? "<none>" : result->name().data());
return result;
}
#endif
/*! returns the Definition object belonging to the first \a level levels of
* full qualified name \a name. Creates an artificial scope if the scope is
* not found and set the parent/child scope relation if the scope is found.
*/
static Definition *buildScopeFromQualifiedName(const QCString name,int level)
{
int i=0;
int p=0,l;
Definition *prevScope=Doxygen::globalScope;
QCString fullScope;
while (i<level)
{
int idx=getScopeFragment(name,p,&l);
QCString nsName = name.mid(idx,l);
if (nsName.isEmpty()) return prevScope;
if (!fullScope.isEmpty()) fullScope+="::";
fullScope+=nsName;
NamespaceDef *nd=Doxygen::namespaceSDict->find(fullScope);
Definition *innerScope = nd;
ClassDef *cd=0;
if (nd==0) cd = getClass(fullScope);
if (nd==0 && cd) // scope is a class
{
innerScope = cd;
}
else if (nd==0 && cd==0) // scope is not known!
{
// introduce bogus namespace
//printf("++ adding dummy namespace %s to %s\n",nsName.data(),prevScope->name().data());
nd=new NamespaceDef(
"[generated]",1,fullScope);
// add namespace to the list
Doxygen::namespaceSDict->inSort(fullScope,nd);
innerScope = nd;
}
else // scope is a namespace
{
}
// make the parent/child scope relation
prevScope->addInnerCompound(innerScope);
innerScope->setOuterScope(prevScope);
// proceed to the next scope fragment
p=idx+l+2;
prevScope=innerScope;
i++;
}
return prevScope;
}
static Definition *findScopeFromQualifiedName(Definition *startScope,const QCString &n,
FileDef *fileScope)
{
//printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? startScope->name().data() : 0, n.data());
Definition *resultScope=startScope;
if (resultScope==0) resultScope=Doxygen::globalScope;
QCString scope=stripTemplateSpecifiersFromScope(n,FALSE);
int l1=0,i1;
i1=getScopeFragment(scope,0,&l1);
if (i1==-1)
{
//printf(">no fragments!\n");
return resultScope;
}
int p=i1+l1,l2=0,i2;
while ((i2=getScopeFragment(scope,p,&l2))!=-1)
{
QCString nestedNameSpecifier = scope.mid(i1,l1);
Definition *orgScope = resultScope;
//printf(" nestedNameSpecifier=%s\n",nestedNameSpecifier.data());
resultScope = resultScope->findInnerCompound(nestedNameSpecifier);
//printf(" resultScope=%p\n",resultScope);
if (resultScope==0)
{
NamespaceSDict *usedNamespaces;
if (orgScope==Doxygen::globalScope && fileScope &&
(usedNamespaces = fileScope->getUsedNamespaces()))
// also search for used namespaces
{
NamespaceSDict::Iterator ni(*usedNamespaces);
NamespaceDef *nd;
for (ni.toFirst();((nd=ni.current()) && resultScope==0);++ni)
{
// restart search within the used namespace
resultScope = findScopeFromQualifiedName(nd,n,fileScope);
}
if (resultScope)
{
// for a nested class A::I in used namespace N, we get
// N::A::I while looking for A, so we should compare
// resultScope->name() against scope.left(i2+l2)
//printf(" -> result=%s scope=%s\n",resultScope->name().data(),scope.data());
if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
{
break;
}
goto nextFragment;
}
}
// also search for used classes. Complication: we haven't been able
// to put them in the right scope yet, because we are still resolving
// the scope relations!
// Therefore loop through all used classes and see if there is a right
// scope match between the used class and nestedNameSpecifier.
QDictIterator<FileDef> ui(g_usingDeclarations);
FileDef *usedFd;
for (ui.toFirst();(usedFd=ui.current());++ui)
{
//printf("Checking using class %s\n",ui.currentKey());
if (rightScopeMatch(ui.currentKey(),nestedNameSpecifier))
{
// ui.currentKey() is the fully qualified name of nestedNameSpecifier
// so use this instead.
QCString fqn = QCString(ui.currentKey())+
scope.right(scope.length()-p);
resultScope = buildScopeFromQualifiedName(fqn,fqn.contains("::"));
//printf("Creating scope from fqn=%s result %p\n",fqn.data(),resultScope);
if (resultScope)
{
//printf("> Match! resultScope=%s\n",resultScope->name().data());
return resultScope;
}
}
}
//printf("> name %s not found in scope %s\n",nestedNameSpecifier.data(),orgScope->name().data());
return 0;
}
nextFragment:
i1=i2;
l1=l2;
p=i2+l2;
}
//printf(">findScopeFromQualifiedName scope %s\n",resultScope->name().data());
return resultScope;
}
ArgumentList *getTemplateArgumentsFromName(
const QCString &name,
const QList<ArgumentList> *tArgLists)
{
if (tArgLists==0) return 0;
QListIterator<ArgumentList> ali(*tArgLists);
// for each scope fragment, check if it is a template and advance through
// the list if so.
int i,p=0;
while ((i=name.find("::",p))!=-1)
{
NamespaceDef *nd = Doxygen::namespaceSDict->find(name.left(i));
if (nd==0)
{
ClassDef *cd = getClass(name.left(i));
if (cd)
{
if (cd->templateArguments())
{
++ali;
}
}
}
p=i+2;
}
return ali.current();
}
static ClassDef::CompoundType convertToCompoundType(int section,int specifier)
{
ClassDef::CompoundType sec=ClassDef::Class;
if (specifier&Entry::Struct)
sec=ClassDef::Struct;
else if (specifier&Entry::Union)
sec=ClassDef::Union;
else if (specifier&Entry::Interface)
sec=ClassDef::Interface;
else if (specifier&Entry::Protocol)
sec=ClassDef::Protocol;
else if (specifier&Entry::Category)
sec=ClassDef::Category;
else if (specifier&Entry::Exception)
sec=ClassDef::Exception;
switch(section)
{
//case Entry::UNION_SEC:
case Entry::UNIONDOC_SEC:
sec=ClassDef::Union;
break;
//case Entry::STRUCT_SEC:
case Entry::STRUCTDOC_SEC:
sec=ClassDef::Struct;
break;
//case Entry::INTERFACE_SEC:
case Entry::INTERFACEDOC_SEC:
sec=ClassDef::Interface;
break;
//case Entry::PROTOCOL_SEC:
case Entry::PROTOCOLDOC_SEC:
sec=ClassDef::Protocol;
break;
//case Entry::CATEGORY_SEC:
case Entry::CATEGORYDOC_SEC:
sec=ClassDef::Category;
break;
//case Entry::EXCEPTION_SEC:
case Entry::EXCEPTIONDOC_SEC:
sec=ClassDef::Exception;
break;
}
return sec;
}
static void addClassToContext(EntryNav *rootNav)
{
//printf("Loading entry for rootNav=%p name=%s\n",rootNav,rootNav->name().data());
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
//NamespaceDef *nd = 0;
FileDef *fd = rootNav->fileDef();
QCString scName;
if (rootNav->parent()->section()&Entry::SCOPE_MASK)
{
scName=rootNav->parent()->name();
}
// name without parent's scope
QCString fullName = root->name;
// strip off any template parameters (but not those for specializations)
fullName=stripTemplateSpecifiersFromScope(fullName);
// name with scope (if not present already)
QCString qualifiedName = fullName;
if (!scName.isEmpty() && !leftScopeMatch(fullName,scName))
{
qualifiedName.prepend(scName+"::");
}
// see if we already found the class before
ClassDef *cd = getClass(qualifiedName);
Debug::print(Debug::Classes,0, " Found class with name %s (qualifiedName=%s -> cd=%p)\n",
cd ? cd->name().data() : root->name.data(), qualifiedName.data(),cd);
if (cd)
{
fullName=cd->name();
Debug::print(Debug::Classes,0," Existing class %s!\n",cd->name().data());
//if (cd->templateArguments()==0)
//{
// //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,root->scopeSpec.data());
// cd->setTemplateArguments(tArgList);
//}
cd->setDocumentation(root->doc,root->docFile,root->docLine);
cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
if (root->bodyLine!=-1 && cd->getStartBodyLine()==-1)
{
cd->setBodySegment(root->bodyLine,root->endBodyLine);
cd->setBodyDef(fd);
}
//cd->setName(fullName); // change name to match docs
if (cd->templateArguments()==0)
{
// this happens if a template class declared with @class is found
// before the actual definition.
ArgumentList *tArgList =
getTemplateArgumentsFromName(cd->name(),root->tArgLists);
cd->setTemplateArguments(tArgList);
}
cd->setCompoundType(convertToCompoundType(root->section,root->spec));
}
else // new class
{
ClassDef::CompoundType sec = convertToCompoundType(root->section,root->spec);
QCString className;
QCString namespaceName;
extractNamespaceName(fullName,className,namespaceName);
//printf("New class: fullname %s namespace `%s' name=`%s' brief=`%s' docs=`%s'\n",
// fullName.data(),namespaceName.data(),className.data(),root->brief.data(),root->doc.data());
QCString tagName;
QCString refFileName;
if (rootNav->tagInfo())
{
tagName = rootNav->tagInfo()->tagName;
refFileName = rootNav->tagInfo()->fileName;
}
cd=new ClassDef(root->fileName,root->startLine,fullName,sec,
tagName,refFileName);
Debug::print(Debug::Classes,0," New class `%s' (sec=0x%08x)! #tArgLists=%d\n",
fullName.data(),root->section,root->tArgLists ? (int)root->tArgLists->count() : -1);
cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
cd->setIsObjectiveC(root->objc);
cd->setHidden(root->hidden);
cd->setArtificial(root->artificial);
cd->setTypeConstraints(root->typeConstr);
//printf("new ClassDef %s tempArgList=%p specScope=%s\n",fullName.data(),root->tArgList,root->scopeSpec.data());
ArgumentList *tArgList =
getTemplateArgumentsFromName(fullName,root->tArgLists);
//printf("class %s template args=%s\n",fullName.data(),
// tArgList ? tempArgListToString(tArgList).data() : "<none>");
cd->setTemplateArguments(tArgList);
cd->setProtection(root->protection);
cd->setIsStatic(root->stat);
// file definition containing the class cd
cd->setBodySegment(root->bodyLine,root->endBodyLine);
cd->setBodyDef(fd);
// see if the class is found inside a namespace
//bool found=addNamespace(root,cd);
// the empty string test is needed for extract all case
cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
cd->insertUsedFile(root->fileName);
// add class to the list
//printf("ClassDict.insert(%s)\n",resolveDefines(fullName).data());
Doxygen::classSDict->append(fullName,cd);
}
cd->addSectionsToDefinition(root->anchors);
if (!root->subGrouping) cd->setSubGrouping(FALSE);
if (cd->hasDocumentation())
{
addIncludeFile(cd,fd,root);
}
if (fd && (root->section & Entry::COMPOUND_MASK))
{
//printf(">> Inserting class `%s' in file `%s' (root->fileName=`%s')\n",
// cd->name().data(),
// fd->name().data(),
// root->fileName.data()
// );
cd->setFileDef(fd);
fd->insertClass(cd);
}
addClassToGroups(root,cd);
cd->setRefItems(root->sli);
rootNav->releaseEntry();
}
//----------------------------------------------------------------------
// build a list of all classes mentioned in the documentation
// and all classes that have a documentation block before their definition.
static void buildClassList(EntryNav *rootNav)
{
if (
((rootNav->section() & Entry::COMPOUND_MASK) ||
rootNav->section()==Entry::OBJCIMPL_SEC) && !rootNav->name().isEmpty()
)
{
addClassToContext(rootNav);
}
RECURSE_ENTRYTREE(buildClassList,rootNav);
}
static void buildClassDocList(EntryNav *rootNav)
{
if (
(rootNav->section() & Entry::COMPOUNDDOC_MASK) && !rootNav->name().isEmpty()
)
{
addClassToContext(rootNav);
}
RECURSE_ENTRYTREE(buildClassDocList,rootNav);
}
static void resolveClassNestingRelations()
{
ClassSDict::Iterator cli(*Doxygen::classSDict);
for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE;
bool done=FALSE;
int iteration=0;
while (!done)
{
done=TRUE;
++iteration;
ClassDef *cd=0;
for (cli.toFirst();(cd=cli.current());++cli)
{
if (!cd->visited)
{
QCString name = stripAnonymousNamespaceScope(cd->name());
//printf("processing=%s, iteration=%d\n",cd->name().data(),iteration);
// also add class to the correct structural context
Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,
name,cd->getFileDef());
if (d)
{
//printf("****** adding %s to scope %s in iteration %d\n",cd->name().data(),d->name().data(),iteration);
d->addInnerCompound(cd);
cd->setOuterScope(d);
cd->visited=TRUE;
done=FALSE;
}
//else
//{
// printf("****** ignoring %s: scope not (yet) found in iteration %d\n",cd->name().data(),iteration);
//}
}
}
}
//give warnings for unresolved compounds
ClassDef *cd=0;
for (cli.toFirst();(cd=cli.current());++cli)
{
if (!cd->visited)
{
QCString name = stripAnonymousNamespaceScope(cd->name());
//printf("processing unresolved=%s, iteration=%d\n",cd->name().data(),iteration);
/// create the scope artificially
// anyway, so we can at least relate scopes properly.
Definition *d = buildScopeFromQualifiedName(name,name.contains("::"));
if (d!=cd && !cd->getDefFileName().isEmpty())
// avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
// for this case doxygen assumes the exitance of a namespace N::N in which C is to be found!
// also avoid warning for stuff imported via a tagfile.
{
d->addInnerCompound(cd);
cd->setOuterScope(d);
warn(cd->getDefFileName(),cd->getDefLine(),
"Warning: Internal inconsistency: scope for class %s not "
"found!",name.data()
);
}
}
}
}
//----------------------------------------------------------------------
// build a list of all namespaces mentioned in the documentation
// and all namespaces that have a documentation block before their definition.
static void buildNamespaceList(EntryNav *rootNav)
{
if (
(rootNav->section()==Entry::NAMESPACE_SEC ||
rootNav->section()==Entry::NAMESPACEDOC_SEC ||
rootNav->section()==Entry::PACKAGEDOC_SEC
) &&
!rootNav->name().isEmpty()
)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
//printf("** buildNamespaceList(%s)\n",root->name.data());
QCString fName = root->name;
if (root->section==Entry::PACKAGEDOC_SEC)
{
fName=substitute(fName,".","::");
}
QCString fullName = stripAnonymousNamespaceScope(fName);
if (!fullName.isEmpty())
{
//printf("Found namespace %s in %s at line %d\n",root->name.data(),
// root->fileName.data(), root->startLine);
NamespaceDef *nd;
if ((nd=Doxygen::namespaceSDict->find(fullName))) // existing namespace
{
#if 0
if (!root->doc.isEmpty() || !root->brief.isEmpty()) // block contains docs
{
if (nd->documentation().isEmpty() && !root->doc.isEmpty())
{
#endif
nd->setDocumentation(root->doc,root->docFile,root->docLine);
nd->setName(fullName); // change name to match docs
nd->addSectionsToDefinition(root->anchors);
#if 0
}
else if (!nd->documentation().isEmpty() && !root->doc.isEmpty())
{
warn(
root->fileName,root->startLine,
"Warning: namespace %s already has a detailed description found in file %s at line %d. "
"Skipping the documentation found here.",
fullName.data(),nd->docFile().data(),nd->docLine());
}
if (nd->briefDescription().isEmpty() && !root->brief.isEmpty())
{
#endif
nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
#if 0
nd->setName(fullName); // change name to match docs
}
else if (!nd->briefDescription().isEmpty() && !root->brief.isEmpty())
{
warn(root->fileName,root->startLine,
"Warning: namespace %s already has a brief description found in file %s at line %d. "
"Skipping the documentation found here.",
fullName.data(),nd->docFile().data(),nd->docLine()
);
}
}
#endif
// file definition containing the namespace nd
FileDef *fd=rootNav->fileDef();
// insert the namespace in the file definition
if (fd) fd->insertNamespace(nd);
addNamespaceToGroups(root,nd);
nd->setRefItems(root->sli);
}
else // fresh namespace
{
QCString tagName;
QCString tagFileName;
if (rootNav->tagInfo())
{
tagName=rootNav->tagInfo()->tagName;
tagFileName=rootNav->tagInfo()->fileName;
}
//printf("++ new namespace %d\n",fullName.data());
NamespaceDef *nd=new NamespaceDef(root->fileName,root->startLine,fullName,tagName,tagFileName);
nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
nd->addSectionsToDefinition(root->anchors);
nd->setHidden(root->hidden);
nd->setArtificial(root->artificial);
//printf("Adding namespace to group\n");
addNamespaceToGroups(root,nd);
nd->setRefItems(root->sli);
// file definition containing the namespace nd
FileDef *fd=rootNav->fileDef();
// insert the namespace in the file definition
if (fd) fd->insertNamespace(nd);
// the empty string test is needed for extract all case
nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
nd->insertUsedFile(root->fileName);
nd->setBodySegment(root->bodyLine,root->endBodyLine);
nd->setBodyDef(fd);
// add class to the list
Doxygen::namespaceSDict->inSort(fullName,nd);
// also add namespace to the correct structural context
Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName);
//printf("adding namespace %s to context %s\n",nd->name().data(),d?d->name().data():"<none>");
if (d==0) // we didn't find anything, create the scope artificially
// anyway, so we can at least relate scopes properly.
{
Definition *d = buildScopeFromQualifiedName(fullName,fullName.contains("::"));
d->addInnerCompound(nd);
nd->setOuterScope(d);
// TODO: Due to the order in which the tag file is written
// a nested class can be found before its parent!
}
else
{
d->addInnerCompound(nd);
nd->setOuterScope(d);
}
}
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(buildNamespaceList,rootNav);
}
//----------------------------------------------------------------------
static NamespaceDef *findUsedNamespace(NamespaceSDict *unl,
const QCString &name)
{
NamespaceDef *usingNd =0;
if (unl)
{
//printf("Found namespace dict %d\n",unl->count());
NamespaceSDict::Iterator unli(*unl);
NamespaceDef *und;
for (unli.toFirst();(und=unli.current());++unli)
{
QCString uScope=und->name()+"::";
usingNd = getResolvedNamespace(uScope+name);
//printf("Also trying with scope=`%s' usingNd=%p\n",(uScope+name).data(),usingNd);
}
}
return usingNd;
}
static void findUsingDirectives(EntryNav *rootNav)
{
if (rootNav->section()==Entry::USINGDIR_SEC)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
//printf("Found using directive %s at line %d of %s\n",
// root->name.data(),root->startLine,root->fileName.data());
QCString name=substitute(root->name,".","::");
if (!name.isEmpty())
{
NamespaceDef *usingNd = 0;
NamespaceDef *nd = 0;
FileDef *fd = rootNav->fileDef();
QCString nsName;
// see if the using statement was found inside a namespace or inside
// the global file scope.
if (rootNav->parent() && rootNav->parent()->section()==Entry::NAMESPACE_SEC &&
(fd==0 || !fd->isJava()) // not a .java file
)
{
nsName=stripAnonymousNamespaceScope(rootNav->parent()->name());
if (!nsName.isEmpty())
{
nd = getResolvedNamespace(nsName);
}
}
// find the scope in which the `using' namespace is defined by prepending
// the possible scopes in which the using statement was found, starting
// with the most inner scope and going to the most outer scope (i.e.
// file scope).
int scopeOffset = nsName.length();
do
{
QCString scope=scopeOffset>0 ?
nsName.left(scopeOffset)+"::" : QCString();
usingNd = getResolvedNamespace(scope+name);
//printf("Trying with scope=`%s' usingNd=%p\n",(scope+name).data(),usingNd);
if (scopeOffset==0)
{
scopeOffset=-1;
}
else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
{
scopeOffset=0;
}
} while (scopeOffset>=0 && usingNd==0);
if (usingNd==0 && nd) // not found, try used namespaces in this scope
// or in one of the parent namespace scopes
{
NamespaceDef *pnd = nd;
while (pnd && usingNd==0)
{
// also try with one of the used namespaces found earlier
usingNd = findUsedNamespace(pnd->getUsedNamespaces(),name);
// goto the parent
Definition *s = pnd->getOuterScope();
if (s && s->definitionType()==Definition::TypeNamespace)
{
pnd = (NamespaceDef*)s;
}
else
{
pnd = 0;
}
}
}
if (usingNd==0 && fd) // still nothing, also try used namespace in the
// global scope
{
usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
}
//printf("%s -> %s\n",name.data(),usingNd?usingNd->name().data():"<none>");
// add the namespace the correct scope
if (usingNd)
{
//printf("using fd=%p nd=%p\n",fd,nd);
if (nd)
{
//printf("Inside namespace %s\n",nd->name().data());
nd->addUsingDirective(usingNd);
}
else if (fd)
{
//printf("Inside file %s\n",fd->name().data());
fd->addUsingDirective(usingNd);
}
}
else // unknown namespace, but add it anyway.
{
//printf("++ new unknown namespace %s\n",name.data());
NamespaceDef *nd=new NamespaceDef(root->fileName,root->startLine,name);
nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
nd->addSectionsToDefinition(root->anchors);
//printf("** Adding namespace %s hidden=%d\n",name.data(),root->hidden);
nd->setHidden(root->hidden);
nd->setArtificial(TRUE);
QListIterator<Grouping> gli(*root->groups);
Grouping *g;
for (;(g=gli.current());++gli)
{
GroupDef *gd=0;
if (!g->groupname.isEmpty() && (gd=Doxygen::groupSDict->find(g->groupname)))
gd->addNamespace(nd);
}
// insert the namespace in the file definition
if (fd)
{
fd->insertNamespace(nd);
fd->addUsingDirective(nd);
}
// the empty string test is needed for extract all case
nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
nd->insertUsedFile(root->fileName);
// add class to the list
Doxygen::namespaceSDict->inSort(name,nd);
nd->setRefItems(root->sli);
}
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(findUsingDirectives,rootNav);
}
//----------------------------------------------------------------------
static void buildListOfUsingDecls(EntryNav *rootNav)
{
if (rootNav->section()==Entry::USINGDECL_SEC &&
!(rootNav->parent()->section()&Entry::COMPOUND_MASK) // not a class/struct member
)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
QCString name = substitute(root->name,".","::");
if (g_usingDeclarations.find(name)==0)
{
FileDef *fd = rootNav->fileDef();
if (fd)
{
g_usingDeclarations.insert(name,fd);
}
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(buildListOfUsingDecls,rootNav);
}
static void findUsingDeclarations(EntryNav *rootNav)
{
if (rootNav->section()==Entry::USINGDECL_SEC &&
!(rootNav->parent()->section()&Entry::COMPOUND_MASK) // not a class/struct member
)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
//printf("Found using declaration %s at line %d of %s inside section %x\n",
// root->name.data(),root->startLine,root->fileName.data(),
// rootNav->parent()->section());
if (!root->name.isEmpty())
{
ClassDef *usingCd = 0;
NamespaceDef *nd = 0;
FileDef *fd = rootNav->fileDef();
QCString scName;
// see if the using statement was found inside a namespace or inside
// the global file scope.
if (rootNav->parent()->section() == Entry::NAMESPACE_SEC)
{
scName=rootNav->parent()->name();
if (!scName.isEmpty())
{
nd = getResolvedNamespace(scName);
}
}
// Assume the using statement was used to import a class.
// Find the scope in which the `using' namespace is defined by prepending
// the possible scopes in which the using statement was found, starting
// with the most inner scope and going to the most outer scope (i.e.
// file scope).
QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
usingCd = getClass(name);
if (usingCd==0)
{
usingCd = Doxygen::hiddenClasses->find(name);
}
//printf("%s -> %p\n",root->name.data(),usingCd);
if (usingCd==0) // definition not in the input => add an artificial class
{
Debug::print(Debug::Classes,0," New using class `%s' (sec=0x%08x)! #tArgLists=%d\n",
name.data(),root->section,root->tArgLists ? (int)root->tArgLists->count() : -1);
usingCd = new ClassDef(
"<using>",1,
name,ClassDef::Class);
Doxygen::hiddenClasses->append(root->name,usingCd);
usingCd->setArtificial(TRUE);
}
else
{
Debug::print(Debug::Classes,0," Found used class %s in scope=%s\n",
usingCd->name().data(),nd?nd->name().data():fd->name().data());
}
if (usingCd) // add the class to the correct scope
{
if (nd)
{
//printf("Inside namespace %s\n",nd->name().data());
nd->addUsingDeclaration(usingCd);
}
else if (fd)
{
//printf("Inside file %s\n",fd->name().data());
fd->addUsingDeclaration(usingCd);
}
}
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(findUsingDeclarations,rootNav);
}
//----------------------------------------------------------------------
static void findUsingDeclImports(EntryNav *rootNav)
{
if (rootNav->section()==Entry::USINGDECL_SEC &&
(rootNav->parent()->section()&Entry::COMPOUND_MASK) // in a class/struct member
)
{
//printf("Found using declaration %s at line %d of %s inside section %x\n",
// root->name.data(),root->startLine,root->fileName.data(),
// root->parent->section);
QCString fullName=removeRedundantWhiteSpace(rootNav->parent()->name());
fullName=stripAnonymousNamespaceScope(fullName);
fullName=stripTemplateSpecifiersFromScope(fullName);
ClassDef *cd = getClass(fullName);
if (cd)
{
//printf("found class %s\n",cd->name().data());
int i=rootNav->name().find("::");
if (i!=-1)
{
QCString scope=rootNav->name().left(i);
QCString memName=rootNav->name().right(rootNav->name().length()-i-2);
ClassDef *bcd = getResolvedClass(cd,0,scope); // todo: file in fileScope parameter
if (bcd)
{
//printf("found class %s\n",bcd->name().data());
MemberNameInfoSDict *mndict=bcd->memberNameInfoSDict();
if (mndict)
{
MemberNameInfo *mni = mndict->find(memName);
if (mni)
{
MemberNameInfoIterator mnii(*mni);
MemberInfo *mi;
for ( ; (mi=mnii.current()) ; ++mnii )
{
MemberDef *md = mi->memberDef;
if (md && md->protection()!=Private)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
//printf("found member %s\n",mni->memberName());
MemberDef *newMd = 0;
{
LockingPtr<ArgumentList> templAl = md->templateArguments();
LockingPtr<ArgumentList> al = md->templateArguments();
newMd = new MemberDef(
root->fileName,root->startLine,
md->typeString(),memName,md->argsString(),
md->excpString(),root->protection,root->virt,
md->isStatic(),Member,md->memberType(),
templAl.pointer(),al.pointer()
);
}
newMd->setMemberClass(cd);
cd->insertMember(newMd);
if (!root->doc.isEmpty() || !root->brief.isEmpty())
{
newMd->setDocumentation(root->doc,root->docFile,root->docLine);
newMd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
newMd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
}
else
{
newMd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
newMd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
newMd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
}
newMd->setDefinition(md->definition());
newMd->enableCallGraph(root->callGraph);
newMd->enableCallerGraph(root->callerGraph);
newMd->setBitfields(md->bitfieldString());
newMd->addSectionsToDefinition(root->anchors);
newMd->setBodySegment(md->getStartBodyLine(),md->getEndBodyLine());
newMd->setBodyDef(md->getBodyDef());
newMd->setInitializer(md->initializer());
newMd->setMaxInitLines(md->initializerLines());
newMd->setMemberGroupId(root->mGrpId);
newMd->setMemberSpecifiers(md->getMemberSpecifiers());
rootNav->releaseEntry();
}
}
}
}
}
}
}
}
RECURSE_ENTRYTREE(findUsingDeclImports,rootNav);
}
//----------------------------------------------------------------------
static void findIncludedUsingDirectives()
{
// first mark all files as not visited
FileNameListIterator fnli(*Doxygen::inputNameList);
FileName *fn;
for (fnli.toFirst();(fn=fnli.current());++fnli)
{
FileNameIterator fni(*fn);
FileDef *fd;
for (;(fd=fni.current());++fni)
{
fd->visited=FALSE;
}
}
// then recursively add using directives found in #include files
// to files that have not been visited.
for (fnli.toFirst();(fn=fnli.current());++fnli)
{
FileNameIterator fni(*fn);
FileDef *fd;
for (fni.toFirst();(fd=fni.current());++fni)
{
if (!fd->visited)
{
//printf("----- adding using directives for file %s\n",fd->name().data());
fd->addIncludedUsingDirectives();
}
}
}
}
//----------------------------------------------------------------------
static MemberDef *addVariableToClass(
EntryNav *rootNav,
ClassDef *cd,
MemberDef::MemberType mtype,
const QCString &name,
bool fromAnnScope,
MemberDef *fromAnnMemb,
Protection prot,
Relationship related)
{
Entry *root = rootNav->entry();
QCString qualScope = cd->qualifiedNameWithTemplateParameters();
QCString scopeSeparator="::";
if (Config_getBool("OPTIMIZE_OUTPUT_JAVA"))
{
qualScope = substitute(qualScope,"::",".");
scopeSeparator=".";
}
Debug::print(Debug::Variables,0,
" class variable:\n"
" `%s' `%s'::`%s' `%s' prot=`%d ann=%d init=`%s'\n",
root->type.data(),
qualScope.data(),
name.data(),
root->args.data(),
root->protection,
fromAnnScope,
root->initializer.data()
);
QCString def;
if (!root->type.isEmpty())
{
if (related || mtype==MemberDef::Friend || Config_getBool("HIDE_SCOPE_NAMES"))
{
def=root->type+" "+name+root->args;
}
else
{
def=root->type+" "+qualScope+scopeSeparator+name+root->args;
}
}
else
{
if (Config_getBool("HIDE_SCOPE_NAMES"))
{
def=name+root->args;
}
else
{
def=qualScope+scopeSeparator+name+root->args;
}
}
def.stripPrefix("static ");
// see if the member is already found in the same scope
// (this may be the case for a static member that is initialized
// outside the class)
MemberName *mn=Doxygen::memberNameSDict->find(name);
if (mn)
{
MemberNameIterator mni(*mn);
MemberDef *md;
for (mni.toFirst();(md=mni.current());++mni)
{
//printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
// md->getClassDef(),cd,root->type.data(),md->typeString());
if (md->getClassDef()==cd &&
removeRedundantWhiteSpace(root->type)==md->typeString())
// member already in the scope
{
if (root->objc &&
root->mtype==Property &&
md->memberType()==MemberDef::Variable)
{ // Objective-C 2.0 property
// turn variable into a property
md->setProtection(root->protection);
cd->reclassifyMember(md,MemberDef::Property);
}
addMemberDocs(rootNav,md,def,0,FALSE);
//printf(" Member already found!\n");
return md;
}
}
}
// new member variable, typedef or enum value
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
root->type,name,root->args,0,
prot,Normal,root->stat,related,
mtype,0,0);
md->setTagInfo(rootNav->tagInfo());
md->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
//md->setDefFile(root->fileName);
//md->setDefLine(root->startLine);
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setDefinition(def);
md->setBitfields(root->bitfields);
md->addSectionsToDefinition(root->anchors);
md->setFromAnonymousScope(fromAnnScope);
md->setFromAnonymousMember(fromAnnMemb);
//md->setIndentDepth(indentDepth);
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setInitializer(root->initializer);
md->setMaxInitLines(root->initLines);
md->setMemberGroupId(root->mGrpId);
md->setMemberSpecifiers(root->spec);
md->setReadAccessor(root->read);
md->setWriteAccessor(root->write);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
md->setHidden(root->hidden);
md->setArtificial(root->artificial);
addMemberToGroups(root,md);
//if (root->mGrpId!=-1)
//{
// printf("memberdef %s in memberGroup %d\n",name.data(),root->mGrpId);
// md->setMemberGroup(memberGroupDict[root->mGrpId]);
//
md->setBodyDef(rootNav->fileDef());
//printf(" Adding member=%s\n",md->name().data());
// add the member to the global list
if (mn)
{
mn->append(md);
}
else // new variable name
{
mn = new MemberName(name);
mn->append(md);
//printf("Adding memberName=%s\n",mn->memberName());
//Doxygen::memberNameDict.insert(name,mn);
//Doxygen::memberNameList.append(mn);
Doxygen::memberNameSDict->append(name,mn);
// add the member to the class
}
//printf(" New member adding to %s (%p)!\n",cd->name().data(),cd);
cd->insertMember(md);
md->setRefItems(root->sli);
//TODO: insert FileDef instead of filename strings.
cd->insertUsedFile(root->fileName);
rootNav->changeSection(Entry::EMPTY_SEC);
return md;
}
//----------------------------------------------------------------------
static MemberDef *addVariableToFile(
EntryNav *rootNav,
MemberDef::MemberType mtype,
const QCString &scope,
const QCString &name,
bool fromAnnScope,
/*int indentDepth,*/
MemberDef *fromAnnMemb)
{
Entry *root = rootNav->entry();
Debug::print(Debug::Variables,0,
" global variable:\n"
" type=`%s' scope=`%s' name=`%s' args=`%s' prot=`%d mtype=%d\n",
root->type.data(),
scope.data(),
name.data(),
root->args.data(),
root->protection,
mtype
);
FileDef *fd = rootNav->fileDef();
// see if we have a typedef that should hide a struct or union
if (mtype==MemberDef::Typedef && Config_getBool("TYPEDEF_HIDES_STRUCT"))
{
QCString type = root->type;
type.stripPrefix("typedef ");
if (type.left(7)=="struct " || type.left(6)=="union ")
{
type.stripPrefix("struct ");
type.stripPrefix("union ");
static QRegExp re("[a-z_A-Z][a-z_A-Z0-9]*");
int l,s;
s = re.match(type,0,&l);
if (s>=0)
{
QCString typeValue = type.mid(s,l);
ClassDef *cd = getClass(typeValue);
if (cd)
{
// this typedef should hide compound name cd, so we
// change the name that is displayed from cd.
cd->setClassName(name);
cd->setDocumentation(root->doc,root->docFile,root->docLine);
cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
return 0;
}
}
}
}
// see if the function is inside a namespace
NamespaceDef *nd = 0;
QCString nscope;
if (!scope.isEmpty())
{
if (scope.find('@')!=-1) return 0; // anonymous scope!
//nscope=removeAnonymousScopes(scope);
//if (!nscope.isEmpty())
//{
nd = getResolvedNamespace(scope);
//}
}
QCString def;
// determine the definition of the global variable
if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@' &&
!Config_getBool("HIDE_SCOPE_NAMES")
)
// variable is inside a namespace, so put the scope before the name
{
static bool optimizeForJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA");
QCString sep="::";
if (optimizeForJava) sep=".";
if (!root->type.isEmpty())
{
def=root->type+" "+nd->name()+sep+name+root->args;
}
else
{
def=nd->name()+sep+name+root->args;
}
}
else
{
if (!root->type.isEmpty() && !root->name.isEmpty())
{
if (name.at(0)=='@') // dummy variable representing annonymous union
def=root->type;
else
def=root->type+" "+name+root->args;
}
else
{
def=name+root->args;
}
}
def.stripPrefix("static ");
MemberName *mn=Doxygen::functionNameSDict->find(name);
if (mn)
{
//QCString nscope=removeAnonymousScopes(scope);
//NamespaceDef *nd=0;
//if (!nscope.isEmpty())
if (!scope.isEmpty())
{
nd = getResolvedNamespace(scope);
}
MemberNameIterator mni(*mn);
MemberDef *md;
for (mni.toFirst();(md=mni.current());++mni)
{
if (
((nd==0 && md->getNamespaceDef()==0 && md->getFileDef() &&
root->fileName==md->getFileDef()->absFilePath()
) // both variable names in the same file
|| (nd!=0 && md->getNamespaceDef()==nd) // both in same namespace
)
&& !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
&& !md->isEnumerate() // in C# an enum value and enum can have the same name
)
// variable already in the scope
{
if (md->getFileDef() &&
! // not a php array
(
(getLanguageFromFileName(md->getFileDef()->name())==SrcLangExt_PHP) &&
(md->argsString()!=root->args && root->args.find('[')!=-1)
)
)
// not a php array variable
{
Debug::print(Debug::Variables,0,
" variable already found: scope=%s\n",md->getOuterScope()->name().data());
addMemberDocs(rootNav,md,def,0,FALSE);
md->setRefItems(root->sli);
return md;
}
}
}
}
Debug::print(Debug::Variables,0,
" new variable, nd=%s!\n",nd?nd->name().data():"<global>");
// new global variable, enum value or typedef
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
root->type,name,root->args,0,
Public, Normal,root->stat,Member,
mtype,0,0);
md->setTagInfo(rootNav->tagInfo());
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->addSectionsToDefinition(root->anchors);
md->setFromAnonymousScope(fromAnnScope);
md->setFromAnonymousMember(fromAnnMemb);
md->setInitializer(root->initializer);
md->setMaxInitLines(root->initLines);
md->setMemberGroupId(root->mGrpId);
md->setDefinition(def);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
md->setExplicitExternal(root->explicitExternal);
//md->setOuterScope(fd);
if (!root->explicitExternal)
{
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setBodyDef(fd);
}
addMemberToGroups(root,md);
md->setRefItems(root->sli);
if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
{
md->setNamespace(nd);
nd->insertMember(md);
}
// add member to the file (we do this even if we have already inserted
// it into the namespace.
if (fd)
{
md->setFileDef(fd);
fd->insertMember(md);
}
// add member definition to the list of globals
if (mn)
{
mn->append(md);
}
else
{
mn = new MemberName(name);
mn->append(md);
Doxygen::functionNameSDict->append(name,mn);
}
rootNav->changeSection(Entry::EMPTY_SEC);
return md;
}
/*! See if the return type string \a type is that of a function pointer
* \returns -1 if this is not a function pointer variable or
* the index at which the brace of (...*name) was found.
*/
static int findFunctionPtr(const QCString &type,int *pLength=0)
{
static const QRegExp re("([^)]*\\*[^)]*)");
int i=-1,l;
if (!type.isEmpty() && // return type is non-empty
(i=re.match(type,0,&l))!=-1 && // contains (...*...)
type.find("operator")==-1 && // not an operator
(type.find(")(")==-1 || type.find("typedef ")!=-1)
// not a function pointer return type
)
{
if (pLength) *pLength=l;
return i;
}
else
{
return -1;
}
}
/*! Returns TRUE iff \a type is a class within scope \a context.
* Used to detect variable declarations that look like function prototypes.
*/
static bool isVarWithConstructor(EntryNav *rootNav)
{
static QRegExp initChars("[0-9\"'&*!^]+");
static QRegExp idChars("[a-z_A-Z][a-z_A-Z0-9]*");
bool result=FALSE;
bool typeIsClass;
QCString type;
Definition *ctx = 0;
FileDef *fd = 0;
int ti;
//printf("isVarWithConstructor(%s)\n",rootNav->name().data());
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
if (rootNav->parent()->section() & Entry::COMPOUND_MASK)
{ // inside a class
result=FALSE;
goto done;
}
else if ((fd = rootNav->fileDef()) &&
(fd->name().right(2)==".c" || fd->name().right(2)==".h")
)
{ // inside a .c file
result=FALSE;
goto done;
}
if (root->type.isEmpty())
{
result=FALSE;
goto done;
}
if (!rootNav->parent()->name().isEmpty())
{
ctx=Doxygen::namespaceSDict->find(rootNav->parent()->name());
}
type = root->type;
// remove qualifiers
findAndRemoveWord(type,"const");
findAndRemoveWord(type,"static");
findAndRemoveWord(type,"volatile");
//if (type.left(6)=="const ") type=type.right(type.length()-6);
typeIsClass=getResolvedClass(ctx,fd,type)!=0;
if (!typeIsClass && (ti=type.find('<'))!=-1)
{
typeIsClass=getResolvedClass(ctx,fd,type.left(ti))!=0;
}
if (typeIsClass) // now we still have to check if the arguments are
// types or values. Since we do not have complete type info
// we need to rely on heuristics :-(
{
//printf("typeIsClass\n");
ArgumentList *al = root->argList;
if (al==0 || al->isEmpty())
{
result=FALSE; // empty arg list -> function prototype.
goto done;
}
ArgumentListIterator ali(*al);
Argument *a;
for (ali.toFirst();(a=ali.current());++ali)
{
if (!a->name.isEmpty() || !a->defval.isEmpty())
{
if (a->name.find(initChars)==0)
{
result=TRUE;
}
else
{
result=FALSE; // arg has (type,name) pair -> function prototype
}
goto done;
}
if (a->type.isEmpty() || getResolvedClass(ctx,fd,a->type)!=0)
{
result=FALSE; // arg type is a known type
goto done;
}
if (checkIfTypedef(ctx,fd,a->type))
{
//printf("%s:%d: false (arg is typedef)\n",__FILE__,__LINE__);
result=FALSE; // argument is a typedef
goto done;
}
if (a->type.at(a->type.length()-1)=='*' ||
a->type.at(a->type.length()-1)=='&')
// type ends with * or & => pointer or reference
{
result=FALSE;
goto done;
}
if (a->type.find(initChars)==0)
{
result=TRUE; // argument type starts with typical initializer char
goto done;
}
QCString resType=resolveTypeDef(ctx,a->type);
if (resType.isEmpty()) resType=a->type;
int len;
if (idChars.match(resType,0,&len)==0) // resType starts with identifier
{
resType=resType.left(len);
//printf("resType=%s\n",resType.data());
if (resType=="int" || resType=="long" || resType=="float" ||
resType=="double" || resType=="char" || resType=="signed" ||
resType=="const" || resType=="unsigned" || resType=="void")
{
result=FALSE; // type keyword -> function prototype
goto done;
}
}
}
result=TRUE;
}
done:
//printf("isVarWithConstructor(%s,%s)=%d\n",rootNav->parent()->name().data(),
// root->type.data(),result);
rootNav->releaseEntry();
return result;
}
static void addVariable(EntryNav *rootNav,int isFuncPtr=-1)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
Debug::print(Debug::Variables,0,
"VARIABLE_SEC: \n"
" type=`%s' name=`%s' args=`%s' bodyLine=`%d' mGrpId=%d\n",
root->type.data(),
root->name.data(),
root->args.data(),
root->bodyLine,
root->mGrpId
);
//printf("root->parent->name=%s\n",root->parent->name.data());
if (root->type.isEmpty() && root->name.find("operator")==-1 &&
(root->name.find('*')!=-1 || root->name.find('&')!=-1))
{
// recover from parse error caused by redundant braces
// like in "int *(var[10]);", which is parsed as
// type="" name="int *" args="(var[10])"
root->type=root->name;
static const QRegExp reName("[a-z_A-Z][a-z_A-Z0-9]*");
int l;
int i=root->args.isEmpty() ? -1 : reName.match(root->args,0,&l);
root->name=root->args.mid(i,l);
root->args=root->args.mid(i+l,root->args.find(')',i+l)-i-l);
//printf("new: type=`%s' name=`%s' args=`%s'\n",
// root->type.data(),root->name.data(),root->args.data());
}
else
{
int i=isFuncPtr;
if (i==-1) i=findFunctionPtr(root->type); // for typedefs isFuncPtr is not yet set
if (i!=-1) // function pointer
{
int ai = root->type.find('[',i);
if (ai>i) // function pointer array
{
root->args.prepend(root->type.right(root->type.length()-ai));
root->type=root->type.left(ai);
}
else if (root->type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
{
root->type=root->type.left(root->type.length()-1);
root->args.prepend(")");
//printf("root->type=%s root->args=%s\n",root->type.data(),root->args.data());
}
}
else if (root->type.find("typedef ")!=-1 && root->type.right(2)=="()") // typedef void (func)(int)
{
root->type=root->type.left(root->type.length()-1);
root->args.prepend(")");
}
}
QCString scope,name=removeRedundantWhiteSpace(root->name);
// find the scope of this variable
EntryNav *p = rootNav->parent();
while ((p->section() & Entry::SCOPE_MASK))
{
QCString scopeName = p->name();
if (!scopeName.isEmpty())
{
scope.prepend(scopeName);
break;
}
p=p->parent();
}
MemberDef::MemberType mtype;
QCString type=root->type.stripWhiteSpace();
ClassDef *cd=0;
bool isRelated=FALSE;
bool isMemberOf=FALSE;
QCString classScope=stripAnonymousNamespaceScope(scope);
classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
QCString annScopePrefix=scope.left(scope.length()-classScope.length());
if (root->name.findRev("::")!=-1)
{
if (root->type=="friend class" || root->type=="friend struct" ||
root->type=="friend union")
{
cd=getClass(scope);
if (cd)
{
addVariableToClass(rootNav, // entry
cd, // class to add member to
MemberDef::Friend, // type of member
name, // name of the member
FALSE, // from Anonymous scope
0, // anonymous member
Public, // protection
Member // related to a class
);
}
}
goto nextMember;
/* skip this member, because it is a
* static variable definition (always?), which will be
* found in a class scope as well, but then we know the
* correct protection level, so only then it will be
* inserted in the correct list!
*/
}
if (type=="@")
mtype=MemberDef::EnumValue;
else if (type.left(8)=="typedef ")
mtype=MemberDef::Typedef;
else if (type.left(7)=="friend ")
mtype=MemberDef::Friend;
else if (root->mtype==Property)
mtype=MemberDef::Property;
else if (root->mtype==Event)
mtype=MemberDef::Event;
else
mtype=MemberDef::Variable;
if (!root->relates.isEmpty()) // related variable
{
isRelated=TRUE;
isMemberOf=(root->relatesType == MemberOf);
if (getClass(root->relates)==0 && !scope.isEmpty())
scope=mergeScopes(scope,root->relates);
else
scope=root->relates;
}
cd=getClass(scope);
if (cd==0 && classScope!=scope) cd=getClass(classScope);
if (cd)
{
MemberDef *md=0;
// if cd is an annonymous scope we insert the member
// into a non-annonymous scope as well. This is needed to
// be able to refer to it using \var or \fn
//int indentDepth=0;
int si=scope.find('@');
//int anonyScopes = 0;
bool added=FALSE;
if (si!=-1) // anonymous scope
{
QCString pScope;
ClassDef *pcd=0;
pScope = scope.left(QMAX(si-2,0));
if (!pScope.isEmpty())
pScope.prepend(annScopePrefix);
else if (annScopePrefix.length()>2)
pScope=annScopePrefix.left(annScopePrefix.length()-2);
if (name.at(0)!='@')
{
if (!pScope.isEmpty() && (pcd=getClass(pScope)))
{
md=addVariableToClass(rootNav, // entry
pcd, // class to add member to
mtype, // member type
name, // member name
TRUE, // from anonymous scope
0, // from anonymous member
root->protection,
isMemberOf ? Foreign : isRelated ? Related : Member
);
added=TRUE;
}
else // anonymous scope inside namespace or file => put variable in the global scope
{
if (mtype==MemberDef::Variable)
{
md=addVariableToFile(rootNav,mtype,pScope,name,TRUE,0);
}
added=TRUE;
}
}
}
//printf("name=`%s' scope=%s scope.right=%s\n",
// name.data(),scope.data(),
// scope.right(scope.length()-si).data());
addVariableToClass(rootNav, // entry
cd, // class to add member to
mtype, // member type
name, // name of the member
FALSE, // from anonymous scope
md, // from anonymous member
root->protection,
isMemberOf ? Foreign : isRelated ? Related : Member);
}
else if (!name.isEmpty()) // global variable
{
//printf("Inserting member in global scope %s!\n",scope.data());
addVariableToFile(rootNav,mtype,scope,name,FALSE,/*0,*/0);
}
nextMember:
rootNav->releaseEntry();
}
//----------------------------------------------------------------------
// Searches the Entry tree for typedef documentation sections.
// If found they are stored in their class or in the global list.
static void buildTypedefList(EntryNav *rootNav)
{
//printf("buildVarList(%s)\n",rootNav->name().data());
if (!rootNav->name().isEmpty() &&
rootNav->section()==Entry::VARIABLE_SEC &&
rootNav->type().find("typedef ")!=-1 // its a typedef
)
{
addVariable(rootNav);
}
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
EntryNav *e;
for (;(e=eli.current());++eli)
{
if (e->section()!=Entry::ENUM_SEC)
{
buildTypedefList(e);
}
}
}
}
//----------------------------------------------------------------------
// Searches the Entry tree for Variable documentation sections.
// If found they are stored in their class or in the global list.
static void buildVarList(EntryNav *rootNav)
{
//printf("buildVarList(%s)\n",rootNav->name().data());
int isFuncPtr=-1;
if (!rootNav->name().isEmpty() &&
(rootNav->type().isEmpty() || g_compoundKeywordDict.find(rootNav->type())==0) &&
(
(rootNav->section()==Entry::VARIABLE_SEC // it's a variable
) ||
(rootNav->section()==Entry::FUNCTION_SEC && // or maybe a function pointer variable
(isFuncPtr=findFunctionPtr(rootNav->type()))!=-1
) ||
(rootNav->section()==Entry::FUNCTION_SEC && // class variable initialized by constructor
isVarWithConstructor(rootNav)
)
)
) // documented variable
{
addVariable(rootNav,isFuncPtr);
}
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
EntryNav *e;
for (;(e=eli.current());++eli)
{
if (e->section()!=Entry::ENUM_SEC)
{
buildVarList(e);
}
}
}
}
//----------------------------------------------------------------------
// Searches the Entry tree for Function sections.
// If found they are stored in their class or in the global list.
static void addMethodToClass(EntryNav *rootNav,ClassDef *cd,
const QCString &rname,bool isFriend)
{
Entry *root = rootNav->entry();
FileDef *fd=rootNav->fileDef();
int l,i=-1;
static QRegExp re("([a-z_A-Z0-9: ]*[ &*]+[ ]*");
if (!root->type.isEmpty() && (i=re.match(root->type,0,&l))!=-1) // function variable
{
root->args+=root->type.right(root->type.length()-i-l);
root->type=root->type.left(i+l);
}
QCString name=removeRedundantWhiteSpace(rname);
if (name.left(2)=="::") name=name.right(name.length()-2);
MemberDef::MemberType mtype;
if (isFriend) mtype=MemberDef::Friend;
else if (root->mtype==Signal) mtype=MemberDef::Signal;
else if (root->mtype==Slot) mtype=MemberDef::Slot;
else if (root->mtype==DCOP) mtype=MemberDef::DCOP;
else mtype=MemberDef::Function;
// strip redundant template specifier for constructors
if ((fd==0 || getLanguageFromFileName(fd->name())==SrcLangExt_Cpp) &&
name.left(9)!="operator " && (i=name.find('<'))!=-1 && name.find('>')!=-1)
{
name=name.left(i);
}
//printf("root->name=`%s; root->args=`%s' root->argList=`%s'\n",
// root->name.data(),root->args.data(),argListToString(root->argList).data()
// );
// adding class member
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
root->type,name,root->args,root->exception,
root->protection,root->virt,
root->stat && root->relatesType != MemberOf,
root->relates.isEmpty() ? Member :
root->relatesType == MemberOf ? Foreign : Related,
mtype,root->tArgLists ? root->tArgLists->last() : 0,root->argList);
md->setTagInfo(rootNav->tagInfo());
md->setMemberClass(cd);
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setDocsForDefinition(!root->proto);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setMemberSpecifiers(root->spec);
md->setMemberGroupId(root->mGrpId);
md->setTypeConstraints(root->typeConstr);
md->setBodyDef(fd);
md->setFileDef(fd);
//md->setScopeTemplateArguments(root->tArgList);
md->addSectionsToDefinition(root->anchors);
QCString def;
QCString qualScope = cd->qualifiedNameWithTemplateParameters();
QCString scopeSeparator="::";
if (Config_getBool("OPTIMIZE_OUTPUT_JAVA"))
{
qualScope = substitute(qualScope,"::",".");
scopeSeparator=".";
}
if (!root->relates.isEmpty() || isFriend || Config_getBool("HIDE_SCOPE_NAMES"))
{
if (!root->type.isEmpty())
{
if (root->argList)
{
def=root->type+" "+name;
}
else
{
def=root->type+" "+name+root->args;
}
}
else
{
if (root->argList)
{
def=name;
}
else
{
def=name+root->args;
}
}
}
else
{
if (!root->type.isEmpty())
{
if (root->argList)
{
def=root->type+" "+qualScope+scopeSeparator+name;
}
else
{
def=root->type+" "+qualScope+scopeSeparator+name+root->args;
}
}
else
{
if (root->argList)
{
def=qualScope+scopeSeparator+name;
}
else
{
def=qualScope+scopeSeparator+name+root->args;
}
}
}
if (def.left(7)=="friend ") def=def.right(def.length()-7);
md->setDefinition(def);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
Debug::print(Debug::Functions,0,
" Func Member:\n"
" `%s' `%s'::`%s' `%s' proto=%d\n"
" def=`%s'\n",
root->type.data(),
qualScope.data(),
rname.data(),
root->args.data(),
root->proto,
def.data()
);
// add member to the global list of all members
//printf("Adding member=%s class=%s\n",md->name().data(),cd->name().data());
MemberName *mn;
if ((mn=Doxygen::memberNameSDict->find(name)))
{
mn->append(md);
}
else
{
mn = new MemberName(name);
mn->append(md);
Doxygen::memberNameSDict->append(name,mn);
}
// add member to the class cd
cd->insertMember(md);
// add file to list of used files
cd->insertUsedFile(root->fileName);
addMemberToGroups(root,md);
rootNav->changeSection(Entry::EMPTY_SEC);
md->setRefItems(root->sli);
}
static void buildFunctionList(EntryNav *rootNav)
{
if (rootNav->section()==Entry::FUNCTION_SEC)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
Debug::print(Debug::Functions,0,
"FUNCTION_SEC:\n"
" `%s' `%s'::`%s' `%s' relates=`%s' relatesType=`%d' file=`%s' line=`%d' bodyLine=`%d' #tArgLists=%d mGrpId=%d spec=%d proto=%d docFile=%s\n",
root->type.data(),
rootNav->parent()->name().data(),
root->name.data(),
root->args.data(),
root->relates.data(),
root->relatesType,
root->fileName.data(),
root->startLine,
root->bodyLine,
root->tArgLists ? (int)root->tArgLists->count() : -1,
root->mGrpId,
root->spec,
root->proto,
root->docFile.data()
);
bool isFriend=root->type.find("friend ")!=-1;
QCString rname = removeRedundantWhiteSpace(root->name);
//printf("rname=%s\n",rname.data());
QCString scope=rootNav->parent()->name(); //stripAnonymousNamespaceScope(root->parent->name);
if (!rname.isEmpty() && scope.find('@')==-1)
{
ClassDef *cd=0;
// check if this function's parent is a class
scope=stripTemplateSpecifiersFromScope(scope,FALSE);
FileDef *rfd=rootNav->fileDef();
int memIndex=rname.findRev("::");
cd=getClass(scope);
if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
{
// strip scope from name
rname=rname.right(rname.length()-rootNav->parent()->name().length()-2);
}
NamespaceDef *nd = 0;
bool isMember=FALSE;
if (memIndex!=-1)
{
int ts=rname.find('<');
int te=rname.find('>');
if (memIndex>0 && (ts==-1 || te==-1))
{
// note: the following code was replaced by inMember=TRUE to deal with a
// function rname='X::foo' of class X inside a namespace also called X...
// bug id 548175
//nd = Doxygen::namespaceSDict->find(rname.left(memIndex));
//isMember = nd==0;
//if (nd)
//{
// // strip namespace scope from name
// scope=rname.left(memIndex);
// rname=rname.right(rname.length()-memIndex-2);
//}
isMember = TRUE;
}
else
{
isMember=memIndex<ts || memIndex>te;
}
}
static QRegExp re("([a-z_A-Z0-9: ]*[ &*]+[ ]*");
if (!rootNav->parent()->name().isEmpty() &&
(rootNav->parent()->section() & Entry::COMPOUND_MASK) &&
cd &&
// do some fuzzy things to exclude function pointers
(root->type.isEmpty() ||
(root->type.find(re,0)==-1 || root->args.find(")[")!=-1) || // type contains ..(..* and args not )[.. -> function pointer
root->type.find(")(")!=-1 || root->type.find("operator")!=-1 // type contains ..)(.. and not "operator"
)
)
{
Debug::print(Debug::Functions,0," --> member %s of class %s!\n",
rname.data(),cd->name().data());
addMethodToClass(rootNav,cd,rname,isFriend);
}
else if (!((rootNav->parent()->section() & Entry::COMPOUND_MASK)
|| rootNav->parent()->section()==Entry::OBJCIMPL_SEC
) &&
!isMember &&
(root->relates.isEmpty() || root->relatesType == Duplicate) &&
root->type.left(7)!="extern " && root->type.left(8)!="typedef "
)
// no member => unrelated function
{
/* check the uniqueness of the function name in the file.
* A file could contain a function prototype and a function definition
* or even multiple function prototypes.
*/
bool found=FALSE;
MemberName *mn;
MemberDef *md=0;
if ((mn=Doxygen::functionNameSDict->find(rname)))
{
Debug::print(Debug::Functions,0," --> function %s already found!\n",rname.data());
MemberNameIterator mni(*mn);
for (mni.toFirst();(!found && (md=mni.current()));++mni)
{
NamespaceDef *mnd = md->getNamespaceDef();
NamespaceDef *rnd = 0;
//printf("root namespace=%s\n",rootNav->parent()->name().data());
QCString fullScope = scope;
QCString parentScope = rootNav->parent()->name();
if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
{
if (!scope.isEmpty()) fullScope.prepend("::");
fullScope.prepend(parentScope);
}
//printf("fullScope=%s\n",fullScope.data());
rnd = getResolvedNamespace(fullScope);
FileDef *mfd = md->getFileDef();
QCString nsName,rnsName;
if (mnd) nsName = mnd->name().copy();
if (rnd) rnsName = rnd->name().copy();
//printf("matching arguments for %s%s %s%s\n",
// md->name().data(),md->argsString(),rname.data(),argListToString(root->argList).data());
LockingPtr<ArgumentList> mdAl = md->argumentList();
LockingPtr<ArgumentList> mdTempl = md->templateArguments();
// in case of template functions, we need to check if the
// functions have the same number of template parameters
bool sameNumTemplateArgs = TRUE;
if (mdTempl!=0 && root->tArgLists)
{
if (mdTempl->count()!=root->tArgLists->getLast()->count())
{
sameNumTemplateArgs = FALSE;
}
}
if (
matchArguments2(md->getOuterScope(),mfd,mdAl.pointer(),
rnd ? rnd : Doxygen::globalScope,rfd,root->argList,
FALSE) &&
sameNumTemplateArgs
)
{
GroupDef *gd=0;
if (root->groups->first()!=0)
{
gd = Doxygen::groupSDict->find(root->groups->first()->groupname.data());
}
//printf("match!\n");
//printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,nsName.data(),rnsName.data());
// see if we need to create a new member
found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
((mnd==0 && rnd==0 && mfd!=0 && // no external reference and
mfd->absFilePath()==root->fileName // prototype in the same file
)
);
// otherwise, allow a duplicate global member with the same argument list
if (!found && gd && gd==md->getGroupDef())
{
// member is already in the group, so we don't want to add it again.
found=TRUE;
}
//printf("combining function with prototype found=%d in namespace %s\n",
// found,nsName.data());
if (found)
{
// merge argument lists
mergeArguments(mdAl.pointer(),root->argList,!root->doc.isEmpty());
// merge documentation
if (md->documentation().isEmpty() && !root->doc.isEmpty())
{
ArgumentList *argList = new ArgumentList;
stringToArgumentList(root->args,argList);
if (root->proto)
{
//printf("setDeclArgumentList to %p\n",argList);
md->setDeclArgumentList(argList);
}
else
{
md->setArgumentList(argList);
}
}
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setDocsForDefinition(!root->proto);
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setBodyDef(rfd);
if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
{
md->setArgsString(root->args);
}
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->addSectionsToDefinition(root->anchors);
md->enableCallGraph(md->hasCallGraph() || root->callGraph);
md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
// merge ingroup specifiers
if (md->getGroupDef()==0 && root->groups->first()!=0)
{
addMemberToGroups(root,md);
}
else if (md->getGroupDef()!=0 && root->groups->count()==0)
{
//printf("existing member is grouped, new member not\n");
root->groups->append(new Grouping(md->getGroupDef()->name(), md->getGroupPri()));
}
else if (md->getGroupDef()!=0 && root->groups->first()!=0)
{
//printf("both members are grouped\n");
}
// if md is a declaration and root is the corresponding
// definition, then turn md into a definition.
if (md->isPrototype() && !root->proto)
{
md->setPrototype(FALSE);
}
}
}
}
}
if (!found) /* global function is unique with respect to the file */
{
Debug::print(Debug::Functions,0," --> new function %s found!\n",rname.data());
//printf("New function type=`%s' name=`%s' args=`%s' bodyLine=%d\n",
// root->type.data(),rname.data(),root->args.data(),root->bodyLine);
// new global function
ArgumentList *tArgList = root->tArgLists ? root->tArgLists->last() : 0;
QCString name=removeRedundantWhiteSpace(rname);
md=new MemberDef(
root->fileName,root->startLine,
root->type,name,root->args,root->exception,
root->protection,root->virt,root->stat,Member,
MemberDef::Function,tArgList,root->argList);
md->setTagInfo(rootNav->tagInfo());
//md->setDefFile(root->fileName);
//md->setDefLine(root->startLine);
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setPrototype(root->proto);
md->setDocsForDefinition(!root->proto);
md->setTypeConstraints(root->typeConstr);
//md->setBody(root->body);
md->setBodySegment(root->bodyLine,root->endBodyLine);
FileDef *fd=rootNav->fileDef();
md->setBodyDef(fd);
md->addSectionsToDefinition(root->anchors);
md->setMemberSpecifiers(root->spec);
md->setMemberGroupId(root->mGrpId);
// see if the function is inside a namespace that was not part of
// the name already (in that case nd should be non-zero already)
if (nd==0 && rootNav->parent()->section() == Entry::NAMESPACE_SEC )
{
//QCString nscope=removeAnonymousScopes(rootNav->parent()->name());
QCString nscope=rootNav->parent()->name();
if (!nscope.isEmpty())
{
nd = getResolvedNamespace(nscope);
}
}
if (!scope.isEmpty())
{
if (Config_getBool("OPTIMIZE_OUTPUT_JAVA"))
{
scope = substitute(scope,"::",".")+".";
}
else
{
scope+="::";
}
}
QCString def;
if (!root->type.isEmpty())
{
if (root->argList)
{
def=root->type+" "+scope+name;
}
else
{
def=root->type+" "+scope+name+root->args;
}
}
else
{
if (root->argList)
{
def=scope+name.copy();
}
else
{
def=scope+name+root->args;
}
}
Debug::print(Debug::Functions,0,
" Global Function:\n"
" `%s' `%s'::`%s' `%s' proto=%d\n"
" def=`%s'\n",
root->type.data(),
rootNav->parent()->name().data(),
rname.data(),
root->args.data(),
root->proto,
def.data()
);
md->setDefinition(def);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
//if (root->mGrpId!=-1)
//{
// md->setMemberGroup(memberGroupDict[root->mGrpId]);
//}
md->setRefItems(root->sli);
if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
{
// add member to namespace
md->setNamespace(nd);
nd->insertMember(md);
}
if (fd)
{
// add member to the file (we do this even if we have already
// inserted it into the namespace)
md->setFileDef(fd);
fd->insertMember(md);
}
// add member to the list of file members
//printf("Adding member=%s\n",md->name().data());
MemberName *mn;
if ((mn=Doxygen::functionNameSDict->find(name)))
{
mn->append(md);
}
else
{
mn = new MemberName(name);
mn->append(md);
Doxygen::functionNameSDict->append(name,mn);
}
addMemberToGroups(root,md);
if (root->relatesType == Simple) // if this is a relatesalso command,
// allow find Member to pick it up
{
rootNav->changeSection(Entry::EMPTY_SEC); // Otherwise we have finished
// with this entry.
}
}
else
{
FileDef *fd=rootNav->fileDef();
if (fd)
{
// add member to the file (we do this even if we have already
// inserted it into the namespace)
fd->insertMember(md);
}
}
//printf("unrelated function %d `%s' `%s' `%s'\n",
// root->parent->section,root->type.data(),rname.data(),root->args.data());
}
else
{
Debug::print(Debug::Functions,0," --> %s not processed!\n",rname.data());
}
}
else if (rname.isEmpty())
{
warn(root->fileName,root->startLine,
"Warning: Illegal member name found."
);
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(buildFunctionList,rootNav);
}
//----------------------------------------------------------------------
static void findFriends()
{
//printf("findFriends()\n");
MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict);
MemberName *fn;
for (;(fn=fnli.current());++fnli) // for each global function name
{
//printf("Function name=`%s'\n",fn->memberName());
MemberName *mn;
if ((mn=Doxygen::memberNameSDict->find(fn->memberName())))
{ // there are members with the same name
//printf("Function name is also a member name\n");
MemberNameIterator fni(*fn);
MemberDef *fmd;
for (;(fmd=fni.current());++fni) // for each function with that name
{
MemberNameIterator mni(*mn);
MemberDef *mmd;
for (;(mmd=mni.current());++mni) // for each member with that name
{
//printf("Checking for matching arguments
// mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
// mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
LockingPtr<ArgumentList> fmdAl = fmd->argumentList();
if ((mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmdAl.pointer(),
fmd->getOuterScope(), fmd->getFileDef(), fmdAl.pointer(),
TRUE
)
) // if the member is related and the arguments match then the
// function is actually a friend.
{
mergeArguments(mmdAl.pointer(),fmdAl.pointer());
if (!fmd->documentation().isEmpty())
{
mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
}
else if (!mmd->documentation().isEmpty())
{
fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
}
if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
{
mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
}
else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
{
fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
}
if (!fmd->inbodyDocumentation().isEmpty())
{
mmd->setInbodyDocumentation(fmd->inbodyDocumentation(),fmd->inbodyFile(),fmd->inbodyLine());
}
else if (!mmd->inbodyDocumentation().isEmpty())
{
fmd->setInbodyDocumentation(mmd->inbodyDocumentation(),mmd->inbodyFile(),mmd->inbodyLine());
}
//printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
{
mmd->setBodySegment(fmd->getStartBodyLine(),fmd->getEndBodyLine());
mmd->setBodyDef(fmd->getBodyDef());
//mmd->setBodyMember(fmd);
}
else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
{
fmd->setBodySegment(mmd->getStartBodyLine(),mmd->getEndBodyLine());
fmd->setBodyDef(mmd->getBodyDef());
//fmd->setBodyMember(mmd);
}
mmd->setDocsForDefinition(fmd->isDocsForDefinition());
mmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
mmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
fmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
fmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
}
}
}
}
}
}
//----------------------------------------------------------------------
static void transferArgumentDocumentation(ArgumentList *decAl,ArgumentList *defAl)
{
if (decAl && defAl)
{
ArgumentListIterator decAli(*decAl);
ArgumentListIterator defAli(*defAl);
Argument *decA,*defA;
for (decAli.toFirst(),defAli.toFirst();
(decA=decAli.current()) && (defA=defAli.current());
++decAli,++defAli)
{
//printf("Argument decA->name=%s (doc=%s) defA->name=%s (doc=%s)\n",
// decA->name.data(),decA->docs.data(),
// defA->name.data(),defA->docs.data()
// );
if (decA->docs.isEmpty() && !defA->docs.isEmpty())
{
decA->docs = defA->docs.copy();
}
else if (defA->docs.isEmpty() && !decA->docs.isEmpty())
{
defA->docs = decA->docs.copy();
}
}
}
}
static void transferFunctionDocumentation()
{
//printf("---- transferFunctionDocumentation()\n");
// find matching function declaration and definitions.
MemberNameSDict::Iterator mnli(*Doxygen::functionNameSDict);
MemberName *mn;
for (;(mn=mnli.current());++mnli)
{
//printf("memberName=%s count=%d\n",mn->memberName(),mn->count());
MemberDef *mdef=0,*mdec=0;
MemberNameIterator mni1(*mn);
/* find a matching function declaration and definition for this function */
for (;(mdec=mni1.current());++mni1)
{
//printf("mdec=%s isPrototype()=%d\n",mdec->name().data(),mdec->isPrototype());
if (mdec->isPrototype() ||
(mdec->isVariable() && mdec->isExternal())
)
{
MemberNameIterator mni2(*mn);
for (;(mdef=mni2.current());++mni2)
{
if (
(mdef->isFunction() && !mdef->isStatic() && !mdef->isPrototype()) ||
(mdef->isVariable() && !mdef->isExternal() && !mdef->isStatic())
)
{
//printf("mdef=(%p,%s) mdec=(%p,%s)\n",
// mdef, mdef ? mdef->name().data() : "",
// mdec, mdec ? mdec->name().data() : "");
LockingPtr<ArgumentList> mdefAl = mdef->argumentList();
LockingPtr<ArgumentList> mdecAl = mdec->argumentList();
if (matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdefAl.pointer(),
mdec->getOuterScope(),mdec->getFileDef(),mdecAl.pointer(),
TRUE
)
) /* match found */
{
//printf("Found member %s: definition in %s (doc=`%s') and declaration in %s (doc=`%s')\n",
// mn->memberName(),
// mdef->getFileDef()->name().data(),mdef->documentation().data(),
// mdec->getFileDef()->name().data(),mdec->documentation().data()
// );
// first merge argument documentation
transferArgumentDocumentation(mdecAl.pointer(),mdefAl.pointer());
/* copy documentation between function definition and declaration */
if (!mdec->briefDescription().isEmpty())
{
mdef->setBriefDescription(mdec->briefDescription(),mdec->briefFile(),mdec->briefLine());
}
else if (!mdef->briefDescription().isEmpty())
{
mdec->setBriefDescription(mdef->briefDescription(),mdef->briefFile(),mdef->briefLine());
}
if (!mdef->documentation().isEmpty())
{
//printf("transfering docs mdef->mdec (%s->%s)\n",mdef->argsString(),mdec->argsString());
mdec->setDocumentation(mdef->documentation(),mdef->docFile(),mdef->docLine());
mdec->setDocsForDefinition(mdef->isDocsForDefinition());
if (mdefAl!=0)
{
ArgumentList *mdefAlComb = new ArgumentList;
stringToArgumentList(mdef->argsString(),mdefAlComb);
transferArgumentDocumentation(mdefAl.pointer(),mdefAlComb);
mdec->setArgumentList(mdefAlComb);
}
}
else if (!mdec->documentation().isEmpty())
{
//printf("transfering docs mdec->mdef (%s->%s)\n",mdec->argsString(),mdef->argsString());
mdef->setDocumentation(mdec->documentation(),mdec->docFile(),mdec->docLine());
mdef->setDocsForDefinition(mdec->isDocsForDefinition());
if (mdecAl!=0)
{
ArgumentList *mdecAlComb = new ArgumentList;
stringToArgumentList(mdec->argsString(),mdecAlComb);
transferArgumentDocumentation(mdecAl.pointer(),mdecAlComb);
mdef->setDeclArgumentList(mdecAlComb);
}
}
if (!mdef->inbodyDocumentation().isEmpty())
{
mdec->setInbodyDocumentation(mdef->inbodyDocumentation(),mdef->inbodyFile(),mdef->inbodyLine());
}
else if (!mdec->inbodyDocumentation().isEmpty())
{
mdef->setInbodyDocumentation(mdec->inbodyDocumentation(),mdec->inbodyFile(),mdec->inbodyLine());
}
if (mdec->getStartBodyLine()!=-1 && mdef->getStartBodyLine()==-1)
{
//printf("body mdec->mdef %d-%d\n",mdec->getStartBodyLine(),mdef->getEndBodyLine());
mdef->setBodySegment(mdec->getStartBodyLine(),mdec->getEndBodyLine());
mdef->setBodyDef(mdec->getBodyDef());
//mdef->setBodyMember(mdec);
}
else if (mdef->getStartBodyLine()!=-1 && mdec->getStartBodyLine()==-1)
{
//printf("body mdef->mdec %d-%d\n",mdef->getStartBodyLine(),mdec->getEndBodyLine());
mdec->setBodySegment(mdef->getStartBodyLine(),mdef->getEndBodyLine());
mdec->setBodyDef(mdef->getBodyDef());
//mdec->setBodyMember(mdef);
}
mdec->mergeMemberSpecifiers(mdef->getMemberSpecifiers());
mdef->mergeMemberSpecifiers(mdec->getMemberSpecifiers());
// copy group info.
if (mdec->getGroupDef()==0 && mdef->getGroupDef()!=0)
{
mdec->setGroupDef(mdef->getGroupDef(),
mdef->getGroupPri(),
mdef->docFile(),
mdef->docLine(),
mdef->hasDocumentation(),
mdef
);
}
else if (mdef->getGroupDef()==0 && mdec->getGroupDef()!=0)
{
mdef->setGroupDef(mdec->getGroupDef(),
mdec->getGroupPri(),
mdec->docFile(),
mdec->docLine(),
mdec->hasDocumentation(),
mdec
);
}
mdec->mergeRefItems(mdef);
mdef->mergeRefItems(mdec);
mdef->setMemberDeclaration(mdec);
mdec->setMemberDefinition(mdef);
mdef->enableCallGraph(mdec->hasCallGraph() || mdef->hasCallGraph());
mdef->enableCallerGraph(mdec->hasCallerGraph() || mdef->hasCallerGraph());
mdec->enableCallGraph(mdec->hasCallGraph() || mdef->hasCallGraph());
mdec->enableCallerGraph(mdec->hasCallerGraph() || mdef->hasCallerGraph());
}
}
}
}
}
}
}
//----------------------------------------------------------------------
static void transferFunctionReferences()
{
MemberNameSDict::Iterator mnli(*Doxygen::functionNameSDict);
MemberName *mn;
for (;(mn=mnli.current());++mnli)
{
MemberDef *md,*mdef=0,*mdec=0;
MemberNameIterator mni(*mn);
/* find a matching function declaration and definition for this function */
for (;(md=mni.current());++mni)
{
if (md->isPrototype())
mdec=md;
else if (md->isVariable() && md->isExternal())
mdec=md;
if (md->isFunction() && !md->isStatic() && !md->isPrototype())
mdef=md;
else if (md->isVariable() && !md->isExternal() && !md->isStatic())
mdef=md;
}
if (mdef && mdec)
{
LockingPtr<ArgumentList> mdefAl = mdef->argumentList();
LockingPtr<ArgumentList> mdecAl = mdec->argumentList();
if (
matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdefAl.pointer(),
mdec->getOuterScope(),mdec->getFileDef(),mdecAl.pointer(),
TRUE
)
) /* match found */
{
LockingPtr<MemberSDict> defDict = mdef->getReferencesMembers();
LockingPtr<MemberSDict> decDict = mdec->getReferencesMembers();
if (defDict!=0)
{
MemberSDict::Iterator msdi(*defDict);
MemberDef *rmd;
for (msdi.toFirst();(rmd=msdi.current());++msdi)
{
if (decDict==0 || decDict->find(rmd->name())==0)
{
mdec->addSourceReferences(rmd);
}
}
}
if (decDict!=0)
{
MemberSDict::Iterator msdi(*decDict);
MemberDef *rmd;
for (msdi.toFirst();(rmd=msdi.current());++msdi)
{
if (defDict==0 || defDict->find(rmd->name())==0)
{
mdef->addSourceReferences(rmd);
}
}
}
defDict = mdef->getReferencedByMembers();
decDict = mdec->getReferencedByMembers();
if (defDict!=0)
{
MemberSDict::Iterator msdi(*defDict);
MemberDef *rmd;
for (msdi.toFirst();(rmd=msdi.current());++msdi)
{
if (decDict==0 || decDict->find(rmd->name())==0)
{
mdec->addSourceReferencedBy(rmd);
}
}
}
if (decDict!=0)
{
MemberSDict::Iterator msdi(*decDict);
MemberDef *rmd;
for (msdi.toFirst();(rmd=msdi.current());++msdi)
{
if (defDict==0 || defDict->find(rmd->name())==0)
{
mdef->addSourceReferencedBy(rmd);
}
}
}
}
}
}
}
//----------------------------------------------------------------------
static void transferRelatedFunctionDocumentation()
{
// find match between function declaration and definition for
// related functions
MemberNameSDict::Iterator mnli(*Doxygen::functionNameSDict);
MemberName *mn;
for (mnli.toFirst();(mn=mnli.current());++mnli)
{
MemberDef *md;
MemberNameIterator mni(*mn);
/* find a matching function declaration and definition for this function */
for (mni.toFirst();(md=mni.current());++mni) // for each global function
{
//printf(" Function `%s'\n",md->name().data());
MemberName *rmn;
if ((rmn=Doxygen::memberNameSDict->find(md->name()))) // check if there is a member with the same name
{
//printf(" Member name found\n");
MemberDef *rmd;
MemberNameIterator rmni(*rmn);
for (rmni.toFirst();(rmd=rmni.current());++rmni) // for each member with the same name
{
LockingPtr<ArgumentList> mdAl = md->argumentList();
LockingPtr<ArgumentList> rmdAl = rmd->argumentList();
//printf(" Member found: related=`%d'\n",rmd->isRelated());
if ((rmd->isRelated() || rmd->isForeign()) && // related function
matchArguments2( md->getOuterScope(), md->getFileDef(), mdAl.pointer(),
rmd->getOuterScope(),rmd->getFileDef(),rmdAl.pointer(),
TRUE
)
)
{
//printf(" Found related member `%s'\n",md->name().data());
if (rmd->relatedAlso())
md->setRelatedAlso(rmd->relatedAlso());
else if (rmd->isForeign())
md->makeForeign();
else
md->makeRelated();
}
}
}
}
}
}
//----------------------------------------------------------------------
/*! make a dictionary of all template arguments of class cd
* that are part of the base class name.
* Example: A template class A with template arguments <R,S,T>
* that inherits from B<T,T,S> will have T and S in the dictionary.
*/
static QDict<int> *getTemplateArgumentsInName(ArgumentList *templateArguments,const QCString &name)
{
QDict<int> *templateNames = new QDict<int>(17);
templateNames->setAutoDelete(TRUE);
static QRegExp re("[a-z_A-Z][a-z_A-Z0-9:]*");
if (templateArguments)
{
ArgumentListIterator ali(*templateArguments);
Argument *arg;
int count=0;
for (ali.toFirst();(arg=ali.current());++ali,count++)
{
int i,p=0,l;
while ((i=re.match(name,p,&l))!=-1)
{
QCString n = name.mid(i,l);
if (n==arg->name)
{
if (templateNames->find(n)==0)
{
templateNames->insert(n,new int(count));
}
}
p=i+l;
}
}
}
return templateNames;
}
/*! Searches a class from within \a context and \a cd and returns its
* definition if found (otherwise 0 is returned).
*/
static ClassDef *findClassWithinClassContext(Definition *context,ClassDef *cd,const QCString &name)
{
FileDef *fd=cd->getFileDef();
ClassDef *result=0;
if (context && cd!=context)
{
result = getResolvedClass(context,0,name,0,0,TRUE,TRUE);
}
if (result==0)
{
result = getResolvedClass(cd,fd,name,0,0,TRUE,TRUE);
}
//printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
// name.data(),
// context ? context->name().data() : "<none>",
// cd ? cd->name().data() : "<none>",
// result ? result->name().data() : "<none>",
// Doxygen::classSDict.find(name)
// );
return result;
}
enum FindBaseClassRelation_Mode
{
TemplateInstances,
DocumentedOnly,
Undocumented
};
static bool findClassRelation(
EntryNav *rootNav,
Definition *context,
ClassDef *cd,
BaseInfo *bi,
QDict<int> *templateNames,
/*bool insertUndocumented*/
FindBaseClassRelation_Mode mode,
bool isArtificial
);
static void findUsedClassesForClass(EntryNav *rootNav,
Definition *context,
ClassDef *masterCd,
ClassDef *instanceCd,
bool isArtificial,
ArgumentList *actualArgs=0,
QDict<int> *templateNames=0
)
{
masterCd->visited=TRUE;
ArgumentList *formalArgs = masterCd->templateArguments();
if (masterCd->memberNameInfoSDict())
{
MemberNameInfoSDict::Iterator mnili(*masterCd->memberNameInfoSDict());
MemberNameInfo *mni;
for (;(mni=mnili.current());++mnili)
{
MemberNameInfoIterator mnii(*mni);
MemberInfo *mi;
for (mnii.toFirst();(mi=mnii.current());++mnii)
{
MemberDef *md=mi->memberDef;
if (md->isVariable()) // for each member variable in this class
{
//printf(" Found variable %s in class %s\n",md->name().data(),masterCd->name().data());
QCString type=removeRedundantWhiteSpace(md->typeString());
QCString typedefValue = resolveTypeDef(masterCd,type);
if (!typedefValue.isEmpty())
{
type = typedefValue;
}
int pos=0;
QCString usedClassName;
QCString templSpec;
bool found=FALSE;
// the type can contain template variables, replace them if present
if (actualArgs)
{
type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
}
//printf(" template substitution gives=%s\n",type.data());
while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec)!=-1)
{
// find the type (if any) that matches usedClassName
ClassDef *typeCd = getResolvedClass(masterCd,
masterCd->getFileDef(),
usedClassName,
0,0,
FALSE,TRUE
);
//printf("====> usedClassName=%s -> typeCd=%s\n",
// usedClassName.data(),typeCd?typeCd->name().data():"<none>");
if (typeCd)
{
usedClassName = typeCd->name();
}
int sp=usedClassName.find('<');
if (sp==-1) sp=0;
int si=usedClassName.findRev("::",sp);
if (si!=-1)
{
// replace any namespace aliases
replaceNamespaceAliases(usedClassName,si);
}
// add any template arguments to the class
QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
//printf(" usedName=%s\n",usedName.data());
bool delTempNames=FALSE;
if (templateNames==0)
{
templateNames = getTemplateArgumentsInName(formalArgs,usedName);
delTempNames=TRUE;
}
BaseInfo bi(usedName,Public,Normal);
findClassRelation(rootNav,context,instanceCd,&bi,templateNames,TemplateInstances,isArtificial);
if (masterCd->templateArguments())
{
ArgumentListIterator ali(*masterCd->templateArguments());
Argument *arg;
int count=0;
for (ali.toFirst();(arg=ali.current());++ali,++count)
{
if (arg->name==usedName) // type is a template argument
{
found=TRUE;
Debug::print(Debug::Classes,0," New used class `%s'\n", usedName.data());
ClassDef *usedCd = Doxygen::hiddenClasses->find(usedName);
if (usedCd==0)
{
usedCd = new ClassDef(
masterCd->getDefFileName(),masterCd->getDefLine(),
usedName,ClassDef::Class);
//printf("making %s a template argument!!!\n",usedCd->name().data());
usedCd->makeTemplateArgument();
usedCd->setUsedOnly(TRUE);
Doxygen::hiddenClasses->append(usedName,usedCd);
}
if (usedCd)
{
if (isArtificial) usedCd->setArtificial(TRUE);
Debug::print(Debug::Classes,0," Adding used class `%s' (1)\n", usedCd->name().data());
instanceCd->addUsedClass(usedCd,md->name());
usedCd->addUsedByClass(instanceCd,md->name());
}
}
}
}
if (!found)
{
ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
//printf("Looking for used class %s: result=%s master=%s\n",
// usedName.data(),usedCd?usedCd->name().data():"<none>",masterCd?masterCd->name().data():"<none>");
if (usedCd)
{
found=TRUE;
Debug::print(Debug::Classes,0," Adding used class `%s' (2)\n", usedCd->name().data());
instanceCd->addUsedClass(usedCd,md->name()); // class exists
usedCd->addUsedByClass(instanceCd,md->name());
}
}
if (delTempNames)
{
delete templateNames;
templateNames=0;
}
}
if (!found && !type.isEmpty()) // used class is not documented in any scope
{
ClassDef *usedCd = Doxygen::hiddenClasses->find(type);
if (usedCd==0 && !Config_getBool("HIDE_UNDOC_RELATIONS"))
{
if (type.right(2)=="(*") // type is a function pointer
{
type+=md->argsString();
}
Debug::print(Debug::Classes,0," New undocumented used class `%s'\n", type.data());
usedCd = new ClassDef(
masterCd->getDefFileName(),masterCd->getDefLine(),
type,ClassDef::Class);
usedCd->setUsedOnly(TRUE);
Doxygen::hiddenClasses->append(type,usedCd);
}
if (usedCd)
{
if (isArtificial) usedCd->setArtificial(TRUE);
Debug::print(Debug::Classes,0," Adding used class `%s' (3)\n", usedCd->name().data());
instanceCd->addUsedClass(usedCd,md->name());
usedCd->addUsedByClass(instanceCd,md->name());
}
}
}
}
}
}
else
{
//printf("no members for class %s (%p)\n",masterCd->name().data(),masterCd);
}
}
static void findBaseClassesForClass(
EntryNav *rootNav,
Definition *context,
ClassDef *masterCd,
ClassDef *instanceCd,
FindBaseClassRelation_Mode mode,
bool isArtificial,
ArgumentList *actualArgs=0,
QDict<int> *templateNames=0
)
{
Entry *root = rootNav->entry();
//if (masterCd->visited) return;
masterCd->visited=TRUE;
// The base class could ofcouse also be a non-nested class
ArgumentList *formalArgs = masterCd->templateArguments();
QListIterator<BaseInfo> bii(*root->extends);
BaseInfo *bi=0;
for (bii.toFirst();(bi=bii.current());++bii)
{
//printf("masterCd=%s bi->name='%s' #actualArgs=%d\n",
// masterCd->localName().data(),bi->name.data(),actualArgs?(int)actualArgs->count():-1);
bool delTempNames=FALSE;
if (templateNames==0)
{
templateNames = getTemplateArgumentsInName(formalArgs,bi->name);
delTempNames=TRUE;
}
BaseInfo tbi(bi->name,bi->prot,bi->virt);
if (actualArgs) // substitute the formal template arguments of the base class
{
tbi.name = substituteTemplateArgumentsInString(bi->name,formalArgs,actualArgs);
}
//printf("bi->name=%s tbi.name=%s\n",bi->name.data(),tbi.name.data());
if (mode==DocumentedOnly)
{
// find a documented base class in the correct scope
if (!findClassRelation(rootNav,context,instanceCd,&tbi,templateNames,DocumentedOnly,isArtificial))
{
if (!Config_getBool("HIDE_UNDOC_RELATIONS"))
{
// no documented base class -> try to find an undocumented one
findClassRelation(rootNav,context,instanceCd,&tbi,templateNames,Undocumented,isArtificial);
}
}
}
else if (mode==TemplateInstances)
{
findClassRelation(rootNav,context,instanceCd,&tbi,templateNames,TemplateInstances,isArtificial);
}
if (delTempNames)
{
delete templateNames;
templateNames=0;
}
}
}
//----------------------------------------------------------------------
static bool findTemplateInstanceRelation(Entry *root,
Definition *context,
ClassDef *templateClass,const QCString &templSpec,
QDict<int> *templateNames,
bool isArtificial)
{
Debug::print(Debug::Classes,0," derived from template %s with parameters %s\n",
templateClass->name().data(),templSpec.data());
//printf("findTemplateInstanceRelation(base=%s templSpec=%s templateNames=",
// templateClass->name().data(),templSpec.data());
//if (templateNames)
//{
// QDictIterator<int> qdi(*templateNames);
// int *tempArgIndex;
// for (;(tempArgIndex=qdi.current());++qdi)
// {
// printf("(%s->%d) ",qdi.currentKey().data(),*tempArgIndex);
// }
//}
//printf("\n");
bool existingClass = (templSpec ==
tempArgListToString(templateClass->templateArguments())
);
if (existingClass) return TRUE;
bool freshInstance=FALSE;
ClassDef *instanceClass = templateClass->insertTemplateInstance(
root->fileName,root->startLine,templSpec,freshInstance);
if (isArtificial) instanceClass->setArtificial(TRUE);
instanceClass->setIsObjectiveC(root->objc);
if (freshInstance)
{
Debug::print(Debug::Classes,0," found fresh instance '%s'!\n",instanceClass->name().data());
Doxygen::classSDict->append(instanceClass->name(),instanceClass);
instanceClass->setTemplateBaseClassNames(templateNames);
// search for new template instances caused by base classes of
// instanceClass
EntryNav *templateRootNav = g_classEntries.find(templateClass->name());
if (templateRootNav)
{
bool unloadNeeded=FALSE;
Entry *templateRoot = templateRootNav->entry();
if (templateRoot==0) // not yet loaded
{
templateRootNav->loadEntry(g_storage);
templateRoot = templateRootNav->entry();
ASSERT(templateRoot!=0); // now it should really be loaded
unloadNeeded=TRUE;
}
Debug::print(Debug::Classes,0," template root found %s templSpec=%s!\n",
templateRoot->name.data(),templSpec.data());
ArgumentList *templArgs = new ArgumentList;
stringToArgumentList(templSpec,templArgs);
findBaseClassesForClass(templateRootNav,context,templateClass,instanceClass,
TemplateInstances,isArtificial,templArgs,templateNames);
findUsedClassesForClass(templateRootNav,context,templateClass,instanceClass,
isArtificial,templArgs,templateNames);
delete templArgs;
if (unloadNeeded) // still cleanup to do
{
templateRootNav->releaseEntry();
}
}
else
{
Debug::print(Debug::Classes,0," no template root entry found!\n");
// TODO: what happened if we get here?
}
//Debug::print(Debug::Classes,0," Template instance %s : \n",instanceClass->name().data());
//ArgumentList *tl = templateClass->templateArguments();
}
else
{
Debug::print(Debug::Classes,0," instance already exists!\n");
}
return TRUE;
}
static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
{
QCString n=name;
int index=n.find('<');
if (index!=-1)
{
n=n.left(index);
}
bool result = rightScopeMatch(scope,n);
return result;
}
/*! Searches for the end of a template in prototype \a s starting from
* character position \a startPos. If the end was found the position
* of the closing \> is returned, otherwise -1 is returned.
*
* Handles exotic cases such as
* \code
* Class<(id<0)>
* Class<bits<<2>
* Class<"<">
* Class<'<'>
* Class<(")<")>
* \endcode
*/
static int findEndOfTemplate(const QCString &s,int startPos)
{
// locate end of template
int e=startPos;
int brCount=1;
int roundCount=0;
int len = s.length();
bool insideString=FALSE;
bool insideChar=FALSE;
char pc = 0;
while (e<len && brCount!=0)
{
char c=s.at(e);
switch(c)
{
case '<':
if (!insideString && !insideChar)
{
if (e<len-1 && s.at(e+1)=='<')
e++;
else if (roundCount==0)
brCount++;
}
break;
case '>':
if (!insideString && !insideChar)
{
if (e<len-1 && s.at(e+1)=='>')
e++;
else if (roundCount==0)
brCount--;
}
break;
case '(':
if (!insideString && !insideChar)
roundCount++;
break;
case ')':
if (!insideString && !insideChar)
roundCount--;
break;
case '"':
if (!insideChar)
{
if (insideString && pc!='\\')
insideString=FALSE;
else
insideString=TRUE;
}
break;
case '\'':
if (!insideString)
{
if (insideChar && pc!='\\')
insideChar=FALSE;
else
insideChar=TRUE;
}
break;
}
pc = c;
e++;
}
return brCount==0 ? e : -1;
}
static bool findClassRelation(
EntryNav *rootNav,
Definition *context,
ClassDef *cd,
BaseInfo *bi,
QDict<int> *templateNames,
FindBaseClassRelation_Mode mode,
bool isArtificial
)
{
//printf("findClassRelation(class=%s base=%s templateNames=",
// cd->name().data(),bi->name.data());
//if (templateNames)
//{
// QDictIterator<int> qdi(*templateNames);
// int *tempArgIndex;
// for (;(tempArgIndex=qdi.current());++qdi)
// {
// printf("(%s->%d) ",qdi.currentKey(),*tempArgIndex);
// }
//}
//printf("\n");
Entry *root = rootNav->entry();
QCString biName=bi->name;
bool explicitGlobalScope=FALSE;
//printf("findClassRelation: biName=`%s'\n",biName.data());
if (biName.left(2)=="::") // explicit global scope
{
biName=biName.right(biName.length()-2);
explicitGlobalScope=TRUE;
}
EntryNav *parentNode=rootNav->parent();
bool lastParent=FALSE;
do // for each parent scope, starting with the largest scope
// (in case of nested classes)
{
QCString scopeName= parentNode ? parentNode->name().data() : "";
int scopeOffset=explicitGlobalScope ? 0 : scopeName.length();
do // try all parent scope prefixes, starting with the largest scope
{
//printf("scopePrefix=`%s' biName=`%s'\n",
// scopeName.left(scopeOffset).data(),biName.data());
QCString baseClassName=biName;
if (scopeOffset>0)
{
baseClassName.prepend(scopeName.left(scopeOffset)+"::");
}
//QCString stripped;
//baseClassName=stripTemplateSpecifiersFromScope
// (removeRedundantWhiteSpace(baseClassName),TRUE,
// &stripped);
MemberDef *baseClassTypeDef=0;
QCString templSpec;
ClassDef *baseClass=getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context,
cd->getFileDef(),
baseClassName,
&baseClassTypeDef,
&templSpec,
mode==Undocumented,
TRUE
);
//printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
// baseClassName.data(),baseClass,cd,explicitGlobalScope);
//printf(" scope=`%s' baseClassName=`%s' baseClass=%s templSpec=%s\n",
// cd ? cd->name().data():"<none>",
// baseClassName.data(),
// baseClass?baseClass->name().data():"<none>",
// templSpec.data()
// );
//if (baseClassName.left(root->name.length())!=root->name ||
// baseClassName.at(root->name.length())!='<'
// ) // Check for base class with the same name.
// // If found then look in the outer scope for a match
// // and prevent recursion.
if (!isRecursiveBaseClass(rootNav->name(),baseClassName) || explicitGlobalScope)
{
Debug::print(
Debug::Classes,0," class relation %s inherited/used by %s found (%s and %s) templSpec='%s'\n",
baseClassName.data(),
rootNav->name().data(),
(bi->prot==Private)?"private":((bi->prot==Protected)?"protected":"public"),
(bi->virt==Normal)?"normal":"virtual",
templSpec.data()
);
int i=baseClassName.find('<');
int si=baseClassName.findRev("::",i==-1 ? baseClassName.length() : i);
if (si==-1) si=0;
if (baseClass==0 && i!=-1)
// base class has template specifiers
{
// TODO: here we should try to find the correct template specialization
// but for now, we only look for the unspecializated base class.
int e=findEndOfTemplate(baseClassName,i+1);
//printf("baseClass==0 i=%d e=%d\n",i,e);
if (e!=-1) // end of template was found at e
{
templSpec=removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
baseClassName=baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
baseClass=getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context,
cd->getFileDef(),
baseClassName,
&baseClassTypeDef,
0, //&templSpec,
mode==Undocumented,
TRUE
);
//printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
// baseClass,baseClassName.data(),templSpec.data());
}
}
else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
// know it is a template, so see if
// we can also link to the explicit
// instance (for instance if a class
// derived from a template argument)
{
//printf("baseClass=%p templSpec=%s\n",baseClass,templSpec.data());
ClassDef *templClass=getClass(baseClass->name()+templSpec);
if (templClass)
{
// use the template instance instead of the template base.
baseClass = templClass;
templSpec.resize(0);
}
}
//printf("cd=%p baseClass=%p\n",cd,baseClass);
bool found=baseClass!=0 && (baseClass!=cd || mode==TemplateInstances);
//printf("1. found=%d\n",found);
if (!found && si!=-1)
{
QCString tmpTemplSpec;
// replace any namespace aliases
replaceNamespaceAliases(baseClassName,si);
baseClass=getResolvedClass(explicitGlobalScope ? Doxygen::globalScope : context,
cd->getFileDef(),
baseClassName,
&baseClassTypeDef,
&tmpTemplSpec,
mode==Undocumented,
TRUE
);
found=baseClass!=0 && baseClass!=cd;
if (found) templSpec = tmpTemplSpec;
}
//printf("2. found=%d\n",found);
//printf("root->name=%s biName=%s baseClassName=%s\n",
// root->name.data(),biName.data(),baseClassName.data());
if (!found)
{
baseClass=findClassWithinClassContext(context,cd,baseClassName);
//printf("findClassWithinClassContext(%s,%s)=%p\n",
// cd->name().data(),baseClassName.data(),baseClass);
found = baseClass!=0 && baseClass!=cd;
}
bool isATemplateArgument = templateNames!=0 && templateNames->find(biName)!=0;
// make templSpec canonical
// Warning: the following line doesn't work for Mixin classes (see bug 560623)
// templSpec = getCanonicalTemplateSpec(cd, cd->getFileDef(), templSpec);
//printf("3. found=%d\n",found);
if (found)
{
Debug::print(Debug::Classes,0," Documented base class `%s' templSpec=%s\n",biName.data(),templSpec.isEmpty()?"":templSpec.data());
// add base class to this class
// if templSpec is not empty then we should "instantiate"
// the template baseClass. A new ClassDef should be created
// to represent the instance. To be able to add the (instantiated)
// members and documentation of a template class
// (inserted in that template class at a later stage),
// the template should know about its instances.
// the instantiation process, should be done in a recursive way,
// since instantiating a template may introduce new inheritance
// relations.
if (!templSpec.isEmpty() && mode==TemplateInstances)
{
// if baseClass is actually a typedef then we should not
// instantiate it, since typedefs are in a different namespace
// see bug531637 for an example where this would otherwise hang
// doxygen
if (baseClassTypeDef==0)
{
//printf(" => findTemplateInstanceRelation: %p\n",baseClassTypeDef);
findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,isArtificial);
}
}
else if (mode==DocumentedOnly || mode==Undocumented)
{
//printf(" => insert base class\n");
QCString usedName;
if (baseClassTypeDef)
{
usedName=biName;
//printf("***** usedName=%s templSpec=%s\n",usedName.data(),templSpec.data());
}
if (Config_getBool("SIP_SUPPORT")) bi->prot=Public;
cd->insertBaseClass(baseClass,usedName,bi->prot,bi->virt,templSpec);
// add this class as super class to the base class
baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
}
return TRUE;
}
else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
{
Debug::print(Debug::Classes,0,
" New undocumented base class `%s' baseClassName=%s\n",
biName.data(),baseClassName.data()
);
baseClass=0;
if (isATemplateArgument)
{
baseClass=Doxygen::hiddenClasses->find(baseClassName);
if (baseClass==0)
{
baseClass=new ClassDef(root->fileName,root->startLine,
baseClassName,ClassDef::Class);
Doxygen::hiddenClasses->append(baseClassName,baseClass);
if (isArtificial) baseClass->setArtificial(TRUE);
}
}
else
{
baseClass=Doxygen::classSDict->find(baseClassName);
//printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
// baseClassName.data(),baseClass,biName.data(),templSpec.data());
if (baseClass==0)
{
baseClass=new ClassDef(root->fileName,root->startLine,
baseClassName,ClassDef::Class);
Doxygen::classSDict->append(baseClassName,baseClass);
if (isArtificial) baseClass->setArtificial(TRUE);
}
}
// add base class to this class
cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
// add this class as super class to the base class
baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
// the undocumented base was found in this file
baseClass->insertUsedFile(root->fileName);
baseClass->setOuterScope(Doxygen::globalScope);
return TRUE;
}
else
{
Debug::print(Debug::Classes,0," Base class `%s' not found\n",biName.data());
}
}
else
{
if (mode!=TemplateInstances)
{
warn(root->fileName,root->startLine,
"Detected potential recursive class relation "
"between class %s and base class %s!\n",
root->name.data(),baseClassName.data()
);
}
// for mode==TemplateInstance this case is quite common and
// indicates a relation between a template class and a template
// instance with the same name.
}
if (scopeOffset==0)
{
scopeOffset=-1;
}
else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
{
scopeOffset=0;
}
//printf("new scopeOffset=`%d'",scopeOffset);
} while (scopeOffset>=0);
if (parentNode==0)
{
lastParent=TRUE;
}
else
{
parentNode=parentNode->parent();
}
} while (lastParent);
return FALSE;
}
//----------------------------------------------------------------------
// Computes the base and super classes for each class in the tree
static bool isClassSection(EntryNav *rootNav)
{
if ( !rootNav->name().isEmpty() )
{
if (rootNav->section() & Entry::COMPOUND_MASK)
// is it a compound (class, struct, union, interface ...)
{
return TRUE;
}
else if (rootNav->section() & Entry::COMPOUNDDOC_MASK)
// is it a documentation block with inheritance info.
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
bool extends = root->extends->count()>0;
rootNav->releaseEntry();
if (extends) return TRUE;
}
}
return FALSE;
}
/*! Builds a dictionary of all entry nodes in the tree starting with \a root
*/
static void findClassEntries(EntryNav *rootNav)
{
if (isClassSection(rootNav))
{
g_classEntries.insert(rootNav->name(),rootNav);
}
RECURSE_ENTRYTREE(findClassEntries,rootNav);
}
/*! Using the dictionary build by findClassEntries(), this
* function will look for additional template specialization that
* exists as inheritance relations only. These instances will be
* added to the template they are derived from.
*/
static void findInheritedTemplateInstances()
{
ClassSDict::Iterator cli(*Doxygen::classSDict);
for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE;
QDictIterator<EntryNav> edi(g_classEntries);
EntryNav *rootNav;
for (;(rootNav=edi.current());++edi)
{
ClassDef *cd;
// strip any annonymous scopes first
QCString bName=stripAnonymousNamespaceScope(rootNav->name());
bName=stripTemplateSpecifiersFromScope(bName);
Debug::print(Debug::Classes,0," Inheritance: Class %s : \n",bName.data());
if ((cd=getClass(bName)))
{
rootNav->loadEntry(g_storage);
//printf("Class %s %d\n",cd->name().data(),root->extends->count());
findBaseClassesForClass(rootNav,cd,cd,cd,TemplateInstances,FALSE);
rootNav->releaseEntry();
}
}
}
static void findUsedTemplateInstances()
{
ClassSDict::Iterator cli(*Doxygen::classSDict);
for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE;
QDictIterator<EntryNav> edi(g_classEntries);
EntryNav *rootNav;
for (;(rootNav=edi.current());++edi)
{
ClassDef *cd;
// strip any annonymous scopes first
QCString bName=stripAnonymousNamespaceScope(rootNav->name());
bName=stripTemplateSpecifiersFromScope(bName);
Debug::print(Debug::Classes,0," Usage: Class %s : \n",bName.data());
if ((cd=getClass(bName)))
{
rootNav->loadEntry(g_storage);
findUsedClassesForClass(rootNav,cd,cd,cd,TRUE);
rootNav->releaseEntry();
}
}
}
static void computeClassRelations()
{
ClassSDict::Iterator cli(*Doxygen::classSDict);
for (cli.toFirst();cli.current();++cli) cli.current()->visited=FALSE;
QDictIterator<EntryNav> edi(g_classEntries);
EntryNav *rootNav;
for (;(rootNav=edi.current());++edi)
{
ClassDef *cd;
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
// strip any annonymous scopes first
QCString bName=stripAnonymousNamespaceScope(rootNav->name());
bName=stripTemplateSpecifiersFromScope(bName);
Debug::print(Debug::Classes,0," Relations: Class %s : \n",bName.data());
if ((cd=getClass(bName)))
{
findBaseClassesForClass(rootNav,cd,cd,cd,DocumentedOnly,FALSE);
}
if ((cd==0 || (!cd->hasDocumentation() && !cd->isReference())) &&
bName.right(2)!="::")
{
if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
(guessSection(root->fileName)==Entry::HEADER_SEC ||
Config_getBool("EXTRACT_LOCAL_CLASSES")) && // not defined in source file
(root->protection!=Private || Config_getBool("EXTRACT_PRIVATE")) && // hidden by protection
!Config_getBool("HIDE_UNDOC_CLASSES") // undocumented class are visible
)
warn_undoc(
root->fileName,root->startLine,
"Warning: Compound %s is not documented.",
root->name.data()
);
}
rootNav->releaseEntry();
}
}
static void computeTemplateClassRelations()
{
QDictIterator<EntryNav> edi(g_classEntries);
EntryNav *rootNav;
for (;(rootNav=edi.current());++edi)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
QCString bName=stripAnonymousNamespaceScope(root->name);
bName=stripTemplateSpecifiersFromScope(bName);
ClassDef *cd=getClass(bName);
// strip any annonymous scopes first
QDict<ClassDef> *templInstances = 0;
if (cd && (templInstances=cd->getTemplateInstances()))
{
Debug::print(Debug::Classes,0," Template class %s : \n",cd->name().data());
QDictIterator<ClassDef> tdi(*templInstances);
ClassDef *tcd;
for (tdi.toFirst();(tcd=tdi.current());++tdi) // for each template instance
{
Debug::print(Debug::Classes,0," Template instance %s : \n",tcd->name().data());
QCString templSpec = tdi.currentKey();
ArgumentList *templArgs = new ArgumentList;
stringToArgumentList(templSpec,templArgs);
QList<BaseInfo> *baseList=root->extends;
BaseInfo *bi=baseList->first();
while (bi) // for each base class of the template
{
// check if the base class is a template argument
BaseInfo tbi(bi->name,bi->prot,bi->virt);
ArgumentList *tl = cd->templateArguments();
if (tl)
{
QDict<int> *baseClassNames = tcd->getTemplateBaseClassNames();
QDict<int> *templateNames = getTemplateArgumentsInName(tl,bi->name);
// for each template name that we inherit from we need to
// substitute the formal with the actual arguments
QDict<int> *actualTemplateNames = new QDict<int>(17);
actualTemplateNames->setAutoDelete(TRUE);
QDictIterator<int> qdi(*templateNames);
for (qdi.toFirst();qdi.current();++qdi)
{
int templIndex = *qdi.current();
Argument *actArg = 0;
if (templIndex<(int)templArgs->count())
{
actArg=templArgs->at(templIndex);
}
if (actArg!=0 &&
baseClassNames!=0 &&
baseClassNames->find(actArg->type)!=0 &&
actualTemplateNames->find(actArg->type)==0
)
{
actualTemplateNames->insert(actArg->type,new int(templIndex));
}
}
delete templateNames;
tbi.name = substituteTemplateArgumentsInString(bi->name,tl,templArgs);
// find a documented base class in the correct scope
if (!findClassRelation(rootNav,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
{
// no documented base class -> try to find an undocumented one
findClassRelation(rootNav,cd,tcd,&tbi,actualTemplateNames,Undocumented,FALSE);
}
delete actualTemplateNames;
}
bi=baseList->next();
}
delete templArgs;
} // class has no base classes
}
rootNav->releaseEntry();
}
}
//-----------------------------------------------------------------------
// compute the references (anchors in HTML) for each function in the file
static void computeMemberReferences()
{
ClassSDict::Iterator cli(*Doxygen::classSDict);
ClassDef *cd=0;
for (cli.toFirst();(cd=cli.current());++cli)
{
cd->computeAnchors();
}
FileName *fn=Doxygen::inputNameList->first();
while (fn)
{
FileDef *fd=fn->first();
while (fd)
{
fd->computeAnchors();
fd=fn->next();
}
fn=Doxygen::inputNameList->next();
}
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd=0;
for (nli.toFirst();(nd=nli.current());++nli)
{
nd->computeAnchors();
}
GroupSDict::Iterator gli(*Doxygen::groupSDict);
GroupDef *gd;
for (gli.toFirst();(gd=gli.current());++gli)
{
gd->computeAnchors();
}
}
//----------------------------------------------------------------------
static void addListReferences()
{
MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
MemberName *mn=0;
for (mnli.toFirst();(mn=mnli.current());++mnli)
{
MemberNameIterator mni(*mn);
MemberDef *md=0;
for (mni.toFirst();(md=mni.current());++mni)
{
md->visited=FALSE;
}
}
MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict);
for (fnli.toFirst();(mn=fnli.current());++fnli)
{
MemberNameIterator mni(*mn);
MemberDef *md=0;
for (mni.toFirst();(md=mni.current());++mni)
{
md->visited=FALSE;
}
}
ClassSDict::Iterator cli(*Doxygen::classSDict);
ClassDef *cd=0;
for (cli.toFirst();(cd=cli.current());++cli)
{
cd->addListReferences();
}
FileName *fn=Doxygen::inputNameList->first();
while (fn)
{
FileDef *fd=fn->first();
while (fd)
{
fd->addListReferences();
fd=fn->next();
}
fn=Doxygen::inputNameList->next();
}
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd=0;
for (nli.toFirst();(nd=nli.current());++nli)
{
nd->addListReferences();
}
GroupSDict::Iterator gli(*Doxygen::groupSDict);
GroupDef *gd;
for (gli.toFirst();(gd=gli.current());++gli)
{
gd->addListReferences();
}
PageSDict::Iterator pdi(*Doxygen::pageSDict);
PageDef *pd=0;
for (pdi.toFirst();(pd=pdi.current());++pdi)
{
QCString name = pd->name();
if (pd->getGroupDef())
{
name = pd->getGroupDef()->getOutputFileBase();
}
{
LockingPtr< QList<ListItemInfo> > xrefItems = pd->xrefListItems();
addRefItem(xrefItems.pointer(),
name,
theTranslator->trPage(TRUE,TRUE),
name,pd->title(),0);
}
}
DirSDict::Iterator ddi(*Doxygen::directories);
DirDef *dd = 0;
for (ddi.toFirst();(dd=ddi.current());++ddi)
{
QCString name = dd->getOutputFileBase();
//if (dd->getGroupDef())
//{
// name = dd->getGroupDef()->getOutputFileBase();
//}
LockingPtr< QList<ListItemInfo> > xrefItems = dd->xrefListItems();
addRefItem(xrefItems.pointer(),
name,
theTranslator->trDir(TRUE,TRUE),
name,dd->displayName(),0);
}
}
//----------------------------------------------------------------------
static void generateXRefPages()
{
QDictIterator<RefList> di(*Doxygen::xrefLists);
RefList *rl;
for (di.toFirst();(rl=di.current());++di)
{
rl->generatePage();
}
}
//----------------------------------------------------------------------
// Copy the documentation in entry `root' to member definition `md' and
// set the function declaration of the member to `funcDecl'. If the boolean
// over_load is set the standard overload text is added.
static void addMemberDocs(EntryNav *rootNav,
MemberDef *md, const char *funcDecl,
ArgumentList *al,
bool over_load,
NamespaceSDict *
)
{
Entry *root = rootNav->entry();
//printf("addMemberDocs: `%s'::`%s' `%s' funcDecl=`%s' mSpec=%d\n",
// root->parent->name.data(),md->name().data(),md->argsString(),funcDecl,root->spec);
QCString fDecl=funcDecl;
// strip extern specifier
fDecl.stripPrefix("extern ");
md->setDefinition(fDecl);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
ClassDef *cd=md->getClassDef();
NamespaceDef *nd=md->getNamespaceDef();
QCString fullName;
if (cd)
fullName = cd->name();
else if (nd)
fullName = nd->name();
if (!fullName.isEmpty()) fullName+="::";
fullName+=md->name();
FileDef *rfd=rootNav->fileDef();
// TODO determine scope based on root not md
Definition *rscope = md->getOuterScope();
LockingPtr<ArgumentList> mdAl = md->argumentList();
if (al)
{
//printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
mergeArguments(mdAl.pointer(),al,!root->doc.isEmpty());
}
else
{
if (
matchArguments2( md->getOuterScope(), md->getFileDef(), mdAl.pointer(),
rscope,rfd,root->argList,
TRUE
)
)
{
//printf("merging arguments (2)\n");
mergeArguments(mdAl.pointer(),root->argList,!root->doc.isEmpty());
}
}
if (over_load) // the \overload keyword was used
{
QCString doc=getOverloadDocs();
if (!root->doc.isEmpty())
{
doc+="<p>";
doc+=root->doc;
}
md->setDocumentation(doc,root->docFile,root->docLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setDocsForDefinition(!root->proto);
}
else
{
//printf("overwrite!\n");
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setDocsForDefinition(!root->proto);
//printf("overwrite!\n");
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
if (
(md->inbodyDocumentation().isEmpty() ||
!rootNav->parent()->name().isEmpty()
) && !root->inbodyDocs.isEmpty()
)
{
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
}
}
//printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
// md->initializer().data(),md->initializer().isEmpty(),
// root->initializer.data(),root->initializer.isEmpty()
// );
if (md->initializer().isEmpty() && !root->initializer.isEmpty())
{
//printf("setInitializer\n");
md->setInitializer(root->initializer);
}
md->setMaxInitLines(root->initLines);
if (rfd)
{
if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
)
{
//printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setBodyDef(rfd);
}
md->setRefItems(root->sli);
}
md->enableCallGraph(md->hasCallGraph() || root->callGraph);
md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
md->mergeMemberSpecifiers(root->spec);
md->addSectionsToDefinition(root->anchors);
addMemberToGroups(root,md);
if (cd) cd->insertUsedFile(root->fileName);
//printf("root->mGrpId=%d\n",root->mGrpId);
if (root->mGrpId!=-1)
{
if (md->getMemberGroupId()!=-1)
{
if (md->getMemberGroupId()!=root->mGrpId)
{
warn(
root->fileName,root->startLine,
"Warning: member %s belongs to two different groups. The second "
"one found here will be ignored.",
md->name().data()
);
}
}
else // set group id
{
//printf("setMemberGroupId=%d md=%s\n",root->mGrpId,md->name().data());
md->setMemberGroupId(root->mGrpId);
}
}
}
//----------------------------------------------------------------------
// find a class definition given the scope name and (optionally) a
// template list specifier
static ClassDef *findClassDefinition(FileDef *fd,NamespaceDef *nd,
const char *scopeName)
{
ClassDef *tcd = getResolvedClass(nd,fd,scopeName,0,0,TRUE,TRUE);
return tcd;
}
//----------------------------------------------------------------------
// Adds the documentation contained in `root' to a global function
// with name `name' and argument list `args' (for overloading) and
// function declaration `decl' to the corresponding member definition.
static bool findGlobalMember(EntryNav *rootNav,
const QCString &namespaceName,
const char *name,
const char *tempArg,
const char *,
const char *decl)
{
Entry *root = rootNav->entry();
Debug::print(Debug::FindMembers,0,
"2. findGlobalMember(namespace=%s,name=%s,tempArg=%s,decl=%s)\n",
namespaceName.data(),name,tempArg,decl);
QCString n=name;
if (n.isEmpty()) return FALSE;
if (n.find("::")!=-1) return FALSE; // skip undefined class members
MemberName *mn=Doxygen::functionNameSDict->find(n+tempArg); // look in function dictionary
if (mn==0)
{
mn=Doxygen::functionNameSDict->find(n); // try without template arguments
}
if (mn) // function name defined
{
Debug::print(Debug::FindMembers,0,"3. Found function scope\n");
//int count=0;
MemberNameIterator mni(*mn);
MemberDef *md;
bool found=FALSE;
for (mni.toFirst();(md=mni.current()) && !found;++mni)
{
NamespaceDef *nd=md->getNamespaceDef();
//printf("Namespace namespaceName=%s nd=%s\n",
// namespaceName.data(),nd ? nd->name().data() : "<none>");
FileDef *fd=rootNav->fileDef();
//printf("File %s\n",fd ? fd->name().data() : "<none>");
NamespaceSDict *nl = fd ? fd->getUsedNamespaces() : 0;
//SDict<Definition> *cl = fd ? fd->getUsedClasses() : 0;
//printf("NamespaceList %p\n",nl);
// search in the list of namespaces that are imported via a
// using declaration
bool viaUsingDirective = nl && nd && nl->find(nd->qualifiedName())!=0;
if ((namespaceName.isEmpty() && nd==0) || // not in a namespace
(nd && nd->name()==namespaceName) || // or in the same namespace
viaUsingDirective // member in `using' namespace
)
{
Debug::print(Debug::FindMembers,0,"4. Try to add member `%s' to scope `%s'\n",
md->name().data(),namespaceName.data());
QCString nsName = nd ? nd->name().data() : "";
NamespaceDef *rnd = 0;
if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceSDict->find(namespaceName);
LockingPtr<ArgumentList> mdAl = md->argumentList();
bool matching=
(mdAl==0 && root->argList->count()==0) ||
md->isVariable() || md->isTypedef() || /* in case of function pointers */
matchArguments2(md->getOuterScope(),md->getFileDef(),mdAl.pointer(),
rnd ? rnd : Doxygen::globalScope,fd,root->argList,
FALSE);
// for template members we need to check if the number of
// template arguments is the same, otherwise we are dealing with
// different functions.
if (matching && root->tArgLists)
{
LockingPtr<ArgumentList> mdTempl = md->templateArguments();
if (mdTempl!=0)
{
if (root->tArgLists->getLast()->count()!=mdTempl->count())
{
matching=FALSE;
}
}
}
//printf("%s<->%s\n",
// argListToString(md->argumentList()).data(),
// argListToString(root->argList).data());
// for static members we also check if the comment block was found in
// the same file. This is needed because static members with the same
// name can be in different files. Thus it would be wrong to just
// put the comment block at the first syntactically matching member.
if (matching && md->isStatic() &&
md->getDefFileName()!=root->fileName &&
mn->count()>1)
{
matching = FALSE;
}
if (matching) // add docs to the member
{
Debug::print(Debug::FindMembers,0,"5. Match found\n");
addMemberDocs(rootNav,md,decl,root->argList,FALSE);
found=TRUE;
}
}
}
if (!found && root->relatesType != Duplicate) // no match
{
QCString fullFuncDecl=decl;
if (root->argList) fullFuncDecl+=argListToString(root->argList,TRUE);
warn(root->fileName,root->startLine,
"Warning: no matching file member found for \n%s",fullFuncDecl.data());
if (mn->count()>0)
{
warn_cont("Possible candidates:\n");
for (mni.toFirst();(md=mni.current());++mni)
{
warn_cont(" %s\n",md->declaration());
}
}
}
}
else // got docs for an undefined member!
{
if (root->type!="friend class" &&
root->type!="friend struct" &&
root->type!="friend union" &&
(!Config_getBool("TYPEDEF_HIDES_STRUCT") ||
root->type.find("typedef ")==-1)
)
{
warn(root->fileName,root->startLine,
"Warning: documented function `%s' was not declared or defined.",decl
);
}
}
return TRUE;
}
static bool isSpecialization(
const QList<ArgumentList> &srcTempArgLists,
const QList<ArgumentList> &dstTempArgLists
)
{
QListIterator<ArgumentList> srclali(srcTempArgLists);
QListIterator<ArgumentList> dstlali(dstTempArgLists);
for (;srclali.current();++srclali,++dstlali)
{
ArgumentList *sal = srclali.current();
ArgumentList *dal = dstlali.current();
if (!(sal && dal && sal->count()==dal->count())) return TRUE;
}
return FALSE;
}
static QCString substituteTemplatesInString(
const QList<ArgumentList> &srcTempArgLists,
const QList<ArgumentList> &dstTempArgLists,
ArgumentList *funcTempArgList, // can be used to match template specializations
const QCString &src
)
{
QCString dst;
QRegExp re( "[A-Za-z_][A-Za-z_0-9]*");
//printf("type=%s\n",sa->type.data());
int i,p=0,l;
while ((i=re.match(src,p,&l))!=-1) // for each word in srcType
{
bool found=FALSE;
dst+=src.mid(p,i-p);
QCString name=src.mid(i,l);
QListIterator<ArgumentList> srclali(srcTempArgLists);
QListIterator<ArgumentList> dstlali(dstTempArgLists);
for (;srclali.current() && !found;++srclali,++dstlali)
{
ArgumentListIterator tsali(*srclali.current());
ArgumentListIterator tdali(*dstlali.current());
Argument *tsa =0,*tda=0, *fa=0;
if (funcTempArgList)
{
fa=funcTempArgList->first();
}
for (tsali.toFirst();(tsa=tsali.current()) && !found;++tsali)
{
tda = tdali.current();
//if (tda) printf("tsa=%s|%s tda=%s|%s\n",
// tsa->type.data(),tsa->name.data(),
// tda->type.data(),tda->name.data());
if (name==tsa->name)
{
if (tda && tda->name.isEmpty())
{
int vc=0;
if (tda->type.left(6)=="class ") vc=6;
else if (tda->type.left(9)=="typename ") vc=9;
if (vc>0) // convert type=="class T" to type=="class" name=="T"
{
tda->name = tda->type.mid(vc);
tda->type = tda->type.left(vc-1);
}
}
if (tda && !tda->name.isEmpty())
{
name=tda->name; // substitute
found=TRUE;
}
else if (fa)
{
name=fa->type;
found=TRUE;
}
}
if (tda)
++tdali;
else if (fa)
fa=funcTempArgList->next();
}
//printf(" srcList='%s' dstList='%s faList='%s'\n",
// argListToString(srclali.current()).data(),
// argListToString(dstlali.current()).data(),
// funcTempArgList ? argListToString(funcTempArgList).data() : "<none>");
}
dst+=name;
p=i+l;
}
dst+=src.right(src.length()-p);
//printf(" substituteTemplatesInString(%s)=%s\n",
// src.data(),dst.data());
return dst;
}
static void substituteTemplatesInArgList(
const QList<ArgumentList> &srcTempArgLists,
const QList<ArgumentList> &dstTempArgLists,
ArgumentList *src,
ArgumentList *dst,
ArgumentList *funcTempArgs = 0
)
{
ArgumentListIterator sali(*src);
Argument *sa=0;
Argument *da=dst->first();
for (sali.toFirst();(sa=sali.current());++sali) // for each member argument
{
QCString dstType = substituteTemplatesInString(
srcTempArgLists,dstTempArgLists,funcTempArgs,
sa->type);
QCString dstArray = substituteTemplatesInString(
srcTempArgLists,dstTempArgLists,funcTempArgs,
sa->array);
if (da==0)
{
da=new Argument(*sa);
dst->append(da);
da->type=dstType;
da->array=dstArray;
da=0;
}
else
{
da->type=dstType;
da->type=dstArray;
da=dst->next();
}
}
dst->constSpecifier = src->constSpecifier;
dst->volatileSpecifier = src->volatileSpecifier;
dst->pureSpecifier = src->pureSpecifier;
//printf("substituteTemplatesInArgList: replacing %s with %s\n",
// argListToString(src).data(),argListToString(dst).data()
// );
}
/*! This function tries to find a member (in a documented class/file/namespace)
* that corresponds to the function/variable declaration given in \a funcDecl.
*
* The boolean \a overloaded is used to specify whether or not a standard
* overload documentation line should be generated.
*
* The boolean \a isFunc is a hint that indicates that this is a function
* instead of a variable or typedef.
*/
static void findMember(EntryNav *rootNav,
QCString funcDecl,
bool overloaded,
bool isFunc
)
{
Entry *root = rootNav->entry();
Debug::print(Debug::FindMembers,0,
"findMember(root=%p,funcDecl=`%s',related=`%s',overload=%d,"
"isFunc=%d mGrpId=%d tArgList=%p (#=%d) "
"spec=%d isObjC=%d\n",
root,funcDecl.data(),root->relates.data(),overloaded,isFunc,root->mGrpId,
root->tArgLists,root->tArgLists ? root->tArgLists->count() : 0,
root->spec,root->objc
);
QCString scopeName;
QCString className;
QCString namespaceName;
QCString funcType;
QCString funcName;
QCString funcArgs;
QCString funcTempList;
QCString exceptions;
QCString funcSpec;
bool isRelated=FALSE;
bool isMemberOf=FALSE;
bool isFriend=FALSE;
bool done;
do
{
done=TRUE;
if (funcDecl.stripPrefix("friend ")) // treat friends as related members
{
isFriend=TRUE;
done=FALSE;
}
if (funcDecl.stripPrefix("inline "))
{
root->spec|=Entry::Inline;
done=FALSE;
}
if (funcDecl.stripPrefix("explicit "))
{
root->spec|=Entry::Explicit;
done=FALSE;
}
if (funcDecl.stripPrefix("mutable "))
{
root->spec|=Entry::Mutable;
done=FALSE;
}
if (funcDecl.stripPrefix("virtual "))
{
done=FALSE;
}
} while (!done);
// delete any ; from the function declaration
int sep;
while ((sep=funcDecl.find(';'))!=-1)
{
funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
}
// make sure the first character is a space to simplify searching.
if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
// remove some superfluous spaces
funcDecl= substitute(
substitute(
substitute(funcDecl,"~ ","~"),
":: ","::"
),
" ::","::"
).stripWhiteSpace();
//printf("funcDecl=`%s'\n",funcDecl.data());
if (isFriend && funcDecl.left(6)=="class ")
{
//printf("friend class\n");
funcDecl=funcDecl.right(funcDecl.length()-6);
funcName = funcDecl.copy();
}
else if (isFriend && funcDecl.left(7)=="struct ")
{
funcDecl=funcDecl.right(funcDecl.length()-7);
funcName = funcDecl.copy();
}
else
{
// extract information from the declarations
parseFuncDecl(funcDecl,root->objc,scopeName,funcType,funcName,
funcArgs,funcTempList,exceptions
);
}
//printf("scopeName=`%s' funcType=`%s' funcName=`%s' funcArgs=`%s'\n",
// scopeName.data(),funcType.data(),funcName.data(),funcArgs.data());
// the class name can also be a namespace name, we decide this later.
// if a related class name is specified and the class name could
// not be derived from the function declaration, then use the
// related field.
//printf("scopeName=`%s' className=`%s' namespaceName=`%s'\n",
// scopeName.data(),className.data(),namespaceName.data());
if (!root->relates.isEmpty())
{ // related member, prefix user specified scope
isRelated=TRUE;
isMemberOf=(root->relatesType == MemberOf);
if (getClass(root->relates)==0 && !scopeName.isEmpty())
{
scopeName= mergeScopes(scopeName,root->relates);
}
else
{
scopeName = root->relates;
}
}
if (root->relates.isEmpty() && rootNav->parent() &&
((rootNav->parent()->section()&Entry::SCOPE_MASK) ||
(rootNav->parent()->section()==Entry::OBJCIMPL_SEC)
) &&
!rootNav->parent()->name().isEmpty()) // see if we can combine scopeName
// with the scope in which it was found
{
QCString joinedName = rootNav->parent()->name()+"::"+scopeName;
if (!scopeName.isEmpty() &&
(getClass(joinedName) || Doxygen::namespaceSDict->find(joinedName)))
{
scopeName = joinedName;
}
else
{
scopeName = mergeScopes(rootNav->parent()->name(),scopeName);
}
}
else // see if we can prefix a namespace or class that is used from the file
{
FileDef *fd=rootNav->fileDef();
if (fd)
{
NamespaceSDict *fnl = fd->getUsedNamespaces();
if (fnl)
{
QCString joinedName;
NamespaceDef *fnd;
NamespaceSDict::Iterator nsdi(*fnl);
for (nsdi.toFirst();(fnd=nsdi.current());++nsdi)
{
joinedName = fnd->name()+"::"+scopeName;
if (Doxygen::namespaceSDict->find(joinedName))
{
scopeName=joinedName;
break;
}
}
}
}
}
scopeName=stripTemplateSpecifiersFromScope(
removeRedundantWhiteSpace(scopeName),FALSE,&funcSpec);
// funcSpec contains the last template specifiers of the given scope.
// If this method does not have any template arguments or they are
// empty while funcSpec is not empty we assume this is a
// specialization of a method. If not, we clear the funcSpec and treat
// this as a normal method of a template class.
if (!(root->tArgLists &&
root->tArgLists->count()>0 &&
root->tArgLists->first()->count()==0
)
)
{
funcSpec.resize(0);
}
// split scope into a namespace and a class part
extractNamespaceName(scopeName,className,namespaceName,TRUE);
//printf("scopeName=`%s' className=`%s' namespaceName=`%s'\n",
// scopeName.data(),className.data(),namespaceName.data());
//namespaceName=removeAnonymousScopes(namespaceName);
if (namespaceName.find('@')!=-1) return; // skip stuff in anonymous namespace...
//printf("namespaceName=`%s' className=`%s'\n",namespaceName.data(),className.data());
// merge class and namespace scopes again
scopeName.resize(0);
if (!namespaceName.isEmpty())
{
if (className.isEmpty())
{
scopeName=namespaceName;
}
else if (!root->relates.isEmpty() || // relates command with explicit scope
!getClass(className)) // class name only exists in a namespace
{
scopeName=namespaceName+"::"+className;
}
else
{
scopeName=className;
}
}
else if (!className.isEmpty())
{
scopeName=className;
}
//printf("new scope=`%s'\n",scopeName.data());
QCString tempScopeName=scopeName;
ClassDef *cd=getClass(scopeName);
if (cd)
{
if (root->tArgLists) root->tArgLists->first();
if (funcSpec.isEmpty())
{
tempScopeName=cd->qualifiedNameWithTemplateParameters(root->tArgLists);
}
else
{
tempScopeName=scopeName+funcSpec;
}
}
//printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
// scopeName.data(),cd,root->tArgLists,tempScopeName.data());
//printf("scopeName=`%s' className=`%s'\n",scopeName.data(),className.data());
// rebuild the function declaration (needed to get the scope right).
if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool("HIDE_SCOPE_NAMES"))
{
if (!funcType.isEmpty())
{
if (isFunc) // a function -> we use argList for the arguments
{
funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
}
else
{
funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
}
}
else
{
if (isFunc) // a function => we use argList for the arguments
{
funcDecl=tempScopeName+"::"+funcName+funcTempList;
}
else // variable => add `argument' list
{
funcDecl=tempScopeName+"::"+funcName+funcArgs;
}
}
}
else // build declaration without scope
{
if (!funcType.isEmpty()) // but with a type
{
if (isFunc) // function => omit argument list
{
funcDecl=funcType+" "+funcName+funcTempList;
}
else // variable => add `argument' list
{
funcDecl=funcType+" "+funcName+funcArgs;
}
}
else // no type
{
if (isFunc)
{
funcDecl=funcName+funcTempList;
}
else
{
funcDecl=funcName+funcArgs;
}
}
}
if (funcType=="template class" && !funcTempList.isEmpty())
return; // ignore explicit template instantiations
Debug::print(Debug::FindMembers,0,
"findMember() Parse results:\n"
" namespaceName=`%s'\n"
" className=`%s`\n"
" funcType=`%s'\n"
" funcSpec=`%s'\n"
" funcName=`%s'\n"
" funcArgs=`%s'\n"
" funcTempList=`%s'\n"
" funcDecl=`%s'\n"
" related=`%s'\n"
" exceptions=`%s'\n"
" isRelated=%d\n"
" isMemberOf=%d\n"
" isFriend=%d\n"
" isFunc=%d\n\n",
namespaceName.data(),className.data(),
funcType.data(),funcSpec.data(),funcName.data(),funcArgs.data(),funcTempList.data(),
funcDecl.data(),root->relates.data(),exceptions.data(),isRelated,isMemberOf,isFriend,
isFunc
);
MemberName *mn=0;
if (!funcName.isEmpty()) // function name is valid
{
Debug::print(Debug::FindMembers,0,
"1. funcName=`%s'\n",funcName.data());
if (funcName.left(9)=="operator ") // strip class scope from cast operator
{
funcName = substitute(funcName,className+"::","");
}
if (!funcTempList.isEmpty()) // try with member specialization
{
mn=Doxygen::memberNameSDict->find(funcName+funcTempList);
}
if (mn==0) // try without specialization
{
mn=Doxygen::memberNameSDict->find(funcName);
}
if (!isRelated && mn) // function name already found
{
Debug::print(Debug::FindMembers,0,
"2. member name exists (%d members with this name)\n",mn->count());
if (!className.isEmpty()) // class name is valid
{
if (funcSpec.isEmpty()) // not a member specialization
{
int count=0;
int noMatchCount=0;
MemberNameIterator mni(*mn);
MemberDef *md;
bool memFound=FALSE;
for (mni.toFirst();!memFound && (md=mni.current());++mni)
{
ClassDef *cd=md->getClassDef();
Debug::print(Debug::FindMembers,0,
"3. member definition found, "
"scope needed=`%s' scope=`%s' args=`%s' fileName=%s\n",
scopeName.data(),cd ? cd->name().data() : "<none>",
md->argsString(),
root->fileName.data());
//printf("Member %s (member scopeName=%s) (this scopeName=%s) classTempList=%s\n",md->name().data(),cd->name().data(),scopeName.data(),classTempList.data());
FileDef *fd=rootNav->fileDef();
NamespaceDef *nd=0;
if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
//printf("scopeName %s->%s\n",scopeName.data(),
// stripTemplateSpecifiersFromScope(scopeName,FALSE).data());
ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
if (tcd==0 && stripAnonymousNamespaceScope(cd->name())==scopeName)
{
// don't be fooled by anonymous scopes
tcd=cd;
}
//printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n",
// scopeName.data(),nd?nd->name().data():"<none>",tcd,tcd?tcd->name().data():"",cd);
if (cd && tcd==cd) // member's classes match
{
Debug::print(Debug::FindMembers,0,
"4. class definition %s found\n",cd->name().data());
// get the template parameter lists found at the member declaration
QList<ArgumentList> declTemplArgs;
cd->getTemplateParameterLists(declTemplArgs);
LockingPtr<ArgumentList> templAl = md->templateArguments();
if (templAl!=0)
{
declTemplArgs.append(templAl.pointer());
}
// get the template parameter lists found at the member definition
QList<ArgumentList> *defTemplArgs = root->tArgLists;
//printf("defTemplArgs=%p\n",defTemplArgs);
// do we replace the decl argument lists with the def argument lists?
bool substDone=FALSE;
ArgumentList *argList=0;
/* substitute the occurrences of class template names in the
* argument list before matching
*/
LockingPtr<ArgumentList> mdAl = md->argumentList();
if (declTemplArgs.count()>0 && defTemplArgs &&
declTemplArgs.count()==defTemplArgs->count() &&
mdAl.pointer()
)
{
/* the function definition has template arguments
* and the class definition also has template arguments, so
* we must substitute the template names of the class by that
* of the function definition before matching.
*/
argList = new ArgumentList;
substituteTemplatesInArgList(declTemplArgs,*defTemplArgs,
mdAl.pointer(),argList);
substDone=TRUE;
}
else /* no template arguments, compare argument lists directly */
{
argList = mdAl.pointer();
}
Debug::print(Debug::FindMembers,0,
"5. matching `%s'<=>`%s' className=%s namespaceName=%s\n",
argListToString(argList,TRUE).data(),argListToString(root->argList,TRUE).data(),
className.data(),namespaceName.data()
);
bool matching=
md->isVariable() || md->isTypedef() || // needed for function pointers
(mdAl.pointer()==0 && root->argList->count()==0) ||
matchArguments2(
md->getClassDef(),md->getFileDef(),argList,
cd,fd,root->argList,
TRUE);
Debug::print(Debug::FindMembers,0,
"6. match results of matchArguments2 = %d\n",matching);
if (substDone) // found a new argument list
{
if (matching) // replace member's argument list
{
md->setDefinitionTemplateParameterLists(root->tArgLists);
md->setArgumentList(argList); // new owner of the list => no delete
}
else // no match
{
if (!funcTempList.isEmpty() &&
isSpecialization(declTemplArgs,*defTemplArgs))
{
// check if we are dealing with a partial template
// specialization. In this case we add it to the class
// even though the member arguments do not match.
// TODO: copy other aspects?
root->protection=md->protection(); // copy protection level
addMethodToClass(rootNav,cd,md->name(),isFriend);
return;
}
delete argList;
}
}
if (matching)
{
addMemberDocs(rootNav,md,funcDecl,0,overloaded,0/* TODO */);
count++;
memFound=TRUE;
}
}
else if (cd && cd!=tcd) // we did find a class with the same name as cd
// but in a different namespace
{
noMatchCount++;
}
}
if (count==0 && rootNav->parent() &&
rootNav->parent()->section()==Entry::OBJCIMPL_SEC)
{
goto localObjCMethod;
}
if (count==0 && !(isFriend && funcType=="class"))
{
int candidates=0;
if (mn->count()>0)
{
//printf("Assume template class\n");
for (mni.toFirst();(md=mni.current());++mni)
{
ClassDef *cd=md->getClassDef();
//printf("cd->name()==%s className=%s\n",cd->name().data(),className.data());
if (cd!=0 && rightScopeMatch(cd->name(),className))
{
LockingPtr<ArgumentList> templAl = md->templateArguments();
if (root->tArgLists && templAl!=0 &&
root->tArgLists->getLast()->count()<=templAl->count())
{
addMethodToClass(rootNav,cd,md->name(),isFriend);
return;
}
candidates++;
}
}
}
warn(root->fileName,root->startLine,
"Warning: no %smatching class member found for",
noMatchCount>1 ? "uniquely " : ""
);
if (root->tArgLists)
{
QListIterator<ArgumentList> alli(*root->tArgLists);
ArgumentList *al;
for (;(al=alli.current());++alli)
{
warn_cont(" template %s\n",tempArgListToString(al).data());
}
}
QCString fullFuncDecl=funcDecl.copy();
if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
warn_cont(" %s\n",fullFuncDecl.data());
if (candidates>0)
{
warn_cont("Possible candidates:\n");
for (mni.toFirst();(md=mni.current());++mni)
{
ClassDef *cd=md->getClassDef();
if (cd!=0 && rightScopeMatch(cd->name(),className))
{
LockingPtr<ArgumentList> templAl = md->templateArguments();
if (templAl!=0)
{
warn_cont(" template %s\n",tempArgListToString(templAl.pointer()).data());
}
warn_cont(" ");
if (md->typeString())
{
warn_cont("%s ",md->typeString());
}
QCString qScope = cd->qualifiedNameWithTemplateParameters();
if (!qScope.isEmpty()) warn_cont("%s::%s",qScope.data(),md->name().data());
if (md->argsString()) warn_cont("%s",md->argsString());
if (noMatchCount>1) warn_cont(" at line %d of file %s",md->getDefLine(),md->getDefFileName().data());
warn_cont("\n");
}
}
}
}
}
else if (cd) // member specialization
{
MemberDef::MemberType mtype=MemberDef::Function;
ArgumentList *tArgList = new ArgumentList;
// getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
funcType,funcName,funcArgs,exceptions,
root->protection,root->virt,root->stat,Member,
mtype,tArgList,root->argList);
//printf("new specialized member %s args=`%s'\n",md->name().data(),funcArgs.data());
md->setTagInfo(rootNav->tagInfo());
md->setMemberClass(cd);
md->setTemplateSpecialization(TRUE);
md->setTypeConstraints(root->typeConstr);
md->setDefinition(funcDecl);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setDocsForDefinition(!root->proto);
md->setPrototype(root->proto);
md->addSectionsToDefinition(root->anchors);
md->setBodySegment(root->bodyLine,root->endBodyLine);
FileDef *fd=rootNav->fileDef();
md->setBodyDef(fd);
md->setMemberSpecifiers(root->spec);
md->setMemberGroupId(root->mGrpId);
mn->append(md);
cd->insertMember(md);
md->setRefItems(root->sli);
delete tArgList;
}
else
{
//printf("*** Specialized member %s of unknown scope %s%s found!\n",
// scopeName.data(),funcName.data(),funcArgs.data());
}
}
else if (overloaded) // check if the function belongs to only one class
{
// for unique overloaded member we allow the class to be
// omitted, this is to be Qt compatable. Using this should
// however be avoided, because it is error prone
MemberNameIterator mni(*mn);
MemberDef *md=mni.toFirst();
ASSERT(md);
ClassDef *cd=md->getClassDef();
ASSERT(cd);
QCString className=cd->name().copy();
++mni;
bool unique=TRUE;
for (;(md=mni.current());++mni)
{
ClassDef *cd=md->getClassDef();
if (className!=cd->name()) unique=FALSE;
}
if (unique)
{
MemberDef::MemberType mtype;
if (root->mtype==Signal) mtype=MemberDef::Signal;
else if (root->mtype==Slot) mtype=MemberDef::Slot;
else if (root->mtype==DCOP) mtype=MemberDef::DCOP;
else mtype=MemberDef::Function;
// new overloaded member function
ArgumentList *tArgList =
getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
//printf("new related member %s args=`%s'\n",md->name().data(),funcArgs.data());
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
funcType,funcName,funcArgs,exceptions,
root->protection,root->virt,root->stat,Related,
mtype,tArgList,root->argList);
md->setTagInfo(rootNav->tagInfo());
md->setTypeConstraints(root->typeConstr);
md->setMemberClass(cd);
md->setDefinition(funcDecl);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
QCString doc=getOverloadDocs();
doc+="<p>";
doc+=root->doc;
md->setDocumentation(doc,root->docFile,root->docLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setDocsForDefinition(!root->proto);
md->setPrototype(root->proto);
md->addSectionsToDefinition(root->anchors);
md->setBodySegment(root->bodyLine,root->endBodyLine);
FileDef *fd=rootNav->fileDef();
md->setBodyDef(fd);
md->setMemberSpecifiers(root->spec);
md->setMemberGroupId(root->mGrpId);
mn->append(md);
cd->insertMember(md);
cd->insertUsedFile(root->fileName);
md->setRefItems(root->sli);
}
}
else // unrelated function with the same name as a member
{
if (!findGlobalMember(rootNav,namespaceName,funcName,funcTempList,funcArgs,funcDecl))
{
QCString fullFuncDecl=funcDecl.copy();
if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
warn(root->fileName,root->startLine,
"Warning: Cannot determine class for function\n%s",
fullFuncDecl.data()
);
}
}
}
else if (isRelated && !root->relates.isEmpty())
{
Debug::print(Debug::FindMembers,0,"2. related function\n"
" scopeName=%s className=%s\n",scopeName.data(),className.data());
if (className.isEmpty()) className=root->relates;
ClassDef *cd;
//printf("scopeName=`%s' className=`%s'\n",scopeName.data(),className.data());
if ((cd=getClass(scopeName)))
{
bool newMember=TRUE; // assume we have a new member
bool newMemberName=FALSE;
bool isDefine=FALSE;
{
MemberName *mn = Doxygen::functionNameSDict->find(funcName);
if (mn)
{
MemberDef *md = mn->first();
while (md && !isDefine)
{
isDefine = isDefine || md->isDefine();
md = mn->next();
}
}
}
FileDef *fd=rootNav->fileDef();
if ((mn=Doxygen::memberNameSDict->find(funcName))==0)
{
mn=new MemberName(funcName);
newMemberName=TRUE; // we create a new member name
}
else
{
MemberDef *rmd=mn->first();
while (rmd && newMember) // see if we got another member with matching arguments
{
LockingPtr<ArgumentList> rmdAl = rmd->argumentList();
newMember=newMember &&
!matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmdAl.pointer(),
cd,fd,root->argList,
TRUE);
if (newMember) rmd=mn->next();
}
if (!newMember && rmd) // member already exists as rmd -> add docs
{
//printf("addMemberDocs for related member %s\n",root->name.data());
//rmd->setMemberDefTemplateArguments(root->mtArgList);
addMemberDocs(rootNav,rmd,funcDecl,0,overloaded);
}
}
if (newMember) // need to create a new member
{
MemberDef::MemberType mtype;
if (isDefine)
mtype=MemberDef::Define;
else if (root->mtype==Signal)
mtype=MemberDef::Signal;
else if (root->mtype==Slot)
mtype=MemberDef::Slot;
else if (root->mtype==DCOP)
mtype=MemberDef::DCOP;
else
mtype=MemberDef::Function;
//printf("New related name `%s' `%d'\n",funcName.data(),
// root->argList ? (int)root->argList->count() : -1);
// new related (member) function
#if 0 // removed as it doesn't handle related template functions correctly
ArgumentList *tArgList =
getTemplateArgumentsFromName(scopeName+"::"+funcName,root->tArgLists);
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
funcType,funcName,funcArgs,exceptions,
root->protection,root->virt,root->stat,TRUE,
mtype,tArgList,funcArgs.isEmpty() ? 0 : root->argList);
#endif
// first note that we pass:
// (root->tArgLists ? root->tArgLists->last() : 0)
// for the template arguments fo the new "member."
// this accurately reflects the template arguments of
// the related function, which don't have to do with
// those of the related class.
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
funcType,funcName,funcArgs,exceptions,
root->protection,root->virt,
root->stat && !isMemberOf,
isMemberOf ? Foreign : isRelated ? Related : Member,
mtype,
(root->tArgLists ? root->tArgLists->last() : 0),
funcArgs.isEmpty() ? 0 : root->argList);
//
// we still have the problem that
// MemberDef::writeDocumentation() in memberdef.cpp
// writes the template argument list for the class,
// as if this member is a member of the class.
// fortunately, MemberDef::writeDocumentation() has
// a special mechanism that allows us to totally
// override the set of template argument lists that
// are printed. We use that and set it to the
// template argument lists of the related function.
//
md->setDefinitionTemplateParameterLists(root->tArgLists);
md->setTagInfo(rootNav->tagInfo());
//printf("Related member name=`%s' decl=`%s' bodyLine=`%d'\n",
// funcName.data(),funcDecl.data(),root->bodyLine);
// try to find the matching line number of the body from the
// global function list
bool found=FALSE;
if (root->bodyLine==-1)
{
MemberName *rmn=Doxygen::functionNameSDict->find(funcName);
if (rmn)
{
MemberDef *rmd=rmn->first();
while (rmd && !found) // see if we got another member with matching arguments
{
LockingPtr<ArgumentList> rmdAl = rmd->argumentList();
// check for matching argument lists
if (
matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmdAl.pointer(),
cd,fd,root->argList,
TRUE)
)
{
found=TRUE;
}
if (!found) rmd=rmn->next();
}
if (rmd) // member found -> copy line number info
{
md->setBodySegment(rmd->getStartBodyLine(),rmd->getEndBodyLine());
md->setBodyDef(rmd->getBodyDef());
//md->setBodyMember(rmd);
}
}
}
if (!found) // line number could not be found or is available in this
// entry
{
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setBodyDef(fd);
}
//if (root->mGrpId!=-1)
//{
// md->setMemberGroup(memberGroupDict[root->mGrpId]);
//}
md->setMemberClass(cd);
md->setMemberSpecifiers(root->spec);
md->setDefinition(funcDecl);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setDocsForDefinition(!root->proto);
md->setPrototype(root->proto);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->addSectionsToDefinition(root->anchors);
md->setMemberGroupId(root->mGrpId);
//md->setMemberDefTemplateArguments(root->mtArgList);
mn->append(md);
cd->insertMember(md);
cd->insertUsedFile(root->fileName);
md->setRefItems(root->sli);
if (root->relatesType == Duplicate) md->setRelatedAlso(cd);
addMemberToGroups(root,md);
//printf("Adding member=%s\n",md->name().data());
if (newMemberName)
{
//Doxygen::memberNameList.append(mn);
//Doxygen::memberNameDict.insert(funcName,mn);
Doxygen::memberNameSDict->append(funcName,mn);
}
}
if (root->relatesType == Duplicate)
{
if (!findGlobalMember(rootNav,namespaceName,funcName,funcTempList,funcArgs,funcDecl))
{
QCString fullFuncDecl=funcDecl.copy();
if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
warn(root->fileName,root->startLine,
"Warning: Cannot determine file/namespace for relatedalso function\n%s",
fullFuncDecl.data()
);
}
}
}
else
{
warn_undoc(root->fileName,root->startLine,
"Warning: class `%s' for related function `%s' is not "
"documented.",
className.data(),funcName.data()
);
}
}
else if (rootNav->parent() && rootNav->parent()->section()==Entry::OBJCIMPL_SEC)
{
localObjCMethod:
ClassDef *cd;
//printf("scopeName=`%s' className=`%s'\n",scopeName.data(),className.data());
if (Config_getBool("EXTRACT_LOCAL_METHODS") && (cd=getClass(scopeName)))
{
//printf("Local objective C method `%s' of class `%s' found\n",root->name.data(),cd->name().data());
MemberDef *md=new MemberDef(
root->fileName,root->startLine,
funcType,funcName,funcArgs,exceptions,
root->protection,root->virt,root->stat,Member,
MemberDef::Function,0,root->argList);
md->setTagInfo(rootNav->tagInfo());
md->makeImplementationDetail();
md->setMemberClass(cd);
md->setDefinition(funcDecl);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->setDocsForDefinition(!root->proto);
md->setPrototype(root->proto);
md->addSectionsToDefinition(root->anchors);
md->setBodySegment(root->bodyLine,root->endBodyLine);
FileDef *fd=rootNav->fileDef();
md->setBodyDef(fd);
md->setMemberSpecifiers(root->spec);
md->setMemberGroupId(root->mGrpId);
cd->insertMember(md);
cd->insertUsedFile(root->fileName);
md->setRefItems(root->sli);
if ((mn=Doxygen::memberNameSDict->find(root->name)))
{
mn->append(md);
}
else
{
mn = new MemberName(root->name);
mn->append(md);
Doxygen::memberNameSDict->append(root->name,mn);
}
}
else
{
// local objective C method found for class without interface
}
}
else // unrelated not overloaded member found
{
bool globMem = findGlobalMember(rootNav,namespaceName,funcName,funcTempList,funcArgs,funcDecl);
if (className.isEmpty() && !globMem)
{
warn(root->fileName,root->startLine,
"Warning: class for member `%s' cannot "
"be found.", funcName.data()
);
}
else if (!className.isEmpty() && !globMem)
{
warn(root->fileName,root->startLine,
"Warning: member `%s' of class `%s' cannot be found",
funcName.data(),className.data());
}
}
}
else
{
// this should not be called
warn(root->fileName,root->startLine,
"Warning: member with no name found.");
}
return;
}
//----------------------------------------------------------------------
// find the members corresponding to the different documentation blocks
// that are extracted from the sources.
static void filterMemberDocumentation(EntryNav *rootNav)
{
Entry *root = rootNav->entry();
int i=-1,l;
Debug::print(Debug::FindMembers,0,
"findMemberDocumentation(): root->type=`%s' root->inside=`%s' root->name=`%s' root->args=`%s' section=%x root->spec=%d root->mGrpId=%d\n",
root->type.data(),root->inside.data(),root->name.data(),root->args.data(),root->section,root->spec,root->mGrpId
);
//printf("rootNav->parent()->name()=%s\n",rootNav->parent()->name().data());
bool isFunc=TRUE;
if (root->relatesType == Duplicate && !root->relates.isEmpty())
{
QCString tmp = root->relates;
root->relates.resize(0);
filterMemberDocumentation(rootNav);
root->relates = tmp;
}
if ( // detect func variable/typedef to func ptr
(i=findFunctionPtr(root->type,&l))!=-1
)
{
//printf("Fixing function pointer!\n");
// fix type and argument
root->args.prepend(root->type.right(root->type.length()-i-l));
root->type=root->type.left(i+l);
//printf("Results type=%s,name=%s,args=%s\n",root->type.data(),root->name.data(),root->args.data());
isFunc=FALSE;
}
else if ((root->type.left(8)=="typedef " && root->args.find('(')!=-1))
// detect function types marked as functions
{
isFunc=FALSE;
}
//printf("Member %s isFunc=%d\n",root->name.data(),isFunc);
if (root->section==Entry::MEMBERDOC_SEC)
{
//printf("Documentation for inline member `%s' found args=`%s'\n",
// root->name.data(),root->args.data());
//if (root->relates.length()) printf(" Relates %s\n",root->relates.data());
if (root->type.isEmpty())
{
findMember(rootNav,root->name+root->args+root->exception,FALSE,isFunc);
}
else
{
findMember(rootNav,root->type+" "+root->name+root->args+root->exception,FALSE,isFunc);
}
}
else if (root->section==Entry::OVERLOADDOC_SEC)
{
//printf("Overloaded member %s found\n",root->name.data());
findMember(rootNav,root->name,TRUE,isFunc);
}
else if
((root->section==Entry::FUNCTION_SEC // function
||
(root->section==Entry::VARIABLE_SEC && // variable
!root->type.isEmpty() && // with a type
g_compoundKeywordDict.find(root->type)==0 // that is not a keyword
// (to skip forward declaration of class etc.)
)
)
)
{
//printf("Documentation for member `%s' found args=`%s' excp=`%s'\n",
// root->name.data(),root->args.data(),root->exception.data());
//if (root->relates.length()) printf(" Relates %s\n",root->relates.data());
//printf("Inside=%s\n Relates=%s\n",root->inside.data(),root->relates.data());
if (root->type=="friend class" || root->type=="friend struct" ||
root->type=="friend union")
{
findMember(rootNav,
root->type+" "+
root->name,
FALSE,FALSE);
}
else if (!root->type.isEmpty())
{
findMember(rootNav,
root->type+" "+
root->inside+
root->name+
root->args+
root->exception,
FALSE,isFunc);
}
else
{
findMember(rootNav,
root->inside+
root->name+
root->args+
root->exception,
FALSE,isFunc);
}
}
else if (root->section==Entry::DEFINE_SEC && !root->relates.isEmpty())
{
findMember(rootNav,root->name+root->args,FALSE,!root->args.isEmpty());
}
else if (root->section==Entry::VARIABLEDOC_SEC)
{
//printf("Documentation for variable %s found\n",root->name.data());
//if (!root->relates.isEmpty()) printf(" Relates %s\n",root->relates.data());
findMember(rootNav,root->name,FALSE,FALSE);
}
else
{
// skip section
//printf("skip section\n");
}
}
static void findMemberDocumentation(EntryNav *rootNav)
{
if (rootNav->section()==Entry::MEMBERDOC_SEC ||
rootNav->section()==Entry::OVERLOADDOC_SEC ||
rootNav->section()==Entry::FUNCTION_SEC ||
rootNav->section()==Entry::VARIABLE_SEC ||
rootNav->section()==Entry::VARIABLEDOC_SEC ||
rootNav->section()==Entry::DEFINE_SEC
)
{
rootNav->loadEntry(g_storage);
filterMemberDocumentation(rootNav);
rootNav->releaseEntry();
}
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
EntryNav *e;
for (;(e=eli.current());++eli)
{
if (e->section()!=Entry::ENUM_SEC) findMemberDocumentation(e);
}
}
}
//----------------------------------------------------------------------
static void findObjCMethodDefinitions(EntryNav *rootNav)
{
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
EntryNav *objCImplNav;
for (;(objCImplNav=eli.current());++eli)
{
if (objCImplNav->section()==Entry::OBJCIMPL_SEC && objCImplNav->children())
{
EntryNavListIterator seli(*objCImplNav->children());
EntryNav *objCMethodNav;
for (;(objCMethodNav=seli.current());++seli)
{
if (objCMethodNav->section()==Entry::FUNCTION_SEC)
{
objCMethodNav->loadEntry(g_storage);
Entry *objCMethod = objCMethodNav->entry();
//Printf(" Found ObjC method definition %s\n",objCMethod->name.data());
findMember(objCMethodNav, objCMethod->type+" "+objCImplNav->name()+"::"+
objCMethod->name+" "+objCMethod->args, FALSE,TRUE);
objCMethod->section=Entry::EMPTY_SEC;
objCMethodNav->releaseEntry();
}
}
}
}
}
}
//----------------------------------------------------------------------
// find and add the enumeration to their classes, namespaces or files
static void findEnums(EntryNav *rootNav)
{
if (rootNav->section()==Entry::ENUM_SEC)
// non anonymous enumeration
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
MemberDef *md=0;
ClassDef *cd=0;
FileDef *fd=0;
NamespaceDef *nd=0;
MemberNameSDict *mnsd=0;
bool isGlobal;
bool isRelated=FALSE;
bool isMemberOf=FALSE;
//printf("Found enum with name `%s' relates=%s\n",root->name.data(),root->relates.data());
int i;
QCString name;
QCString scope;
if ((i=root->name.findRev("::"))!=-1) // scope is specified
{
scope=root->name.left(i); // extract scope
name=root->name.right(root->name.length()-i-2); // extract name
if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope);
}
else // no scope, check the scope in which the docs where found
{
if (( rootNav->parent()->section() & Entry::SCOPE_MASK )
&& !rootNav->parent()->name().isEmpty()
) // found enum docs inside a compound
{
scope=rootNav->parent()->name();
if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope);
}
name=root->name;
}
if (!root->relates.isEmpty())
{ // related member, prefix user specified scope
isRelated=TRUE;
isMemberOf=(root->relatesType == MemberOf);
if (getClass(root->relates)==0 && !scope.isEmpty())
scope=mergeScopes(scope,root->relates);
else
scope=root->relates.copy();
if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope);
}
if (cd && !name.isEmpty()) // found a enum inside a compound
{
//printf("Enum `%s'::`%s'\n",cd->name(),name.data());
fd=0;
mnsd=Doxygen::memberNameSDict;
isGlobal=FALSE;
}
else if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') // found enum inside namespace
{
mnsd=Doxygen::functionNameSDict;
isGlobal=TRUE;
}
else // found a global enum
{
fd=rootNav->fileDef();
mnsd=Doxygen::functionNameSDict;
isGlobal=TRUE;
}
if (!name.isEmpty())
{
// new enum type
md = new MemberDef(
root->fileName,root->startLine,
0,name,0,0,
root->protection,Normal,FALSE,
isMemberOf ? Foreign : isRelated ? Related : Member,
MemberDef::Enumeration,
0,0);
md->setTagInfo(rootNav->tagInfo());
if (!isGlobal) md->setMemberClass(cd); else md->setFileDef(fd);
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setBodyDef(rootNav->fileDef());
//printf("Enum %s definition at line %d of %s: protection=%d\n",
// root->name.data(),root->bodyLine,root->fileName.data(),root->protection);
md->addSectionsToDefinition(root->anchors);
md->setMemberGroupId(root->mGrpId);
md->enableCallGraph(root->callGraph);
md->enableCallerGraph(root->callerGraph);
//printf("%s::setRefItems(%d)\n",md->name().data(),root->sli?root->sli->count():-1);
md->setRefItems(root->sli);
//printf("found enum %s nd=%p\n",name.data(),nd);
bool defSet=FALSE;
if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
{
if (isRelated || Config_getBool("HIDE_SCOPE_NAMES"))
{
md->setDefinition(name);
}
else
{
md->setDefinition(nd->name()+"::"+name);
}
//printf("definition=%s\n",md->definition());
defSet=TRUE;
md->setNamespace(nd);
nd->insertMember(md);
}
// even if we have already added the enum to a namespace, we still
// also want to add it to other appropriate places such as file
// or class.
if (isGlobal)
{
if (!defSet) md->setDefinition(name);
if (fd==0 && rootNav->parent())
{
fd=rootNav->parent()->fileDef();
}
if (fd)
{
md->setFileDef(fd);
fd->insertMember(md);
}
}
else if (cd)
{
if (isRelated || Config_getBool("HIDE_SCOPE_NAMES"))
{
md->setDefinition(name);
}
else
{
md->setDefinition(cd->name()+"::"+name);
}
cd->insertMember(md);
cd->insertUsedFile(root->fileName);
}
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setDocsForDefinition(!root->proto);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
//printf("Adding member=%s\n",md->name().data());
MemberName *mn;
if ((mn=(*mnsd)[name]))
{
// this is used if the same enum is in multiple namespaces/classes
mn->append(md);
}
else // new enum name
{
mn = new MemberName(name);
mn->append(md);
mnsd->append(name,mn);
//printf("add %s to new memberName. Now %d members\n",
// name.data(),mn->count());
}
addMemberToGroups(root,md);
#if 0
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
EntryNav *e;
for (;(e=eli.current());++eli)
{
//printf("e->name=%s isRelated=%d\n",e->name.data(),isRelated);
MemberName *fmn=0;
MemberNameSDict *emnsd = isRelated ? Doxygen::functionNameSDict : mnsd;
if (!e->name().isEmpty() && (fmn=(*emnsd)[e->name()]))
// get list of members with the same name as the field
{
MemberNameIterator fmni(*fmn);
MemberDef *fmd;
for (fmni.toFirst(); (fmd=fmni.current()) ; ++fmni)
{
if (fmd->isEnumValue())
{
//printf("found enum value with same name\n");
if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
{
NamespaceDef *fnd=fmd->getNamespaceDef();
if (fnd==nd) // enum value is inside a namespace
{
md->insertEnumField(fmd);
fmd->setEnumScope(md);
}
}
else if (isGlobal)
{
FileDef *ffd=fmd->getFileDef();
if (ffd==fd) // enum value has file scope
{
md->insertEnumField(fmd);
fmd->setEnumScope(md);
}
}
else if (isRelated && cd) // reparent enum value to
// match the enum's scope
{
md->insertEnumField(fmd); // add field def to list
fmd->setEnumScope(md); // cross ref with enum name
fmd->setEnumClassScope(cd); // cross ref with enum name
fmd->setOuterScope(cd);
fmd->makeRelated();
cd->insertMember(fmd);
}
else
{
ClassDef *fcd=fmd->getClassDef();
if (fcd==cd) // enum value is inside a class
{
//printf("Inserting enum field %s in enum scope %s\n",
// fmd->name().data(),md->name().data());
md->insertEnumField(fmd); // add field def to list
fmd->setEnumScope(md); // cross ref with enum name
}
}
}
}
}
}
}
#endif
}
rootNav->releaseEntry();
}
else
{
RECURSE_ENTRYTREE(findEnums,rootNav);
}
}
//----------------------------------------------------------------------
static void addEnumValuesToEnums(EntryNav *rootNav)
{
if (rootNav->section()==Entry::ENUM_SEC)
// non anonymous enumeration
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
ClassDef *cd=0;
FileDef *fd=0;
NamespaceDef *nd=0;
MemberNameSDict *mnsd=0;
bool isGlobal;
bool isRelated=FALSE;
//printf("Found enum with name `%s' relates=%s\n",root->name.data(),root->relates.data());
int i;
QCString name;
QCString scope;
if ((i=root->name.findRev("::"))!=-1) // scope is specified
{
scope=root->name.left(i); // extract scope
name=root->name.right(root->name.length()-i-2); // extract name
if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope);
}
else // no scope, check the scope in which the docs where found
{
if (( rootNav->parent()->section() & Entry::SCOPE_MASK )
&& !rootNav->parent()->name().isEmpty()
) // found enum docs inside a compound
{
scope=rootNav->parent()->name();
if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope);
}
name=root->name;
}
if (!root->relates.isEmpty())
{ // related member, prefix user specified scope
isRelated=TRUE;
if (getClass(root->relates)==0 && !scope.isEmpty())
scope=mergeScopes(scope,root->relates);
else
scope=root->relates.copy();
if ((cd=getClass(scope))==0) nd=getResolvedNamespace(scope);
}
if (cd && !name.isEmpty()) // found a enum inside a compound
{
//printf("Enum in class `%s'::`%s'\n",cd->name().data(),name.data());
fd=0;
mnsd=Doxygen::memberNameSDict;
isGlobal=FALSE;
}
else if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') // found enum inside namespace
{
//printf("Enum in namespace `%s'::`%s'\n",nd->name().data(),name.data());
mnsd=Doxygen::functionNameSDict;
isGlobal=TRUE;
}
else // found a global enum
{
fd=rootNav->fileDef();
//printf("Enum in file `%s': `%s'\n",fd->name().data(),name.data());
mnsd=Doxygen::functionNameSDict;
isGlobal=TRUE;
}
if (!name.isEmpty())
{
MemberName *mn = mnsd->find(name); // for all members with this name
if (mn)
{
MemberNameIterator mni(*mn);
MemberDef *md;
for (mni.toFirst(); (md=mni.current()) ; ++mni) // for each enum in this list
{
if (md->isEnumerate() && rootNav->children())
{
EntryNavListIterator eli(*rootNav->children()); // for each enum value
EntryNav *e;
for (;(e=eli.current());++eli)
{
SrcLangExt sle;
if (rootNav->fileDef() &&
( (sle=getLanguageFromFileName(rootNav->fileDef()->name()))==SrcLangExt_CSharp
|| sle==SrcLangExt_Java || sle==SrcLangExt_XML
)
)
{
// Unlike C++, for C# enum value are only inside the enum
// scope, so we must create them here and only add them to the
// enum
e->loadEntry(g_storage);
Entry *root = e->entry();
if (md->qualifiedName()==rootNav->name()) // enum value scope matches that of the enum
{
MemberDef *fmd=new MemberDef(
root->fileName,root->startLine,
root->type,root->name,root->args,0,
Public, Normal,root->stat,Member,
MemberDef::EnumValue,0,0);
if (md->getClassDef()) fmd->setMemberClass(md->getClassDef());
else if (md->getNamespaceDef()) fmd->setNamespace(md->getNamespaceDef());
else if (md->getFileDef()) fmd->setFileDef(md->getFileDef());
fmd->setOuterScope(md->getOuterScope());
fmd->setTagInfo(e->tagInfo());
fmd->setDocumentation(root->doc,root->docFile,root->docLine);
fmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
fmd->addSectionsToDefinition(root->anchors);
fmd->setInitializer(root->initializer);
fmd->setMaxInitLines(root->initLines);
fmd->setMemberGroupId(root->mGrpId);
fmd->setExplicitExternal(root->explicitExternal);
if (fmd)
{
md->insertEnumField(fmd);
fmd->setEnumScope(md);
}
}
e->releaseEntry();
}
else
{
//printf("e->name=%s isRelated=%d\n",e->name().data(),isRelated);
MemberName *fmn=0;
MemberNameSDict *emnsd = isRelated ? Doxygen::functionNameSDict : mnsd;
if (!e->name().isEmpty() && (fmn=(*emnsd)[e->name()]))
// get list of members with the same name as the field
{
MemberNameIterator fmni(*fmn);
MemberDef *fmd;
for (fmni.toFirst(); (fmd=fmni.current()) ; ++fmni)
{
if (fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
{
//printf("found enum value with same name %s in scope %s\n",
// fmd->name().data(),fmd->getOuterScope()->name().data());
if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
{
NamespaceDef *fnd=fmd->getNamespaceDef();
if (fnd==nd) // enum value is inside a namespace
{
md->insertEnumField(fmd);
fmd->setEnumScope(md);
}
}
else if (isGlobal)
{
FileDef *ffd=fmd->getFileDef();
if (ffd==fd) // enum value has file scope
{
md->insertEnumField(fmd);
fmd->setEnumScope(md);
}
}
else if (isRelated && cd) // reparent enum value to
// match the enum's scope
{
md->insertEnumField(fmd); // add field def to list
fmd->setEnumScope(md); // cross ref with enum name
fmd->setEnumClassScope(cd); // cross ref with enum name
fmd->setOuterScope(cd);
fmd->makeRelated();
cd->insertMember(fmd);
}
else
{
ClassDef *fcd=fmd->getClassDef();
if (fcd==cd) // enum value is inside a class
{
//printf("Inserting enum field %s in enum scope %s\n",
// fmd->name().data(),md->name().data());
md->insertEnumField(fmd); // add field def to list
fmd->setEnumScope(md); // cross ref with enum name
}
}
}
}
}
}
}
}
}
}
}
rootNav->releaseEntry();
}
else
{
RECURSE_ENTRYTREE(addEnumValuesToEnums,rootNav);
}
}
//----------------------------------------------------------------------
// find the documentation blocks for the enumerations
static void findEnumDocumentation(EntryNav *rootNav)
{
if (rootNav->section()==Entry::ENUMDOC_SEC
&& !rootNav->name().isEmpty()
&& rootNav->name().at(0)!='@' // skip anonymous enums
)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
//printf("Found docs for enum with name `%s' in context %s\n",
// root->name.data(),root->parent->name.data());
int i;
QCString name;
QCString scope;
if ((i=root->name.findRev("::"))!=-1) // scope is specified as part of the name
{
name=root->name.right(root->name.length()-i-2); // extract name
scope=root->name.left(i); // extract scope
//printf("Scope=`%s' Name=`%s'\n",scope.data(),name.data());
}
else // just the name
{
name=root->name;
}
if (( rootNav->parent()->section() & Entry::SCOPE_MASK )
&& !rootNav->parent()->name().isEmpty()
) // found enum docs inside a compound
{
if (!scope.isEmpty()) scope.prepend("::");
scope.prepend(rootNav->parent()->name());
}
ClassDef *cd=getClass(scope);
if (!name.isEmpty())
{
bool found=FALSE;
if (cd)
{
//printf("Enum: scope=`%s' name=`%s'\n",cd->name(),name.data());
QCString className=cd->name().copy();
MemberName *mn=Doxygen::memberNameSDict->find(name);
if (mn)
{
MemberNameIterator mni(*mn);
MemberDef *md;
for (mni.toFirst();(md=mni.current()) && !found;++mni)
{
ClassDef *cd=md->getClassDef();
if (cd && cd->name()==className && md->isEnumerate())
{
// documentation outside a compound overrides the documentation inside it
#if 0
if (!md->documentation() || rootNav->parent()->name().isEmpty())
#endif
{
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setDocsForDefinition(!root->proto);
}
// brief descriptions inside a compound override the documentation
// outside it
#if 0
if (!md->briefDescription() || !rootNav->parent()->name().isEmpty())
#endif
{
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
}
if (!md->inbodyDocumentation() || !rootNav->parent()->name().isEmpty())
{
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
}
if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
{
md->setMemberGroupId(root->mGrpId);
}
md->addSectionsToDefinition(root->anchors);
md->setRefItems(root->sli);
GroupDef *gd=md->getGroupDef();
if (gd==0 &&root->groups->first()!=0) // member not grouped but out-of-line documentation is
{
addMemberToGroups(root,md);
}
found=TRUE;
}
}
}
else
{
//printf("MemberName %s not found!\n",name.data());
}
}
else // enum outside class
{
//printf("Enum outside class: %s grpId=%d\n",name.data(),root->mGrpId);
MemberName *mn=Doxygen::functionNameSDict->find(name);
if (mn)
{
MemberNameIterator mni(*mn);
MemberDef *md;
for (mni.toFirst();(md=mni.current()) && !found;++mni)
{
if (md->isEnumerate())
{
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setDocsForDefinition(!root->proto);
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
md->addSectionsToDefinition(root->anchors);
md->setMemberGroupId(root->mGrpId);
GroupDef *gd=md->getGroupDef();
if (gd==0 && root->groups->first()!=0) // member not grouped but out-of-line documentation is
{
addMemberToGroups(root,md);
}
found=TRUE;
}
}
}
}
if (!found)
{
warn(root->fileName,root->startLine,
"Warning: Documentation for undefined enum `%s' found.",
name.data()
);
}
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(findEnumDocumentation,rootNav);
}
// seach for each enum (member or function) in mnl if it has documented
// enum values.
static void findDEV(const MemberNameSDict &mnsd)
{
MemberName *mn;
MemberNameSDict::Iterator mnli(mnsd);
// for each member name
for (mnli.toFirst();(mn=mnli.current());++mnli)
{
MemberDef *md;
MemberNameIterator mni(*mn);
// for each member definition
for (mni.toFirst();(md=mni.current());++mni)
{
if (md->isEnumerate()) // member is an enum
{
LockingPtr<MemberList> fmdl = md->enumFieldList();
int documentedEnumValues=0;
if (fmdl!=0) // enum has values
{
MemberListIterator fmni(*fmdl);
MemberDef *fmd;
// for each enum value
for (fmni.toFirst();(fmd=fmni.current());++fmni)
{
if (fmd->isLinkableInProject()) documentedEnumValues++;
}
}
// at least one enum value is documented
if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
}
}
}
}
// seach for each enum (member or function) if it has documented enum
// values.
static void findDocumentedEnumValues()
{
findDEV(*Doxygen::memberNameSDict);
findDEV(*Doxygen::functionNameSDict);
}
//----------------------------------------------------------------------
static void addMembersToIndex()
{
MemberName *mn;
MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
// for each member name
for (mnli.toFirst();(mn=mnli.current());++mnli)
{
MemberDef *md;
MemberNameIterator mni(*mn);
// for each member definition
for (mni.toFirst();(md=mni.current());++mni)
{
addClassMemberNameToIndex(md);
}
}
MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict);
// for each member name
for (fnli.toFirst();(mn=fnli.current());++fnli)
{
MemberDef *md;
MemberNameIterator mni(*mn);
// for each member definition
for (mni.toFirst();(md=mni.current());++mni)
{
if (md->getNamespaceDef())
{
addNamespaceMemberNameToIndex(md);
}
else
{
addFileMemberNameToIndex(md);
}
}
}
}
//----------------------------------------------------------------------
// computes the relation between all members. For each member `m'
// the members that override the implementation of `m' are searched and
// the member that `m' overrides is searched.
static void computeMemberRelations()
{
MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
MemberName *mn;
for ( ; (mn=mnli.current()) ; ++mnli ) // for each member name
{
MemberNameIterator mdi(*mn);
MemberDef *md;
for ( ; (md=mdi.current()) ; ++mdi ) // for each member with a specific name
{
MemberDef *bmd = mn->first(); // for each other member with the same name
while (bmd)
{
ClassDef *mcd = md->getClassDef();
if (mcd && mcd->baseClasses())
{
ClassDef *bmcd = bmd->getClassDef();
//printf("Check relation between `%s'::`%s' (%p) and `%s'::`%s' (%p)\n",
// mcd->name().data(),md->name().data(),md,
// bmcd->name().data(),bmd->name().data(),bmd
// );
if (md!=bmd && bmcd && mcd && bmcd!=mcd && mcd->isBaseClass(bmcd,TRUE))
{
LockingPtr<ArgumentList> bmdAl = bmd->argumentList();
LockingPtr<ArgumentList> mdAl = md->argumentList();
//printf(" Base argList=`%s'\n Super argList=`%s'\n",
// argListToString(bmdAl.pointer()).data(),
// argListToString(mdAl.pointer()).data()
// );
if (
matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmdAl.pointer(),
md->getOuterScope(), md->getFileDef(), mdAl.pointer(),
TRUE
)
)
{
//printf(" match found!\n");
if (mcd && bmcd &&
mcd->isLinkable() && bmcd->isLinkable()
)
{
MemberDef *rmd;
if ((rmd=md->reimplements())==0 ||
minClassDistance(mcd,bmcd)<minClassDistance(mcd,rmd->getClassDef())
)
{
//printf("setting (new) reimplements member\n");
md->setReimplements(bmd);
}
//printf("%s: add reimplements member %s\n",mcd->name().data(),bmcd->name().data());
//md->setImplements(bmd);
//printf("%s: add reimplementedBy member %s\n",bmcd->name().data(),mcd->name().data());
bmd->insertReimplementedBy(md);
}
}
}
}
bmd = mn->next();
}
}
}
}
//----------------------------------------------------------------------------
//static void computeClassImplUsageRelations()
//{
// ClassDef *cd;
// ClassSDict::Iterator cli(*Doxygen::classSDict);
// for (;(cd=cli.current());++cli)
// {
// cd->determineImplUsageRelation();
// }
//}
//----------------------------------------------------------------------------
static void createTemplateInstanceMembers()
{
ClassSDict::Iterator cli(*Doxygen::classSDict);
ClassDef *cd;
// for each class
for (cli.toFirst();(cd=cli.current());++cli)
{
// that is a template
QDict<ClassDef> *templInstances = cd->getTemplateInstances();
if (templInstances)
{
QDictIterator<ClassDef> qdi(*templInstances);
ClassDef *tcd=0;
// for each instance of the template
for (qdi.toFirst();(tcd=qdi.current());++qdi)
{
tcd->addMembersToTemplateInstance(cd,qdi.currentKey());
}
}
}
}
//----------------------------------------------------------------------------
// builds the list of all members for each class
static void buildCompleteMemberLists()
{
ClassDef *cd;
// merge members of categories into the class they extend
ClassSDict::Iterator cli(*Doxygen::classSDict);
for (cli.toFirst();(cd=cli.current());++cli)
{
int i=cd->name().find('(');
if (i!=-1) // it is an Objective-C category
{
QCString baseName=cd->name().left(i);
ClassDef *baseClass=Doxygen::classSDict->find(baseName);
if (baseClass)
{
//printf("*** merging members of category %s into %s\n",
// cd->name().data(),baseClass->name().data());
baseClass->mergeCategory(cd);
}
}
}
// merge the member list of base classes into the inherited classes.
for (cli.toFirst();(cd=cli.current());++cli)
{
if (// !cd->isReference() && // not an external class
cd->subClasses()==0 && // is a root of the hierarchy
cd->baseClasses()) // and has at least one base class
{
//printf("*** merging members for %s\n",cd->name().data());
cd->mergeMembers();
}
}
// now sort the member list of all classes.
for (cli.toFirst();(cd=cli.current());++cli)
{
if (cd->memberNameInfoSDict()) cd->memberNameInfoSDict()->sort();
}
}
//----------------------------------------------------------------------------
static void generateFileSources()
{
if (documentedHtmlFiles==0) return;
if (Doxygen::inputNameList->count()>0)
{
FileNameListIterator fnli(*Doxygen::inputNameList);
FileName *fn;
for (;(fn=fnli.current());++fnli)
{
FileNameIterator fni(*fn);
FileDef *fd;
for (;(fd=fni.current());++fni)
{
if (fd->generateSourceFile()) // sources need to be shown in the output
{
msg("Generating code for file %s...\n",fd->docName().data());
fd->writeSource(*g_outputList);
}
else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
// we needed to parse the sources even if we do not show them
{
msg("Parsing code for file %s...\n",fd->docName().data());
fd->parseSource();
}
}
}
}
}
//----------------------------------------------------------------------------
static void generateFileDocs()
{
if (documentedHtmlFiles==0) return;
if (Doxygen::inputNameList->count()>0)
{
FileNameListIterator fnli(*Doxygen::inputNameList);
FileName *fn;
for (fnli.toFirst();(fn=fnli.current());++fnli)
{
FileNameIterator fni(*fn);
FileDef *fd;
for (fni.toFirst();(fd=fni.current());++fni)
{
bool doc = fd->isLinkableInProject();
if (doc)
{
msg("Generating docs for file %s...\n",fd->docName().data());
fd->writeDocumentation(*g_outputList);
}
}
}
}
}
//----------------------------------------------------------------------------
static void addSourceReferences()
{
// add source references for class definitions
ClassSDict::Iterator cli(*Doxygen::classSDict);
ClassDef *cd=0;
for (cli.toFirst();(cd=cli.current());++cli)
{
FileDef *fd=cd->getBodyDef();
if (fd && cd->isLinkableInProject() && cd->getStartBodyLine()!=-1)
{
fd->addSourceRef(cd->getStartBodyLine(),cd,0);
}
}
// add source references for namespace definitions
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd=0;
for (nli.toFirst();(nd=nli.current());++nli)
{
FileDef *fd=nd->getBodyDef();
if (fd && nd->isLinkableInProject() && nd->getStartBodyLine()!=-1)
{
fd->addSourceRef(nd->getStartBodyLine(),nd,0);
}
}
// add source references for member names
MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
MemberName *mn=0;
for (mnli.toFirst();(mn=mnli.current());++mnli)
{
MemberNameIterator mni(*mn);
MemberDef *md=0;
for (mni.toFirst();(md=mni.current());++mni)
{
//printf("class member %s\n",md->name().data());
FileDef *fd=md->getBodyDef();
if (fd &&
md->getStartBodyLine()!=-1 &&
md->isLinkableInProject() &&
(fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
)
{
//printf("Found member `%s' in file `%s' at line `%d' def=%s\n",
// md->name().data(),fd->name().data(),md->getStartBodyLine(),md->getOuterScope()->name().data());
fd->addSourceRef(md->getStartBodyLine(),md->getOuterScope(),md);
}
}
}
MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict);
for (fnli.toFirst();(mn=fnli.current());++fnli)
{
MemberNameIterator mni(*mn);
MemberDef *md=0;
for (mni.toFirst();(md=mni.current());++mni)
{
FileDef *fd=md->getBodyDef();
//printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
// md->name().data(),
// md->getStartBodyLine(),md->getEndBodyLine(),fd,
// md->isLinkableInProject(),
// Doxygen::parseSourcesNeeded);
if (fd &&
md->getStartBodyLine()!=-1 &&
md->isLinkableInProject() &&
(fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
)
{
//printf("Found member `%s' in file `%s' at line `%d' def=%s\n",
// md->name().data(),fd->name().data(),md->getStartBodyLine(),md->getOuterScope()->name().data());
fd->addSourceRef(md->getStartBodyLine(),md->getOuterScope(),md);
}
}
}
}
//----------------------------------------------------------------------------
// generate the documentation of all classes
static void generateClassList(ClassSDict &classSDict)
{
ClassSDict::Iterator cli(classSDict);
for ( ; cli.current() ; ++cli )
{
ClassDef *cd=cli.current();
//printf("cd=%s getOuterScope=%p global=%p\n",cd->name().data(),cd->getOuterScope(),Doxygen::globalScope);
if ((cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
cd->getOuterScope()==Doxygen::globalScope // only look at global classes
) && !cd->isHidden()
)
{
// skip external references, anonymous compounds and
// template instances
if ( cd->isLinkableInProject() && cd->templateMaster()==0)
{
msg("Generating docs for compound %s...\n",cd->name().data());
cd->writeDocumentation(*g_outputList);
cd->writeMemberList(*g_outputList);
}
// even for undocumented classes, the inner classes can be documented.
cd->writeDocumentationForInnerClasses(*g_outputList);
}
}
}
static void generateClassDocs()
{
// write the installdox script if necessary
if (Config_getBool("GENERATE_HTML") &&
(Config_getList("TAGFILES").count()>0 ||
Config_getBool("SEARCHENGINE")
)
)
{
writeInstallScript();
}
msg("Generating annotated compound index...\n");
writeAnnotatedIndex(*g_outputList);
//if (Config_getBool("ALPHABETICAL_INDEX"))
//{
msg("Generating alphabetical compound index...\n");
writeAlphabeticalIndex(*g_outputList);
//}
msg("Generating hierarchical class index...\n");
writeHierarchicalIndex(*g_outputList);
msg("Generating member index...\n");
writeClassMemberIndex(*g_outputList);
if (Doxygen::exampleSDict->count()>0)
{
msg("Generating example index...\n");
}
generateClassList(*Doxygen::classSDict);
generateClassList(*Doxygen::hiddenClasses);
}
//----------------------------------------------------------------------------
static void inheritDocumentation()
{
MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
MemberName *mn;
//int count=0;
for (;(mn=mnli.current());++mnli)
{
MemberNameIterator mni(*mn);
MemberDef *md;
for (;(md=mni.current());++mni)
{
//printf("%04d Member `%s'\n",count++,md->name().data());
if (md->documentation().isEmpty() && md->briefDescription().isEmpty())
{ // no documentation yet
MemberDef *bmd = md->reimplements();
while (bmd && bmd->documentation().isEmpty() &&
bmd->briefDescription().isEmpty()
)
{ // search up the inheritance tree for a documentation member
//printf("bmd=%s class=%s\n",bmd->name().data(),bmd->getClassDef()->name().data());
bmd = bmd->reimplements();
}
if (bmd) // copy the documentation from the reimplemented member
{
md->setInheritsDocsFrom(bmd);
md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
md->setDocsForDefinition(bmd->isDocsForDefinition());
md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
md->copyArgumentNames(bmd);
md->setInbodyDocumentation(bmd->inbodyDocumentation(),bmd->inbodyFile(),bmd->inbodyLine());
}
}
}
}
}
//----------------------------------------------------------------------------
static void combineUsingRelations()
{
// for each file
FileNameListIterator fnli(*Doxygen::inputNameList);
FileName *fn;
for (fnli.toFirst();(fn=fnli.current());++fnli)
{
FileNameIterator fni(*fn);
FileDef *fd;
for (fni.toFirst();(fd=fni.current());++fni)
{
fd->visited=FALSE;
}
}
for (fnli.toFirst();(fn=fnli.current());++fnli)
{
FileNameIterator fni(*fn);
FileDef *fd;
for (fni.toFirst();(fd=fni.current());++fni)
{
fd->combineUsingRelations();
}
}
// for each namespace
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd;
for (nli.toFirst() ; (nd=nli.current()) ; ++nli )
{
nd->visited=FALSE;
}
for (nli.toFirst() ; (nd=nli.current()) ; ++nli )
{
nd->combineUsingRelations();
}
}
//----------------------------------------------------------------------------
static void addMembersToMemberGroup()
{
// for each class
ClassSDict::Iterator cli(*Doxygen::classSDict);
ClassDef *cd;
for ( ; (cd=cli.current()) ; ++cli )
{
cd->addMembersToMemberGroup();
}
// for each file
FileName *fn=Doxygen::inputNameList->first();
while (fn)
{
FileDef *fd=fn->first();
while (fd)
{
fd->addMembersToMemberGroup();
fd=fn->next();
}
fn=Doxygen::inputNameList->next();
}
// for each namespace
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd;
for ( ; (nd=nli.current()) ; ++nli )
{
nd->addMembersToMemberGroup();
}
// for each group
GroupSDict::Iterator gli(*Doxygen::groupSDict);
GroupDef *gd;
for (gli.toFirst();(gd=gli.current());++gli)
{
gd->addMembersToMemberGroup();
}
}
//----------------------------------------------------------------------------
static void distributeMemberGroupDocumentation()
{
// for each class
ClassSDict::Iterator cli(*Doxygen::classSDict);
ClassDef *cd;
for ( ; (cd=cli.current()) ; ++cli )
{
cd->distributeMemberGroupDocumentation();
}
// for each file
FileName *fn=Doxygen::inputNameList->first();
while (fn)
{
FileDef *fd=fn->first();
while (fd)
{
fd->distributeMemberGroupDocumentation();
fd=fn->next();
}
fn=Doxygen::inputNameList->next();
}
// for each namespace
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd;
for ( ; (nd=nli.current()) ; ++nli )
{
nd->distributeMemberGroupDocumentation();
}
// for each group
GroupSDict::Iterator gli(*Doxygen::groupSDict);
GroupDef *gd;
for (gli.toFirst();(gd=gli.current());++gli)
{
gd->distributeMemberGroupDocumentation();
}
}
//----------------------------------------------------------------------------
static void findSectionsInDocumentation()
{
// for each class
ClassSDict::Iterator cli(*Doxygen::classSDict);
ClassDef *cd;
for ( ; (cd=cli.current()) ; ++cli )
{
cd->findSectionsInDocumentation();
}
// for each file
FileName *fn=Doxygen::inputNameList->first();
while (fn)
{
FileDef *fd=fn->first();
while (fd)
{
fd->findSectionsInDocumentation();
fd=fn->next();
}
fn=Doxygen::inputNameList->next();
}
// for each namespace
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd;
for ( ; (nd=nli.current()) ; ++nli )
{
nd->findSectionsInDocumentation();
}
// for each group
GroupSDict::Iterator gli(*Doxygen::groupSDict);
GroupDef *gd;
for (gli.toFirst();(gd=gli.current());++gli)
{
gd->findSectionsInDocumentation();
}
// for each page
PageSDict::Iterator pdi(*Doxygen::pageSDict);
PageDef *pd=0;
for (pdi.toFirst();(pd=pdi.current());++pdi)
{
pd->findSectionsInDocumentation();
}
if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
}
static void flushCachedTemplateRelations()
{
// remove all references to classes from the cache
// as there can be new template instances in the inheritance path
// to this class. Optimization: only remove those classes that
// have inheritance instances as direct or indirect sub classes.
QCacheIterator<LookupInfo> ci(Doxygen::lookupCache);
LookupInfo *li=0;
for (ci.toFirst();(li=ci.current());++ci)
{
if (li->classDef)
{
Doxygen::lookupCache.remove(ci.currentKey());
}
}
// remove all cached typedef resolutions whose target is a
// template class as this may now be a template instance
MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict);
MemberName *fn;
for (;(fn=fnli.current());++fnli) // for each global function name
{
MemberNameIterator fni(*fn);
MemberDef *fmd;
for (;(fmd=fni.current());++fni) // for each function with that name
{
if (fmd->isTypedefValCached())
{
ClassDef *cd = fmd->getCachedTypedefVal();
if (cd->isTemplate()) fmd->invalidateTypedefValCache();
}
}
}
MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
for (;(fn=mnli.current());++mnli) // for each class method name
{
MemberNameIterator mni(*fn);
MemberDef *fmd;
for (;(fmd=mni.current());++mni) // for each function with that name
{
if (fmd->isTypedefValCached())
{
ClassDef *cd = fmd->getCachedTypedefVal();
if (cd->isTemplate()) fmd->invalidateTypedefValCache();
}
}
}
}
//----------------------------------------------------------------------------
static void flushUnresolvedRelations()
{
// Remove all unresolved references to classes from the cache.
// This is needed before resolving the inheritance relations, since
// it would otherwise not find the inheritance relation
// for C in the example below, as B::I was already found to be unresolvable
// (which is correct if you igore the inheritance relation between A and B).
//
// class A { class I {} };
// class B : public A {};
// class C : public B::I {};
//
QCacheIterator<LookupInfo> ci(Doxygen::lookupCache);
LookupInfo *li=0;
for (ci.toFirst();(li=ci.current());++ci)
{
if (li->classDef==0 && li->typeDef==0)
{
Doxygen::lookupCache.remove(ci.currentKey());
}
}
MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict);
MemberName *fn;
for (;(fn=fnli.current());++fnli) // for each global function name
{
MemberNameIterator fni(*fn);
MemberDef *fmd;
for (;(fmd=fni.current());++fni) // for each function with that name
{
fmd->invalidateCachedArgumentTypes();
}
}
MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
for (;(fn=mnli.current());++mnli) // for each class method name
{
MemberNameIterator mni(*fn);
MemberDef *fmd;
for (;(fmd=mni.current());++mni) // for each function with that name
{
fmd->invalidateCachedArgumentTypes();
}
}
}
//----------------------------------------------------------------------------
static void findDefineDocumentation(EntryNav *rootNav)
{
if ((rootNav->section()==Entry::DEFINEDOC_SEC ||
rootNav->section()==Entry::DEFINE_SEC) && !rootNav->name().isEmpty()
)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
//printf("found define `%s' `%s' brief=`%s' doc=`%s'\n",
// root->name.data(),root->args.data(),root->brief.data(),root->doc.data());
if (rootNav->tagInfo() && !root->name.isEmpty()) // define read from a tag file
{
MemberDef *md=new MemberDef("<tagfile>",1,
"#define",root->name,root->args,0,
Public,Normal,FALSE,Member,MemberDef::Define,0,0);
md->setTagInfo(rootNav->tagInfo());
//printf("Searching for `%s' fd=%p\n",filePathName.data(),fd);
md->setFileDef(rootNav->parent()->fileDef());
//printf("Adding member=%s\n",md->name().data());
MemberName *mn;
if ((mn=Doxygen::functionNameSDict->find(root->name)))
{
mn->append(md);
}
else
{
mn = new MemberName(root->name);
mn->append(md);
Doxygen::functionNameSDict->append(root->name,mn);
}
}
MemberName *mn=Doxygen::functionNameSDict->find(root->name);
if (mn)
{
int count=0;
MemberDef *md=mn->first();
while (md)
{
if (md->memberType()==MemberDef::Define) count++;
md=mn->next();
}
if (count==1)
{
md=mn->first();
while (md)
{
if (md->memberType()==MemberDef::Define)
{
#if 0
if (md->documentation().isEmpty())
#endif
{
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setDocsForDefinition(!root->proto);
}
#if 0
if (md->briefDescription().isEmpty())
#endif
{
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
}
if (md->inbodyDocumentation().isEmpty())
{
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
}
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setBodyDef(rootNav->fileDef());
md->addSectionsToDefinition(root->anchors);
md->setMaxInitLines(root->initLines);
md->setRefItems(root->sli);
if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
addMemberToGroups(root,md);
}
md=mn->next();
}
}
else if (count>1 &&
(!root->doc.isEmpty() ||
!root->brief.isEmpty() ||
root->bodyLine!=-1
)
)
// multiple defines don't know where to add docs
// but maybe they are in different files together with their documentation
{
md=mn->first();
while (md)
{
if (md->memberType()==MemberDef::Define)
{
FileDef *fd=md->getFileDef();
if (fd && fd->absFilePath()==root->fileName)
// doc and define in the same file assume they belong together.
{
#if 0
if (md->documentation().isEmpty())
#endif
{
md->setDocumentation(root->doc,root->docFile,root->docLine);
md->setDocsForDefinition(!root->proto);
}
#if 0
if (md->briefDescription().isEmpty())
#endif
{
md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
}
if (md->inbodyDocumentation().isEmpty())
{
md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
}
md->setBodySegment(root->bodyLine,root->endBodyLine);
md->setBodyDef(rootNav->fileDef());
md->addSectionsToDefinition(root->anchors);
md->setRefItems(root->sli);
if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
addMemberToGroups(root,md);
}
}
md=mn->next();
}
//warn("Warning: define %s found in the following files:\n",root->name.data());
//warn("Cannot determine where to add the documentation found "
// "at line %d of file %s. \n",
// root->startLine,root->fileName.data());
}
}
else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
{
static bool preEnabled = Config_getBool("ENABLE_PREPROCESSING");
if (preEnabled)
{
warn(root->fileName,root->startLine,
"Warning: documentation for unknown define %s found.\n",
root->name.data()
);
}
else
{
warn(root->fileName,root->startLine,
"Warning: found documented #define but ignoring it because "
"ENABLE_PREPROCESSING is NO.\n",
root->name.data()
);
}
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(findDefineDocumentation,rootNav);
}
//----------------------------------------------------------------------------
static void findDirDocumentation(EntryNav *rootNav)
{
if (rootNav->section() == Entry::DIRDOC_SEC)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
QCString normalizedName = root->name;
normalizedName = substitute(normalizedName,"\\","/");
if (normalizedName.at(normalizedName.length()-1)!='/')
{
normalizedName+='/';
}
DirDef *dir,*matchingDir=0;
SDict<DirDef>::Iterator sdi(*Doxygen::directories);
for (sdi.toFirst();(dir=sdi.current());++sdi)
{
//printf("Dir: %s<->%s\n",dir->name().data(),normalizedName.data());
if (dir->name().right(normalizedName.length())==normalizedName)
{
if (matchingDir)
{
warn(root->fileName,root->startLine,
"Warning: \\dir command matches multiple directories.\n"
" Applying the command for directory %s\n"
" Ignoring the command for directory %s\n",
matchingDir->name().data(),dir->name().data()
);
}
else
{
matchingDir=dir;
}
}
}
if (matchingDir)
{
//printf("Match for with dir %s\n",matchingDir->name().data());
matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
matchingDir->setRefItems(root->sli);
addDirToGroups(root,matchingDir);
}
else
{
warn(root->fileName,root->startLine,"Warning: No matching "
"directory found for command \\dir %s\n",root->name.data());
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(findDirDocumentation,rootNav);
}
//----------------------------------------------------------------------------
// create a (sorted) list of separate documentation pages
static void buildPageList(EntryNav *rootNav)
{
if (rootNav->section() == Entry::PAGEDOC_SEC)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
if (!root->name.isEmpty())
{
addRelatedPage(rootNav);
}
rootNav->releaseEntry();
}
else if (rootNav->section() == Entry::MAINPAGEDOC_SEC)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
QCString title=root->args.stripWhiteSpace();
if (title.isEmpty()) title=theTranslator->trMainPage();
QCString name = Config_getBool("GENERATE_TREEVIEW")?"main":"index";
addRefItem(root->sli,
name,
"page",
name,
title,
0
);
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(buildPageList,rootNav);
}
static void findMainPage(EntryNav *rootNav)
{
if (rootNav->section() == Entry::MAINPAGEDOC_SEC)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
if (Doxygen::mainPage==0)
{
//printf("Found main page! \n======\n%s\n=======\n",root->doc.data());
QCString title=root->args.stripWhiteSpace();
QCString indexName=Config_getBool("GENERATE_TREEVIEW")?"main":"index";
Doxygen::mainPage = new PageDef(root->fileName,root->startLine,
indexName, root->brief+root->doc+root->inbodyDocs,title);
//setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
Doxygen::mainPage->setFileName(indexName);
addPageToContext(Doxygen::mainPage,rootNav);
// a page name is a label as well!
SectionInfo *si=new SectionInfo(
indexName,
Doxygen::mainPage->name(),
Doxygen::mainPage->title(),
SectionInfo::Page);
Doxygen::sectionDict.insert(indexName,si);
Doxygen::mainPage->addSectionsToDefinition(root->anchors);
}
else
{
warn(root->fileName,root->startLine,
"Warning: found more than one \\mainpage comment block! Skipping this "
"block."
);
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(findMainPage,rootNav);
}
static void computePageRelations(EntryNav *rootNav)
{
if ((rootNav->section()==Entry::PAGEDOC_SEC ||
rootNav->section()==Entry::MAINPAGEDOC_SEC
)
&& !rootNav->name().isEmpty()
)
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
PageDef *pd = root->section==Entry::PAGEDOC_SEC ?
Doxygen::pageSDict->find(root->name) :
Doxygen::mainPage;
if (pd)
{
QListIterator<BaseInfo> bii(*root->extends);
BaseInfo *bi;
for (bii.toFirst();(bi=bii.current());++bii)
{
PageDef *subPd = Doxygen::pageSDict->find(bi->name);
if (subPd)
{
pd->addInnerCompound(subPd);
//printf("*** Added subpage relation: %s->%s\n",
// pd->name().data(),subPd->name().data());
}
}
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(computePageRelations,rootNav);
}
static void checkPageRelations()
{
PageSDict::Iterator pdi(*Doxygen::pageSDict);
PageDef *pd=0;
for (pdi.toFirst();(pd=pdi.current());++pdi)
{
Definition *ppd = pd->getOuterScope();
while (ppd)
{
if (ppd==pd)
{
err("Warning: page defined at line %d of file %s with label %s is a subpage "
"of itself! Please remove this cyclic dependency.\n",
pd->docLine(),pd->docFile().data(),pd->name().data());
exit(1);
}
ppd=ppd->getOuterScope();
}
}
}
//----------------------------------------------------------------------------
static void resolveUserReferences()
{
QDictIterator<SectionInfo> sdi(Doxygen::sectionDict);
SectionInfo *si;
for (;(si=sdi.current());++sdi)
{
//printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
// si->label.data(),si->definition?si->definition->name().data():"<none>",
// si->fileName.data());
PageDef *pd=0;
// hack: the items of a todo/test/bug/deprecated list are all fragments from
// different files, so the resulting section's all have the wrong file
// name (not from the todo/test/bug/deprecated list, but from the file in
// which they are defined). We correct this here by looking at the
// generated section labels!
QDictIterator<RefList> rli(*Doxygen::xrefLists);
RefList *rl;
for (rli.toFirst();(rl=rli.current());++rli)
{
QCString label="_"+rl->listName(); // "_todo", "_test", ...
if (si->label.left(label.length())==label)
{
si->fileName=rl->listName();
si->generated=TRUE;
break;
}
}
//printf("start: si->label=%s si->fileName=%s\n",si->label.data(),si->fileName.data());
if (!si->generated)
{
// if this section is in a page and the page is in a group, then we
// have to adjust the link file name to point to the group.
if (!si->fileName.isEmpty() &&
(pd=Doxygen::pageSDict->find(si->fileName)) &&
pd->getGroupDef())
{
si->fileName=pd->getGroupDef()->getOutputFileBase().copy();
}
if (si->definition)
{
// TODO: there should be one function in Definition that returns
// the file to link to, so we can avoid the following tests.
GroupDef *gd=0;
if (si->definition->definitionType()==Definition::TypeMember)
{
gd = ((MemberDef *)si->definition)->getGroupDef();
}
if (gd)
{
si->fileName=gd->getOutputFileBase().copy();
}
else
{
//si->fileName=si->definition->getOutputFileBase().copy();
//printf("Setting si->fileName to %s\n",si->fileName.data());
}
}
}
//printf("end: si->label=%s si->fileName=%s\n",si->label.data(),si->fileName.data());
}
}
//----------------------------------------------------------------------------
// generate all separate documentation pages
static void generatePageDocs()
{
//printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageSDict->count());
if (documentedPages==0) return;
PageSDict::Iterator pdi(*Doxygen::pageSDict);
PageDef *pd=0;
for (pdi.toFirst();(pd=pdi.current());++pdi)
{
if (!pd->getGroupDef() && !pd->isReference())
{
msg("Generating docs for page %s...\n",pd->name().data());
Doxygen::insideMainPage=TRUE;
pd->writeDocumentation(*g_outputList);
Doxygen::insideMainPage=FALSE;
}
}
}
//----------------------------------------------------------------------------
// create a (sorted) list & dictionary of example pages
static void buildExampleList(EntryNav *rootNav)
{
if (rootNav->section()==Entry::EXAMPLE_SEC && !rootNav->name().isEmpty())
{
rootNav->loadEntry(g_storage);
Entry *root = rootNav->entry();
if (Doxygen::exampleSDict->find(root->name))
{
warn(root->fileName,root->startLine,
"Warning: Example %s was already documented. Ignoring "
"documentation found here.",
root->name.data()
);
}
else
{
PageDef *pd=new PageDef(root->fileName,root->startLine,
root->name,root->brief+root->doc+root->inbodyDocs,root->args);
pd->setFileName(convertNameToFile(pd->name()+"-example"));
pd->addSectionsToDefinition(root->anchors);
//pi->addSections(root->anchors);
Doxygen::exampleSDict->inSort(root->name,pd);
//we don't add example to groups
//addExampleToGroups(root,pd);
}
rootNav->releaseEntry();
}
RECURSE_ENTRYTREE(buildExampleList,rootNav);
}
//----------------------------------------------------------------------------
// prints the Entry tree (for debugging)
void printNavTree(EntryNav *rootNav,int indent)
{
QCString indentStr;
indentStr.fill(' ',indent);
msg("%s%s (sec=0x%x)\n",
indentStr.isEmpty()?"":indentStr.data(),
rootNav->name().isEmpty()?"<empty>":rootNav->name().data(),
rootNav->section());
if (rootNav->children())
{
EntryNavListIterator eli(*rootNav->children());
for (;eli.current();++eli) printNavTree(eli.current(),indent+2);
}
}
//----------------------------------------------------------------------------
// generate the example documentation
static void generateExampleDocs()
{
g_outputList->disable(OutputGenerator::Man);
PageSDict::Iterator pdi(*Doxygen::exampleSDict);
PageDef *pd=0;
for (pdi.toFirst();(pd=pdi.current());++pdi)
{
msg("Generating docs for example %s...\n",pd->name().data());
resetCCodeParserState();
QCString n=pd->getOutputFileBase();
startFile(*g_outputList,n,n,pd->name());
startTitle(*g_outputList,n);
g_outputList->docify(pd->name());
endTitle(*g_outputList,n,0);
g_outputList->parseDoc(pd->docFile(), // file
pd->docLine(), // startLine
pd, // context
0, // memberDef
pd->documentation()+"\n\n\\include "+pd->name(), // docs
TRUE, // index words
TRUE, // is example
pd->name()
);
endFile(*g_outputList);
}
g_outputList->enable(OutputGenerator::Man);
}
//----------------------------------------------------------------------------
// generate module pages
static void generateGroupDocs()
{
GroupSDict::Iterator gli(*Doxygen::groupSDict);
GroupDef *gd;
for (gli.toFirst();(gd=gli.current());++gli)
{
if (!gd->isReference())
{
gd->writeDocumentation(*g_outputList);
}
}
}
//----------------------------------------------------------------------------
//static void generatePackageDocs()
//{
// writePackageIndex(*g_outputList);
//
// if (Doxygen::packageDict.count()>0)
// {
// PackageSDict::Iterator pdi(Doxygen::packageDict);
// PackageDef *pd;
// for (pdi.toFirst();(pd=pdi.current());++pdi)
// {
// pd->writeDocumentation(*g_outputList);
// }
// }
//}
//----------------------------------------------------------------------------
// generate module pages
static void generateNamespaceDocs()
{
writeNamespaceIndex(*g_outputList);
NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
NamespaceDef *nd;
// for each namespace...
for (;(nd=nli.current());++nli)
{
if (nd->isLinkableInProject())
{
msg("Generating docs for namespace %s\n",nd->name().data());
nd->writeDocumentation(*g_outputList);
}
// for each class in the namespace...
ClassSDict::Iterator cli(*nd->getClassSDict());
for ( ; cli.current() ; ++cli )
{
ClassDef *cd=cli.current();
if ( ( cd->isLinkableInProject() &&
cd->templateMaster()==0
) // skip external references, anonymous compounds and
// template instances and nested classes
&& !cd->isHidden()
)
{
msg("Generating docs for compound %s...\n",cd->name().data());
cd->writeDocumentation(*g_outputList);
cd->writeMemberList(*g_outputList);
}
cd->writeDocumentationForInnerClasses(*g_outputList);
}
}
}
#if defined(_WIN32)
static QCString fixSlashes(QCString &s)
{
QCString result;
uint i;
for (i=0;i<s.length();i++)
{
switch(s.at(i))
{
case '/':
case '\\':
result+="\\\\";
break;
default:
result+=s.at(i);
}
}
return result;
}
#endif
//----------------------------------------------------------------------------
// generate files for the search engine
//static void generateSearchIndex()
//{
// if (Config_getBool("SEARCHENGINE") && Config_getBool("GENERATE_HTML"))
// {
// // create search index
// QCString fileName;
// writeSearchButton(Config_getString("HTML_OUTPUT"));
//
//#if !defined(_WIN32)
// // create cgi script
// fileName = Config_getString("HTML_OUTPUT")+"/"+Config_getString("CGI_NAME");
// QFile f(fileName);
// if (f.open(IO_WriteOnly))
// {
// QTextStream t(&f);
// t << "#!/bin/sh" << endl
// << "DOXYSEARCH=" << Config_getString("BIN_ABSPATH") << "/doxysearch" << endl
// << "DOXYPATH=\"" << Config_getString("DOC_ABSPATH") << " ";
//
// QStrList &extDocPaths=Config_getList("EXT_DOC_PATHS");
// char *s= extDocPaths.first();
// while (s)
// {
// t << s << " ";
// s=extDocPaths.next();
// }
//
// t << "\"" << endl
// << "if [ -f $DOXYSEARCH ]" << endl
// << "then" << endl
// << " $DOXYSEARCH $DOXYPATH" << endl
// << "else" << endl
// << " echo \"Content-Type: text/html\"" << endl
// << " echo \"\"" << endl
// << " echo \"<h2>Error: $DOXYSEARCH not found. Check cgi script!</h2>\"" << endl
// << "fi" << endl;
//
// f.close();
// struct stat stat_struct;
// stat(fileName,&stat_struct);
// chmod(fileName,stat_struct.st_mode|S_IXUSR|S_IXGRP|S_IXOTH);
// }
// else
// {
// err("Error: Cannot open file %s for writing\n",fileName.data());
// }
//#else /* Windows platform */
// // create cgi program
// fileName = Config_getString("CGI_NAME").copy();
// if (fileName.right(4)==".cgi")
// fileName=fileName.left(fileName.length()-4);
// fileName+=".c";
// fileName.prepend(Config_getString("HTML_OUTPUT")+"/");
// QFile f(fileName);
// if (f.open(IO_WriteOnly))
// {
// QTextStream t(&f);
// t << "#include <stdio.h>" << endl;
// t << "#include <stdlib.h>" << endl;
// t << "#include <process.h>" << endl;
// t << endl;
// t << "const char *DOXYSEARCH = \"" <<
// fixSlashes(Config_getString("BIN_ABSPATH")) << "\\\\doxysearch.exe\";" << endl;
// t << "const char *DOXYPATH = \"" <<
// fixSlashes(Config_getString("DOC_ABSPATH")) << "\";" << endl;
// t << endl;
// t << "int main(void)" << endl;
// t << "{" << endl;
// t << " char buf[1024];" << endl;
// t << " sprintf(buf,\"%s %s\",DOXYSEARCH,DOXYPATH);" << endl;
// t << " if (system(buf))" << endl;
// t << " {" << endl;
// t << " printf(\"Content-Type: text/html\\n\\n\");" << endl;
// t << " printf(\"<h2>Error: failed to execute %s</h2>\\n\",DOXYSEARCH);" << endl;
// t << " exit(1);" << endl;
// t << " }" << endl;
// t << " return 0;" << endl;
// t << "}" << endl;
// f.close();
// }
// else
// {
// err("Error: Cannot open file %s for writing\n",fileName.data());
// }
//#endif /* !defined(_WIN32) */
//
// // create config file
// fileName = Config_getString("HTML_OUTPUT")+"/search.cfg";
// f.setName(fileName);
// if (f.open(IO_WriteOnly))
// {
// QTextStream t(&f);
// t << Config_getString("DOC_URL") << "/" << endl
// << Config_getString("CGI_URL") << "/" << Config_getString("CGI_NAME") << endl;
// f.close();
// }
// else
// {
// err("Error: Cannot open file %s for writing\n",fileName.data());
// }
// //g_outputList->generateExternalIndex();
// g_outputList->pushGeneratorState();
// g_outputList->disableAllBut(OutputGenerator::Html);
// startFile(*g_outputList,"header"+Doxygen::htmlFileExtension,0,"Search Engine",TRUE);
// g_outputList->endPlainFile();
// g_outputList->startPlainFile("footer"+Doxygen::htmlFileExtension);
// endFile(*g_outputList,TRUE);
// g_outputList->popGeneratorState();
// }
//}
//----------------------------------------------------------------------------
static bool openOutputFile(const char *outFile,QFile &f)
{
bool fileOpened=FALSE;
bool writeToStdout=(outFile[0]=='-' && outFile[1]=='\0');
if (writeToStdout) // write to stdout
{
fileOpened = f.open(IO_WriteOnly,stdout);
}
else // write to file
{
QFileInfo fi(outFile);
if (fi.exists()) // create a backup
{
QDir dir=fi.dir();
QFileInfo backup(fi.fileName()+".bak");
if (backup.exists()) // remove existing backup
dir.remove(backup.fileName());
dir.rename(fi.fileName(),fi.fileName()+".bak");
}
f.setName(outFile);
fileOpened = f.open(IO_WriteOnly|IO_Translate);
}
return fileOpened;
}
/*! Generate a template version of the configuration file.
* If the \a shortList parameter is TRUE a configuration file without
* comments will be generated.
*/
static void generateConfigFile(const char *configFile,bool shortList,
bool updateOnly=FALSE)
{
QFile f;
bool fileOpened=openOutputFile(configFile,f);
bool writeToStdout=(configFile[0]=='-' && configFile[1]=='\0');
if (fileOpened)
{
QTextStream t(&f);
t.setEncoding(QTextStream::UnicodeUTF8);
Config::instance()->writeTemplate(t,shortList,updateOnly);
if (!writeToStdout)
{
if (!updateOnly)
{
msg("\n\nConfiguration file `%s' created.\n\n",configFile);
msg("Now edit the configuration file and enter\n\n");
if (strcmp(configFile,"Doxyfile") || strcmp(configFile,"doxyfile"))
msg(" doxygen %s\n\n",configFile);
else
msg(" doxygen\n\n");
msg("to generate the documentation for your project\n\n");
}
else
{
msg("\n\nConfiguration file `%s' updated.\n\n",configFile);
}
}
}
else
{
err("Error: Cannot open file %s for writing\n",configFile);
exit(1);
}
}
//----------------------------------------------------------------------------
// read and parse a tag file
//static bool readLineFromFile(QFile &f,QCString &s)
//{
// char c=0;
// s.resize(0);
// while (!f.atEnd() && (c=f.getch())!='\n') s+=c;
// return f.atEnd();
//}
//----------------------------------------------------------------------------
static void readTagFile(Entry *root,const char *tl)
{
QCString tagLine = tl;
QCString fileName;
QCString destName;
int eqPos = tagLine.find('=');
if (eqPos!=-1) // tag command contains a destination
{
fileName = tagLine.left(eqPos).stripWhiteSpace();
destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
QFileInfo fi(fileName);
Doxygen::tagDestinationDict.insert(fi.fileName(),new QCString(destName));
//printf("insert tagDestination %s->%s\n",fi.fileName().data(),destName.data());
}
else
{
fileName = tagLine;
}
QFileInfo fi(fileName);
if (!fi.exists() || !fi.isFile())
{
err("Error: Tag file `%s' does not exist or is not a file. Skipping it...\n",
fileName.data());
return;
}
if (!destName.isEmpty())
msg("Reading tag file `%s', location `%s'...\n",fileName.data(),destName.data());
else
msg("Reading tag file `%s'...\n",fileName.data());
parseTagFile(root,fi.absFilePath(),fi.fileName());
}
//----------------------------------------------------------------------------
// returns TRUE if the name of the file represented by `fi' matches
// one of the file patterns in the `patList' list.
static bool patternMatch(QFileInfo *fi,QStrList *patList)
{
bool found=FALSE;
if (patList)
{
QCString pattern=patList->first();
while (!pattern.isEmpty() && !found)
{
int i=pattern.find('=');
if (i!=-1) pattern=pattern.left(i); // strip of the extension specific filter name
#if defined(_WIN32) // windows
QRegExp re(pattern,FALSE,TRUE); // case insensitive match
#else // unix
QRegExp re(pattern,TRUE,TRUE); // case sensitive match
#endif
found = found || re.match(fi->fileName())!=-1 ||
re.match(fi->filePath())!=-1 ||
re.match(fi->absFilePath())!=-1;
//printf("Matching `%s' against pattern `%s' found=%d\n",
// fi->fileName().data(),pattern.data(),found);
pattern=patList->next();
}
}
return found;
}
//----------------------------------------------------------------------------
static void copyStyleSheet()
{
QCString &htmlStyleSheet = Config_getString("HTML_STYLESHEET");
if (!htmlStyleSheet.isEmpty())
{
QFile cssf(htmlStyleSheet);
QFileInfo cssfi(htmlStyleSheet);
if (cssf.open(IO_ReadOnly))
{
QCString destFileName = Config_getString("HTML_OUTPUT")+"/"+cssfi.fileName().data();
QFile df(destFileName);
if (df.open(IO_WriteOnly))
{
char *buffer = new char[cssf.size()];
cssf.readBlock(buffer,cssf.size());
df.writeBlock(buffer,cssf.size());
df.flush();
delete[] buffer;
}
else
{
err("Error: could not write to style sheet %s\n",destFileName.data());
}
}
else
{
err("Error: could not open user specified style sheet %s\n",Config_getString("HTML_STYLESHEET").data());
htmlStyleSheet.resize(0); // revert to the default
}
}
}
//! parse the list of input files
static void parseFiles(Entry *root,EntryNav *rootNav)
{
#if 0
void *cd = 0;
QCString inpEncoding = Config_getString("INPUT_ENCODING");
bool needsTranscoding = !inpEncoding.isEmpty();
if (needsTranscoding)
{
if (!(cd = portable_iconv_open("UTF-8", inpEncoding)))
{
err("Error: unsupported character enconding: '%s'",inpEncoding.data());
exit(1);
}
}
#endif
QCString *s=g_inputFiles.first();
while (s)
{
QCString fileName=*s;
QCString extension;
int ei = fileName.findRev('.');
if (ei!=-1) extension=fileName.right(fileName.length()-ei);
ParserInterface *parser = Doxygen::parserManager->getParser(extension);
QFileInfo fi(fileName);
BufStr preBuf(fi.size()+4096);
if (Config_getBool("ENABLE_PREPROCESSING") &&
parser->needsPreprocessing(extension))
{
BufStr inBuf(fi.size()+4096);
msg("Preprocessing %s...\n",s->data());
readInputFile(fileName,inBuf);
preprocessFile(fileName,inBuf,preBuf);
}
else // no preprocessing
{
msg("Reading %s...\n",s->data());
readInputFile(fileName,preBuf);
}
msg("Have input for \"%s\", size=%d bytes.\n", s->data(), preBuf.size());
BufStr convBuf(preBuf.curPos()+1024);
// convert multi-line C++ comments to C style comments
convertCppComments(&preBuf,&convBuf,fileName);
convBuf.addChar('\0');
// use language parse to parse the file
parser->parseInput(fileName,convBuf.data(),root);
// store the Entry tree in a file and create an index to
// navigate/load entries
bool ambig;
FileDef *fd=findFileDef(Doxygen::inputNameDict,fileName,ambig);
ASSERT(fd!=0);
root->createNavigationIndex(rootNav,g_storage,fd);
s=g_inputFiles.next();
}
}
// resolves a path that may include symlinks, if a recursive symlink is
// found an empty string is returned.
static QCString resolveSymlink(QCString path)
{
int sepPos=0;
QFileInfo fi;
QDict<void> nonSymlinks;
QDict<void> known;
QCString result = path;
QCString oldPrefix = "/";
do
{
#ifdef WIN32
// UNC path, skip server and share name
if (sepPos==0 && (result.left(2)=="//" || result.left(2)=="\\\\"))
sepPos = result.find('/',2);
if (sepPos!=-1)
sepPos = result.find('/',sepPos+1);
#else
sepPos = result.find('/',sepPos+1);
#endif
QCString prefix = sepPos==-1 ? result : result.left(sepPos);
if (nonSymlinks.find(prefix)==0)
{
fi.setFile(prefix);
if (fi.isSymLink())
{
QString target = fi.readLink();
if (QFileInfo(target).isRelative())
{
target = QDir::cleanDirPath(oldPrefix+"/"+target.data());
}
if (sepPos!=-1)
{
if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
{
target+='/';
}
target+=result.mid(sepPos);
}
result = QDir::cleanDirPath(target).data();
sepPos = 0;
if (known.find(result)) return QCString(); // recursive symlink!
known.insert(result,(void*)0x8);
}
else
{
nonSymlinks.insert(prefix,(void*)0x8);
}
oldPrefix = prefix;
}
}
while (sepPos!=-1);
return QDir::cleanDirPath(result).data();
}
static QDict<void> g_pathsVisited(1009);
//----------------------------------------------------------------------------
// Read all files matching at least one pattern in `patList' in the
// directory represented by `fi'.
// The directory is read iff the recusiveFlag is set.
// The contents of all files is append to the input string
int readDir(QFileInfo *fi,
FileNameList *fnList,
FileNameDict *fnDict,
StringDict *exclDict,
QStrList *patList,
QStrList *exclPatList,
StringList *resultList,
StringDict *resultDict,
bool errorIfNotExist,
bool recursive,
QDict<void> *killDict
)
{
QString dirName = fi->absFilePath();
if (fi->isSymLink())
{
dirName = resolveSymlink(dirName.data());
if (dirName.isEmpty()) return 0; // recusive symlink
if (g_pathsVisited.find(dirName)) return 0; // already visited path
g_pathsVisited.insert(dirName,(void*)0x8);
}
QDir dir(dirName);
dir.setFilter( QDir::Files | QDir::Dirs | QDir::Hidden );
int totalSize=0;
msg("Searching for files in directory %s\n", fi->absFilePath().data());
//printf("killDict=%p count=%d\n",killDict,killDict->count());
const QFileInfoList *list = dir.entryInfoList();
if (list)
{
QFileInfoListIterator it( *list );
QFileInfo *cfi;
while ((cfi=it.current()))
{
if (exclDict==0 || exclDict->find(cfi->absFilePath())==0)
{ // file should not be excluded
//printf("killDict->find(%s)\n",cfi->absFilePath().data());
if (!cfi->exists() || !cfi->isReadable())
{
if (errorIfNotExist)
{
err("Warning: source %s is not a readable file or directory... skipping.\n",cfi->absFilePath().data());
}
}
else if (cfi->isFile() &&
(!Config_getBool("EXCLUDE_SYMLINKS") || !cfi->isSymLink()) &&
(patList==0 || patternMatch(cfi,patList)) &&
!patternMatch(cfi,exclPatList) &&
(killDict==0 || killDict->find(cfi->absFilePath())==0)
)
{
totalSize+=cfi->size()+cfi->absFilePath().length()+4;
QCString name=convertToQCString(cfi->fileName());
//printf("New file %s\n",name.data());
if (fnDict)
{
FileDef *fd=new FileDef(cfi->dirPath()+"/",name);
FileName *fn=0;
if (!name.isEmpty() && (fn=(*fnDict)[name]))
{
fn->append(fd);
}
else
{
fn = new FileName(cfi->absFilePath(),name);
fn->append(fd);
if (fnList) fnList->inSort(fn);
fnDict->insert(name,fn);
}
}
QCString *rs=0;
if (resultList || resultDict)
{
rs=new QCString(cfi->absFilePath());
}
if (resultList) resultList->append(rs);
if (resultDict) resultDict->insert(cfi->absFilePath(),rs);
if (killDict) killDict->insert(cfi->absFilePath(),(void *)0x8);
}
else if (recursive &&
(!Config_getBool("EXCLUDE_SYMLINKS") || !cfi->isSymLink()) &&
cfi->isDir() && cfi->fileName()!="." &&
!patternMatch(cfi,exclPatList) &&
cfi->fileName()!="..")
{
cfi->setFile(cfi->absFilePath());
totalSize+=readDir(cfi,fnList,fnDict,exclDict,
patList,exclPatList,resultList,resultDict,errorIfNotExist,
recursive,killDict);
}
}
++it;
}
}
return totalSize;
}
//----------------------------------------------------------------------------
// read a file or all files in a directory and append their contents to the
// input string. The names of the files are appended to the `fiList' list.
int readFileOrDirectory(const char *s,
FileNameList *fnList,
FileNameDict *fnDict,
StringDict *exclDict,
QStrList *patList,
QStrList *exclPatList,
StringList *resultList,
StringDict *resultDict,
bool recursive,
bool errorIfNotExist,
QDict<void> *killDict
)
{
//printf("killDict=%p count=%d\n",killDict,killDict->count());
// strip trailing slashes
if (s==0) return 0;
QCString fs = s;
char lc = fs.at(fs.length()-1);
if (lc=='/' || lc=='\\') fs = fs.left(fs.length()-1);
QFileInfo fi(fs);
//printf("readFileOrDirectory(%s)\n",s);
int totalSize=0;
{
if (exclDict==0 || exclDict->find(fi.absFilePath())==0)
{
if (!fi.exists() || !fi.isReadable())
{
if (errorIfNotExist)
{
err("Warning: source %s is not a readable file or directory... skipping.\n",s);
}
}
else if (!Config_getBool("EXCLUDE_SYMLINKS") || !fi.isSymLink())
{
if (fi.isFile())
{
//printf("killDict->find(%s)\n",fi.absFilePath().data());
if (killDict==0 || killDict->find(fi.absFilePath())==0)
{
totalSize+=fi.size()+fi.absFilePath().length()+4; //readFile(&fi,fiList,input);
//fiList->inSort(new FileInfo(fi));
QCString name=convertToQCString(fi.fileName());
//printf("New file %s\n",name.data());
if (fnDict)
{
FileDef *fd=new FileDef(fi.dirPath(TRUE)+"/",name);
FileName *fn=0;
if (!name.isEmpty() && (fn=(*fnDict)[name]))
{
fn->append(fd);
}
else
{
fn = new FileName(fi.absFilePath(),name);
fn->append(fd);
if (fnList) fnList->inSort(fn);
fnDict->insert(name,fn);
}
}
QCString *rs=0;
if (resultList || resultDict)
{
rs=new QCString(fi.absFilePath());
if (resultList) resultList->append(rs);
if (resultDict) resultDict->insert(fi.absFilePath(),rs);
}
if (killDict) killDict->insert(fi.absFilePath(),(void *)0x8);
}
}
else if (fi.isDir()) // readable dir
{
totalSize+=readDir(&fi,fnList,fnDict,exclDict,patList,
exclPatList,resultList,resultDict,errorIfNotExist,
recursive,killDict);
}
}
}
}
return totalSize;
}
//----------------------------------------------------------------------------
void readFormulaRepository()
{
QFile f(Config_getString("HTML_OUTPUT")+"/formula.repository");
if (f.open(IO_ReadOnly)) // open repository
{
msg("Reading formula repository...\n");
QTextStream t(&f);
QCString line;
while (!t.eof())
{
line=t.readLine();
int se=line.find(':'); // find name and text separator.
if (se==-1)
{
err("Warning: formula.repository is corrupted!\n");
break;
}
else
{
QCString formName = line.left(se);
QCString formText = line.right(line.length()-se-1);
Formula *f=new Formula(formText);
Doxygen::formulaList.append(f);
Doxygen::formulaDict.insert(formText,f);
Doxygen::formulaNameDict.insert(formName,f);
}
}
}
}
//----------------------------------------------------------------------------
static void expandAliases()
{
QDictIterator<QCString> adi(Doxygen::aliasDict);
QCString *s;
for (adi.toFirst();(s=adi.current());++adi)
{
*s = expandAlias(adi.currentKey(),*s);
}
}
//----------------------------------------------------------------------------
static void escapeAliases()
{
QDictIterator<QCString> adi(Doxygen::aliasDict);
QCString *s;
for (adi.toFirst();(s=adi.current());++adi)
{
QCString value=*s,newValue;
int in,p=0;
// for each \n in the alias command value
while ((in=value.find("\\n",p))!=-1)
{
newValue+=value.mid(p,in-p);
// expand \n's except if \n is part of a built-in command.
if (value.mid(in,5)!="\\note" &&
value.mid(in,5)!="\\name" &&
value.mid(in,10)!="\\namespace" &&
value.mid(in,14)!="\\nosubgrouping"
)
{
newValue+="\\_linebr ";
}
else
{
newValue+="\\n";
}
p=in+2;
}
newValue+=value.mid(p,value.length()-p);
*s=newValue;
//printf("Alias %s has value %s\n",adi.currentKey().data(),s->data());
}
}
//----------------------------------------------------------------------------
void readAliases()
{
// add aliases to a dictionary
Doxygen::aliasDict.setAutoDelete(TRUE);
QStrList &aliasList = Config_getList("ALIASES");
const char *s=aliasList.first();
while (s)
{
if (Doxygen::aliasDict[s]==0)
{
QCString alias=s;
int i=alias.find('=');
if (i>0)
{
QCString name=alias.left(i).stripWhiteSpace();
QCString value=alias.right(alias.length()-i-1);
//printf("Alias: found name=`%s' value=`%s'\n",name.data(),value.data());
if (!name.isEmpty())
{
QCString *dn=Doxygen::aliasDict[name];
if (dn==0) // insert new alias
{
Doxygen::aliasDict.insert(name,new QCString(value));
}
else // overwrite previous alias
{
*dn=value;
}
}
}
}
s=aliasList.next();
}
expandAliases();
escapeAliases();
}
//----------------------------------------------------------------------------
static void dumpSymbol(QTextStream &t,Definition *d)
{
QCString anchor;
if (d->definitionType()==Definition::TypeMember)
{
MemberDef *md = (MemberDef *)d;
anchor=":"+md->anchor();
}
QCString scope;
if (d->getOuterScope() && d->getOuterScope()!=Doxygen::globalScope)
{
scope = d->getOuterScope()->getOutputFileBase()+Doxygen::htmlFileExtension;
}
t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
<< d->getOutputFileBase()+Doxygen::htmlFileExtension+anchor << "','"
<< scope << "','"
<< d->name() << "','"
<< d->getDefFileName() << "','"
<< d->getDefLine()
<< "');" << endl;
}
static void dumpSymbolMap()
{
QFile f("symbols.sql");
if (f.open(IO_WriteOnly))
{
QTextStream t(&f);
QDictIterator<DefinitionIntf> di(*Doxygen::symbolMap);
DefinitionIntf *intf;
for (;(intf=di.current());++di)
{
if (intf->definitionType()==DefinitionIntf::TypeSymbolList) // list of symbols
{
DefinitionListIterator dli(*(DefinitionList*)intf);
Definition *d;
// for each symbol
for (dli.toFirst();(d=dli.current());++dli)
{
dumpSymbol(t,d);
}
}
else // single symbol
{
Definition *d = (Definition *)intf;
if (d!=Doxygen::globalScope) dumpSymbol(t,d);
}
}
}
}
//----------------------------------------------------------------------------
void dumpConfigAsXML()
{
QFile f("config.xml");
if (f.open(IO_WriteOnly))
{
QTextStream t(&f);
Config::instance()->writeXML(t);
}
}
//----------------------------------------------------------------------------
// print the usage of doxygen
static void usage(const char *name)
{
msg("Doxygen version %s\nCopyright Dimitri van Heesch 1997-2008\n\n",versionString);
msg("You can use doxygen in a number of ways:\n\n");
msg("1) Use doxygen to generate a template configuration file:\n");
msg(" %s [-s] -g [configName]\n\n",name);
msg(" If - is used for configName doxygen will write to standard output.\n\n");
msg("2) Use doxygen to update an old configuration file:\n");
msg(" %s [-s] -u [configName]\n\n",name);
msg("3) Use doxygen to generate documentation using an existing ");
msg("configuration file:\n");
msg(" %s [configName]\n\n",name);
msg(" If - is used for configName doxygen will read from standard input.\n\n");
msg("4) Use doxygen to generate a template file controlling the layout of the\n");
msg(" generated documentation:\n");
msg(" %s -l layoutFileName.xml\n\n",name);
msg("5) Use doxygen to generate a template style sheet file for RTF, HTML or Latex.\n");
msg(" RTF: %s -w rtf styleSheetFile\n",name);
msg(" HTML: %s -w html headerFile footerFile styleSheetFile [configFile]\n",name);
msg(" LaTeX: %s -w latex headerFile styleSheetFile [configFile]\n\n",name);
msg("6) Use doxygen to generate an rtf extensions file\n");
msg(" RTF: %s -e rtf extensionsFile\n\n",name);
msg("If -s is specified the comments in the config file will be omitted.\n");
msg("If configName is omitted `Doxyfile' will be used as a default.\n\n");
exit(1);
}
//----------------------------------------------------------------------------
// read the argument of option `c' from the comment argument list and
// update the option index `optind'.
static const char *getArg(int argc,char **argv,int &optind)
{
char *s=0;
if (strlen(&argv[optind][2])>0)
s=&argv[optind][2];
else if (optind+1<argc && argv[optind+1][0]!='-')
s=argv[++optind];
return s;
}
//----------------------------------------------------------------------------
extern void commentScanTest();
void initDoxygen()
{
setlocale(LC_ALL,"");
setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
setlocale(LC_NUMERIC,"C");
//Doxygen::symbolMap->setAutoDelete(TRUE);
Doxygen::runningTime.start();
initPreprocessor();
Doxygen::parserManager = new ParserManager;
Doxygen::parserManager->registerParser("c", new CLanguageScanner, TRUE);
Doxygen::parserManager->registerParser("python", new PythonLanguageScanner);
Doxygen::parserManager->registerParser("fortran", new FortranLanguageScanner);
Doxygen::parserManager->registerParser("vhdl", new VHDLLanguageScanner);
Doxygen::parserManager->registerParser("dbusxml", new DBusXMLScanner);
// register any additional parsers here...
initDefaultExtensionMapping();
initClassMemberIndices();
initNamespaceMemberIndices();
initFileMemberIndices();
Doxygen::symbolMap = new QDict<DefinitionIntf>(1000);
Doxygen::inputNameList = new FileNameList;
Doxygen::inputNameList->setAutoDelete(TRUE);
Doxygen::memberNameSDict = new MemberNameSDict(10000);
Doxygen::memberNameSDict->setAutoDelete(TRUE);
Doxygen::functionNameSDict = new MemberNameSDict(10000);
Doxygen::functionNameSDict->setAutoDelete(TRUE);
Doxygen::groupSDict = new GroupSDict(17);
Doxygen::groupSDict->setAutoDelete(TRUE);
Doxygen::globalScope = new NamespaceDef("<globalScope>",1,"<globalScope>");
Doxygen::namespaceSDict = new NamespaceSDict(20);
Doxygen::namespaceSDict->setAutoDelete(TRUE);
Doxygen::classSDict = new ClassSDict(1009);
Doxygen::classSDict->setAutoDelete(TRUE);
Doxygen::hiddenClasses = new ClassSDict(257);
Doxygen::hiddenClasses->setAutoDelete(TRUE);
Doxygen::directories = new DirSDict(17);
Doxygen::directories->setAutoDelete(TRUE);
Doxygen::pageSDict = new PageSDict(1009); // all doc pages
Doxygen::pageSDict->setAutoDelete(TRUE);
Doxygen::exampleSDict = new PageSDict(1009); // all examples
Doxygen::exampleSDict->setAutoDelete(TRUE);
Doxygen::inputNameDict = new FileNameDict(10007);
Doxygen::includeNameDict = new FileNameDict(10007);
Doxygen::exampleNameDict = new FileNameDict(1009);
Doxygen::exampleNameDict->setAutoDelete(TRUE);
Doxygen::imageNameDict = new FileNameDict(257);
Doxygen::dotFileNameDict = new FileNameDict(257);
Doxygen::sectionDict.setAutoDelete(TRUE);
Doxygen::memGrpInfoDict.setAutoDelete(TRUE);
Doxygen::tagDestinationDict.setAutoDelete(TRUE);
Doxygen::lookupCache.setAutoDelete(TRUE);
Doxygen::dirRelations.setAutoDelete(TRUE);
}
void cleanUpDoxygen()
{
delete Doxygen::inputNameDict;
delete Doxygen::includeNameDict;
delete Doxygen::exampleNameDict;
delete Doxygen::imageNameDict;
delete Doxygen::dotFileNameDict;
delete Doxygen::mainPage;
delete Doxygen::pageSDict;
delete Doxygen::exampleSDict;
delete Doxygen::globalScope;
delete Doxygen::xrefLists;
delete Doxygen::parserManager;
cleanUpPreprocessor();
delete theTranslator;
delete g_outputList;
Mappers::freeMappers();
codeFreeScanner();
if (Doxygen::symbolMap)
{
// iterate through Doxygen::symbolMap and delete all
// DefinitionList objects, since they have no owner
QDictIterator<DefinitionIntf> dli(*Doxygen::symbolMap);
DefinitionIntf *di;
for (dli.toFirst();(di=dli.current());)
{
if (di->definitionType()==DefinitionIntf::TypeSymbolList)
{
DefinitionIntf *tmp = Doxygen::symbolMap->take(dli.currentKey());
delete (DefinitionList *)tmp;
}
else
{
++dli;
}
}
}
delete Doxygen::inputNameList;
delete Doxygen::memberNameSDict;
delete Doxygen::functionNameSDict;
delete Doxygen::groupSDict;
delete Doxygen::classSDict;
delete Doxygen::hiddenClasses;
delete Doxygen::namespaceSDict;
delete Doxygen::directories;
//delete Doxygen::symbolMap; <- we cannot do this unless all static lists
// (such as Doxygen::namespaceSDict)
// with objects based on Definition are made
// dynamic first
}
void readConfiguration(int argc, char **argv)
{
/**************************************************************************
* Handle arguments *
**************************************************************************/
int optind=1;
const char *configName=0;
const char *layoutName=0;
const char *debugLabel;
const char *formatName;
bool genConfig=FALSE;
bool shortList=FALSE;
bool updateConfig=FALSE;
bool genLayout=FALSE;
while (optind<argc && argv[optind][0]=='-' &&
(isalpha(argv[optind][1]) || argv[optind][1]=='?' ||
argv[optind][1]=='-')
)
{
switch(argv[optind][1])
{
case 'g':
genConfig=TRUE;
configName=getArg(argc,argv,optind);
if (strcmp(argv[optind+1],"-")==0)
{ configName="-"; optind++; }
if (!configName)
{ configName="Doxyfile"; }
break;
case 'l':
genLayout=TRUE;
layoutName=getArg(argc,argv,optind);
if (!layoutName)
{ layoutName="DoxygenLayout.xml"; }
break;
case 'd':
debugLabel=getArg(argc,argv,optind);
Debug::setFlag(debugLabel);
break;
case 's':
shortList=TRUE;
break;
case 'u':
updateConfig=TRUE;
break;
case 'e':
formatName=getArg(argc,argv,optind);
if (!formatName)
{
err("Error:option -e is missing format specifier rtf.\n");
cleanUpDoxygen();
exit(1);
}
if (stricmp(formatName,"rtf")==0)
{
if (optind+1>=argc)
{
err("Error: option \"-e rtf\" is missing an extensions file name\n");
cleanUpDoxygen();
exit(1);
}
QFile f;
if (openOutputFile(argv[optind+1],f))
{
RTFGenerator::writeExtensionsFile(f);
}
cleanUpDoxygen();
exit(1);
}
err("Error: option \"-e\" has invalid format specifier.\n");
cleanUpDoxygen();
exit(1);
break;
case 'w':
formatName=getArg(argc,argv,optind);
if (!formatName)
{
err("Error: option -w is missing format specifier rtf, html or latex\n");
cleanUpDoxygen();
exit(1);
}
if (stricmp(formatName,"rtf")==0)
{
if (optind+1>=argc)
{
err("Error: option \"-w rtf\" is missing a style sheet file name\n");
cleanUpDoxygen();
exit(1);
}
QFile f;
if (openOutputFile(argv[optind+1],f))
{
RTFGenerator::writeStyleSheetFile(f);
}
cleanUpDoxygen();
exit(1);
}
else if (stricmp(formatName,"html")==0)
{
if (optind+4<argc)
{
if (!Config::instance()->parse(argv[optind+4]))
{
err("Error opening or reading configuration file %s!\n",argv[optind+4]);
cleanUpDoxygen();
exit(1);
}
Config::instance()->substituteEnvironmentVars();
Config::instance()->convertStrToVal();
Config::instance()->check();
}
else
{
Config::instance()->init();
}
if (optind+3>=argc)
{
err("Error: option \"-w html\" does not have enough arguments\n");
cleanUpDoxygen();
exit(1);
}
QCString outputLanguage=Config_getEnum("OUTPUT_LANGUAGE");
if (!setTranslator(outputLanguage))
{
err("Warning: Output language %s not supported! Using English instead.\n", outputLanguage.data());
}
QFile f;
if (openOutputFile(argv[optind+1],f))
{
HtmlGenerator::writeHeaderFile(f);
}
f.close();
if (openOutputFile(argv[optind+2],f))
{
HtmlGenerator::writeFooterFile(f);
}
f.close();
if (openOutputFile(argv[optind+3],f))
{
HtmlGenerator::writeStyleSheetFile(f);
}
cleanUpDoxygen();
exit(0);
}
else if (stricmp(formatName,"latex")==0)
{
if (optind+3<argc) // use config file to get settings
{
if (!Config::instance()->parse(argv[optind+3]))
{
err("Error opening or reading configuration file %s!\n",argv[optind+3]);
exit(1);
}
Config::instance()->substituteEnvironmentVars();
Config::instance()->convertStrToVal();
Config::instance()->check();
}
else // use default config
{
Config::instance()->init();
}
if (optind+2>=argc)
{
err("Error: option \"-w latex\" does not have enough arguments\n");
cleanUpDoxygen();
exit(1);
}
QCString outputLanguage=Config_getEnum("OUTPUT_LANGUAGE");
if (!setTranslator(outputLanguage))
{
err("Warning: Output language %s not supported! Using English instead.\n", outputLanguage.data());
}
QFile f;
if (openOutputFile(argv[optind+1],f))
{
LatexGenerator::writeHeaderFile(f);
}
f.close();
if (openOutputFile(argv[optind+2],f))
{
LatexGenerator::writeStyleSheetFile(f);
}
cleanUpDoxygen();
exit(0);
}
else
{
err("Error: Illegal format specifier %s: should be one of rtf, html, or latex\n",formatName);
cleanUpDoxygen();
exit(1);
}
break;
case 'm':
g_dumpSymbolMap = TRUE;
break;
case 'x':
g_dumpConfigAsXML = TRUE;
break;
case '-':
if (strcmp(&argv[optind][2],"help")==0)
{
usage(argv[0]);
}
else if (strcmp(&argv[optind][2],"version")==0)
{
msg("%s\n",versionString);
cleanUpDoxygen();
exit(0);
}
break;
case 'b':
setvbuf(stdout,NULL,_IONBF,0);
Doxygen::outputToWizard=TRUE;
break;
case 'h':
case '?':
usage(argv[0]);
break;
default:
err("Unknown option -%c\n",argv[optind][1]);
usage(argv[0]);
}
optind++;
}
/**************************************************************************
* Parse or generate the config file *
**************************************************************************/
Config::instance()->init();
if (genConfig)
{
if (g_dumpConfigAsXML)
{
checkConfiguration();
generateConfigFile(configName,shortList);
dumpConfigAsXML();
exit(0);
}
else
{
generateConfigFile(configName,shortList);
}
cleanUpDoxygen();
exit(0);
}
if (genLayout)
{
writeDefaultLayoutFile(layoutName);
cleanUpDoxygen();
exit(0);
}
QFileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
if (optind>=argc)
{
if (configFileInfo1.exists())
{
configName="Doxyfile";
}
else if (configFileInfo2.exists())
{
configName="doxyfile";
}
else
{
err("Doxyfile not found and no input file specified!\n");
usage(argv[0]);
}
}
else
{
QFileInfo fi(argv[optind]);
if (fi.exists() || strcmp(argv[optind],"-")==0)
{
configName=argv[optind];
}
else
{
err("Error: configuration file %s not found!\n",argv[optind]);
usage(argv[0]);
}
}
if (!Config::instance()->parse(configName))
{
err("Error: could not open or read configuration file %s!\n",configName);
cleanUpDoxygen();
exit(1);
}
if (updateConfig)
{
generateConfigFile(configName,shortList,TRUE);
cleanUpDoxygen();
exit(0);
}
/* Perlmod wants to know the path to the config file.*/
QFileInfo configFileInfo(configName);
setPerlModDoxyfile(configFileInfo.absFilePath());
}
/** check and resolve config options */
void checkConfiguration()
{
Config::instance()->substituteEnvironmentVars();
Config::instance()->convertStrToVal();
Config::instance()->check();
initWarningFormat();
}
/** adjust globals that depend on configuration settings. */
void adjustConfiguration()
{
QCString outputLanguage=Config_getEnum("OUTPUT_LANGUAGE");
if (!setTranslator(outputLanguage))
{
err("Warning: Output language %s not supported! Using English instead.\n",
outputLanguage.data());
}
QStrList &includePath = Config_getList("INCLUDE_PATH");
char *s=includePath.first();
while (s)
{
QFileInfo fi(s);
addSearchDir(fi.absFilePath());
s=includePath.next();
}
/* Set the global html file extension. */
Doxygen::htmlFileExtension = Config_getString("HTML_FILE_EXTENSION");
Doxygen::xrefLists->setAutoDelete(TRUE);
Doxygen::parseSourcesNeeded = Config_getBool("CALL_GRAPH") ||
Config_getBool("CALLER_GRAPH") ||
Config_getBool("REFERENCES_RELATION") ||
Config_getBool("REFERENCED_BY_RELATION");
/**************************************************************************
* Add custom extension mappings
**************************************************************************/
QStrList &extMaps = Config_getList("EXTENSION_MAPPING");
char *mapping = extMaps.first();
while (mapping)
{
QCString mapStr = mapping;
int i;
if ((i=mapStr.find('='))!=-1)
{
QCString ext=mapStr.left(i).stripWhiteSpace().lower();
QCString language=mapStr.mid(i+1).stripWhiteSpace().lower();
if (!updateLanguageMapping(ext,language))
{
err("Failed to map file extension '%s' to unsupported language '%s'.\n"
"Check the EXTENSION_MAPPING setting in the config file.\n",
ext.data(),language.data());
}
else
{
msg("Adding custom extension mapping: .%s will be treated as language %s\n",
ext.data(),language.data());
}
}
mapping = extMaps.next();
}
// add predefined macro name to a dictionary
QStrList &expandAsDefinedList =Config_getList("EXPAND_AS_DEFINED");
s=expandAsDefinedList.first();
while (s)
{
if (Doxygen::expandAsDefinedDict[s]==0)
{
Doxygen::expandAsDefinedDict.insert(s,(void *)666);
}
s=expandAsDefinedList.next();
}
// read aliases and store them in a dictionary
readAliases();
// store number of spaces in a tab into Doxygen::spaces
int &tabSize = Config_getInt("TAB_SIZE");
Doxygen::spaces.resize(tabSize+1);
int sp;for (sp=0;sp<tabSize;sp++) Doxygen::spaces.at(sp)=' ';
Doxygen::spaces.at(tabSize)='\0';
}
#ifdef HAS_SIGNALS
static void stopDoxygen(int)
{
QDir thisDir;
msg("Cleaning up...\n");
if (!Doxygen::entryDBFileName.isEmpty())
{
thisDir.remove(Doxygen::entryDBFileName);
}
if (!Doxygen::objDBFileName.isEmpty())
{
thisDir.remove(Doxygen::objDBFileName);
}
exit(1);
}
#endif
static void exitDoxygen()
{
if (!g_successfulRun) // premature exit
{
QDir thisDir;
msg("Exiting...\n");
if (!Doxygen::entryDBFileName.isEmpty())
{
thisDir.remove(Doxygen::entryDBFileName);
}
if (!Doxygen::objDBFileName.isEmpty())
{
thisDir.remove(Doxygen::objDBFileName);
}
}
}
static QCString createOutputDirectory(const QCString &baseDirName,
const char *formatDirOption,
const char *defaultDirName)
{
// Note the & on the next line, we modify the formatDirOption!
QCString &formatDirName = Config_getString(formatDirOption);
if (formatDirName.isEmpty())
{
formatDirName = baseDirName + defaultDirName;
}
else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
{
formatDirName.prepend(baseDirName+'/');
}
QDir formatDir(formatDirName);
if (!formatDir.exists() && !formatDir.mkdir(formatDirName))
{
err("Could not create output directory %s\n", formatDirName.data());
cleanUpDoxygen();
exit(1);
}
return formatDirName;
}
static QCString getQchFileName()
{
QCString const & qchFile = Config_getString("QCH_FILE");
if (!qchFile.isEmpty())
{
return qchFile;
}
QCString const & projectName = Config_getString("PROJECT_NAME");
QCString const & versionText = Config_getString("PROJECT_NUMBER");
return QCString("../qch/")
+ (projectName.isEmpty() ? QCString("index") : projectName)
+ (versionText.isEmpty() ? QCString("") : QCString("-") + versionText)
+ QCString(".qch");
}
void searchInputFiles(StringList &inputFiles)
{
QStrList &exclPatterns = Config_getList("EXCLUDE_PATTERNS");
bool alwaysRecursive = Config_getBool("RECURSIVE");
StringDict excludeNameDict(1009);
excludeNameDict.setAutoDelete(TRUE);
// gather names of all files in the include path
msg("Searching for include files...\n");
QStrList &includePathList = Config_getList("INCLUDE_PATH");
char *s=includePathList.first();
while (s)
{
QStrList &pl = Config_getList("INCLUDE_FILE_PATTERNS");
if (pl.count()==0)
{
pl = Config_getList("FILE_PATTERNS");
}
readFileOrDirectory(s,0,Doxygen::includeNameDict,0,&pl,
&exclPatterns,0,0,
alwaysRecursive);
s=includePathList.next();
}
msg("Searching for example files...\n");
QStrList &examplePathList = Config_getList("EXAMPLE_PATH");
s=examplePathList.first();
while (s)
{
readFileOrDirectory(s,0,Doxygen::exampleNameDict,0,
&Config_getList("EXAMPLE_PATTERNS"),
0,0,0,
(alwaysRecursive || Config_getBool("EXAMPLE_RECURSIVE")));
s=examplePathList.next();
}
msg("Searching for images...\n");
QStrList &imagePathList=Config_getList("IMAGE_PATH");
s=imagePathList.first();
while (s)
{
readFileOrDirectory(s,0,Doxygen::imageNameDict,0,0,
0,0,0,
alwaysRecursive);
s=imagePathList.next();
}
msg("Searching for dot files...\n");
QStrList &dotFileList=Config_getList("DOTFILE_DIRS");
s=dotFileList.first();
while (s)
{
readFileOrDirectory(s,0,Doxygen::dotFileNameDict,0,0,
0,0,0,
alwaysRecursive);
s=dotFileList.next();
}
msg("Searching for files to exclude\n");
QStrList &excludeList = Config_getList("EXCLUDE");
s=excludeList.first();
while (s)
{
readFileOrDirectory(s,0,0,0,&Config_getList("FILE_PATTERNS"),
0,0,&excludeNameDict,
alwaysRecursive,
FALSE);
s=excludeList.next();
}
/**************************************************************************
* Determine Input Files *
**************************************************************************/
msg("Searching for files to process...\n");
QDict<void> *killDict = new QDict<void>(10007);
int inputSize=0;
QStrList &inputList=Config_getList("INPUT");
inputFiles.setAutoDelete(TRUE);
s=inputList.first();
while (s)
{
QCString path=s;
uint l = path.length();
// strip trailing slashes
if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
inputSize+=readFileOrDirectory(
path,
Doxygen::inputNameList,
Doxygen::inputNameDict,
&excludeNameDict,
&Config_getList("FILE_PATTERNS"),
&exclPatterns,
&inputFiles,0,
alwaysRecursive,
TRUE,
killDict);
s=inputList.next();
}
delete killDict;
}
void parseInput()
{
atexit(exitDoxygen);
/**************************************************************************
* Make sure the output directory exists
**************************************************************************/
QCString &outputDirectory = Config_getString("OUTPUT_DIRECTORY");
if (outputDirectory.isEmpty())
{
outputDirectory=QDir::currentDirPath();
}
else
{
QDir dir(outputDirectory);
if (!dir.exists())
{
dir.setPath(QDir::currentDirPath());
if (!dir.mkdir(outputDirectory))
{
err("Error: tag OUTPUT_DIRECTORY: Output directory `%s' does not "
"exist and cannot be created\n",outputDirectory.data());
cleanUpDoxygen();
exit(1);
}
else if (!Config_getBool("QUIET"))
{
err("Notice: Output directory `%s' does not exist. "
"I have created it for you.\n", outputDirectory.data());
}
dir.cd(outputDirectory);
}
outputDirectory=dir.absPath();
}
/**************************************************************************
* Initialize global lists and dictionaries
**************************************************************************/
int cacheSize = Config_getInt("SYMBOL_CACHE_SIZE");
if (cacheSize<0) cacheSize=0;
if (cacheSize>9) cacheSize=9;
Doxygen::symbolCache = new ObjCache(16+cacheSize); // 16 -> room for 65536 elements,
// ~2.0 MByte "overhead"
Doxygen::symbolStorage = new Store;
#ifdef HAS_SIGNALS
signal(SIGINT, stopDoxygen);
#endif
uint pid = portable_pid();
Doxygen::objDBFileName.sprintf("doxygen_objdb_%d.tmp",pid);
Doxygen::objDBFileName.prepend(outputDirectory+"/");
Doxygen::entryDBFileName.sprintf("doxygen_entrydb_%d.tmp",pid);
Doxygen::entryDBFileName.prepend(outputDirectory+"/");
if (Doxygen::symbolStorage->open(Doxygen::objDBFileName)==-1)
{
err("Failed to open temporary file %s\n",Doxygen::objDBFileName.data());
exit(1);
}
/**************************************************************************
* Initialize some global constants
**************************************************************************/
g_compoundKeywordDict.insert("template class",(void *)8);
g_compoundKeywordDict.insert("template struct",(void *)8);
g_compoundKeywordDict.insert("class",(void *)8);
g_compoundKeywordDict.insert("struct",(void *)8);
g_compoundKeywordDict.insert("union",(void *)8);
g_compoundKeywordDict.insert("interface",(void *)8);
g_compoundKeywordDict.insert("exception",(void *)8);
/**************************************************************************
* Check/create output directorties *
**************************************************************************/
QCString htmlOutput;
bool &generateHtml = Config_getBool("GENERATE_HTML");
if (generateHtml)
htmlOutput = createOutputDirectory(outputDirectory,"HTML_OUTPUT","/html");
QCString xmlOutput;
bool &generateXml = Config_getBool("GENERATE_XML");
if (generateXml)
xmlOutput = createOutputDirectory(outputDirectory,"XML_OUTPUT","/xml");
QCString xmlDitaOutput;
bool &generateXmlDita = Config_getBool("GENERATE_XML_DITA");
if (generateXmlDita)
xmlDitaOutput = createOutputDirectory(outputDirectory,"XML_DITA_OUTPUT","/dita");
QCString latexOutput;
bool &generateLatex = Config_getBool("GENERATE_LATEX");
if (generateLatex)
latexOutput = createOutputDirectory(outputDirectory,"LATEX_OUTPUT","/latex");
QCString rtfOutput;
bool &generateRtf = Config_getBool("GENERATE_RTF");
if (generateRtf)
rtfOutput = createOutputDirectory(outputDirectory,"RTF_OUTPUT","/rtf");
QCString manOutput;
bool &generateMan = Config_getBool("GENERATE_MAN");
if (generateMan)
manOutput = createOutputDirectory(outputDirectory,"MAN_OUTPUT","/man");
if (Config_getBool("HAVE_DOT"))
{
QCString curFontPath = Config_getString("DOT_FONTPATH");
if (curFontPath.isEmpty())
{
portable_getenv("DOTFONTPATH");
QCString newFontPath = ".";
if (!curFontPath.isEmpty())
{
newFontPath+=portable_pathListSeparator();
newFontPath+=curFontPath;
}
portable_setenv("DOTFONTPATH",newFontPath);
}
else
{
portable_setenv("DOTFONTPATH",curFontPath);
}
}
/**************************************************************************
* Handle layout file *
**************************************************************************/
LayoutDocManager::instance().init();
QCString layoutFileName = Config_getString("LAYOUT_FILE");
bool defaultLayoutUsed = FALSE;
if (layoutFileName.isEmpty())
{
layoutFileName = "DoxygenLayout.xml";
defaultLayoutUsed = TRUE;
}
QFile layoutFile(layoutFileName);
if (layoutFile.open(IO_ReadOnly))
{
msg("Parsing layout file %s...\n",layoutFileName.data());
QTextStream t(&layoutFile);
LayoutDocManager::instance().parse(t);
}
else if (!defaultLayoutUsed)
{
err("Warning: failed to open layout file '%s' for reading!\n",layoutFileName.data());
}
/**************************************************************************
* Read and preprocess input *
**************************************************************************/
// prevent search in the output directories
QStrList &exclPatterns = Config_getList("EXCLUDE_PATTERNS");
if (generateHtml) exclPatterns.append(htmlOutput);
if (generateXml) exclPatterns.append(xmlOutput);
if (generateXmlDita) exclPatterns.append(xmlDitaOutput);
if (generateLatex) exclPatterns.append(latexOutput);
if (generateRtf) exclPatterns.append(rtfOutput);
if (generateMan) exclPatterns.append(manOutput);
searchInputFiles(g_inputFiles);
// Notice: the order of the function calls below is very important!
if (Config_getBool("GENERATE_HTML"))
{
readFormulaRepository();
}
/**************************************************************************
* Handle Tag Files *
**************************************************************************/
g_storage = new FileStorage;
g_storage->setName(Doxygen::entryDBFileName);
if (!g_storage->open(IO_WriteOnly))
{
err("Failed to create temporary storage file %s\n",
Doxygen::entryDBFileName.data());
exit(1);
}
Entry *root=new Entry;
EntryNav *rootNav = new EntryNav(0,root);
rootNav->setEntry(root);
msg("Reading and parsing tag files\n");
QStrList &tagFileList = Config_getList("TAGFILES");
char *s=tagFileList.first();
while (s)
{
readTagFile(root,s);
root->createNavigationIndex(rootNav,g_storage,0);
s=tagFileList.next();
}
/**************************************************************************
* Parse source files *
**************************************************************************/
parseFiles(root,rootNav);
g_storage->close();
if (!g_storage->open(IO_ReadOnly))
{
err("Failed to open temporary storage file %s for reading",
Doxygen::entryDBFileName.data());
exit(1);
}
//printNavTree(rootNav,0);
// we are done with input scanning now, so free up the buffers used by flex
// (can be around 4MB)
preFreeScanner();
scanFreeScanner();
pyscanFreeScanner();
//delete rootNav;
//g_storage.close();
//exit(1);
/**************************************************************************
* Gather information *
**************************************************************************/
msg("Building group list...\n");
buildGroupList(rootNav);
organizeSubGroups(rootNav);
msg("Building directory list...\n");
buildDirectories();
findDirDocumentation(rootNav);
if (Config_getBool("BUILTIN_STL_SUPPORT"))
{
addSTLClasses(rootNav);
}
msg("Building namespace list...\n");
buildNamespaceList(rootNav);
findUsingDirectives(rootNav);
msg("Building file list...\n");
buildFileList(rootNav);
//generateFileTree();
msg("Building class list...\n");
buildClassList(rootNav);
msg("Associating documentation with classes...\n");
buildClassDocList(rootNav);
// build list of using declarations here (global list)
buildListOfUsingDecls(rootNav);
msg("Computing nesting relations for classes...\n");
resolveClassNestingRelations();
// calling buildClassList may result in cached relations that
// become invalid after resolveClassNestingRelations(), that's why
// we need to clear the cache here
Doxygen::lookupCache.clear();
// we don't need the list of using declaration anymore
g_usingDeclarations.clear();
msg("Building example list...\n");
buildExampleList(rootNav);
msg("Searching for enumerations...\n");
findEnums(rootNav);
// Since buildVarList calls isVarWithConstructor
// and this calls getResolvedClass we need to process
// typedefs first so the relations between classes via typedefs
// are properly resolved. See bug 536385 for an example.
msg("Searching for documented typedefs...\n");
buildTypedefList(rootNav);
msg("Searching for members imported via using declarations...\n");
findUsingDeclImports(rootNav);
// this should be after buildTypedefList in order to properly import
// used typedefs
findUsingDeclarations(rootNav);
msg("Searching for included using directives...\n");
findIncludedUsingDirectives();
msg("Searching for documented variables...\n");
buildVarList(rootNav);
msg("Building member list...\n"); // using class info only !
buildFunctionList(rootNav);
msg("Searching for friends...\n");
findFriends();
msg("Searching for documented defines...\n");
findDefineDocumentation(rootNav);
findClassEntries(rootNav);
msg("Computing class inheritance relations...\n");
findInheritedTemplateInstances();
msg("Computing class usage relations...\n");
findUsedTemplateInstances();
msg("Flushing cached template relations that have become invalid...\n");
flushCachedTemplateRelations();
msg("Creating members for template instances...\n");
createTemplateInstanceMembers();
msg("Computing class relations...\n");
computeTemplateClassRelations();
flushUnresolvedRelations();
if (Config_getBool("OPTIMIZE_OUTPUT_VHDL"))
{
VhdlDocGen::computeVhdlComponentRelations();
}
else
{
computeClassRelations();
}
g_classEntries.clear();
msg("Add enum values to enums...\n");
addEnumValuesToEnums(rootNav);
findEnumDocumentation(rootNav);
msg("Searching for member function documentation...\n");
findObjCMethodDefinitions(rootNav);
findMemberDocumentation(rootNav); // may introduce new members !
transferRelatedFunctionDocumentation();
transferFunctionDocumentation();
msg("Building page list...\n");
buildPageList(rootNav);
msg("Search for main page...\n");
findMainPage(rootNav);
msg("Computing page relations...\n");
computePageRelations(rootNav);
checkPageRelations();
msg("Determining the scope of groups...\n");
findGroupScope(rootNav);
msg("Sorting lists...\n");
Doxygen::memberNameSDict->sort();
Doxygen::functionNameSDict->sort();
Doxygen::hiddenClasses->sort();
Doxygen::classSDict->sort();
msg("Freeing entry tree\n");
delete rootNav;
g_storage->close();
delete g_storage;
g_storage=0;
QDir thisDir;
thisDir.remove(Doxygen::entryDBFileName);
msg("Determining which enums are documented\n");
findDocumentedEnumValues();
msg("Computing member relations...\n");
computeMemberRelations();
msg("Building full member lists recursively...\n");
buildCompleteMemberLists();
msg("Adding members to member groups.\n");
addMembersToMemberGroup();
if (Config_getBool("DISTRIBUTE_GROUP_DOC"))
{
msg("Distributing member group documentation.\n");
distributeMemberGroupDocumentation();
}
msg("Computing member references...\n");
computeMemberReferences();
if (Config_getBool("INHERIT_DOCS"))
{
msg("Inheriting documentation...\n");
inheritDocumentation();
}
// compute the shortest possible names of all files
// without loosing the uniqueness of the file names.
msg("Generating disk names...\n");
Doxygen::inputNameList->generateDiskNames();
msg("Adding source references...\n");
addSourceReferences();
msg("Adding xrefitems...\n");
addListReferences();
generateXRefPages();
if (Config_getBool("SHOW_DIRECTORIES") && Config_getBool("DIRECTORY_GRAPH"))
{
msg("Computing dependencies between directories...\n");
computeDirDependencies();
}
msg("Counting data structures...\n");
countDataStructures();
msg("Resolving user defined references...\n");
resolveUserReferences();
msg("Finding anchors and sections in the documentation...\n");
findSectionsInDocumentation();
transferFunctionReferences();
msg("Combining using relations...\n");
combineUsingRelations();
msg("Adding members to index pages...\n");
addMembersToIndex();
}
void generateOutput()
{
/**************************************************************************
* Initialize output generators *
**************************************************************************/
//// dump all symbols
//SDict<DefinitionList>::Iterator sdi(Doxygen::symbolMap);
//DefinitionList *dl;
//for (sdi.toFirst();(dl=sdi.current());++sdi)
//{
// DefinitionListIterator dli(*dl);
// Definition *d;
// printf("Symbol: ");
// for (dli.toFirst();(d=dli.current());++dli)
// {
// printf("%s ",d->qualifiedName().data());
// }
// printf("\n");
//}
if (g_dumpSymbolMap)
{
dumpSymbolMap();
exit(0);
}
initDocParser();
g_outputList = new OutputList(TRUE);
if (Config_getBool("GENERATE_HTML"))
{
g_outputList->add(new HtmlGenerator);
HtmlGenerator::init();
#if 0
if (Config_getBool("GENERATE_INDEXLOG")) Doxygen::indexList.addIndex(new IndexLog);
#endif
bool generateHtmlHelp = Config_getBool("GENERATE_HTMLHELP");
bool generateEclipseHelp = Config_getBool("GENERATE_ECLIPSEHELP");
bool generateQhp = Config_getBool("GENERATE_QHP");
bool generateTreeView = Config_getBool("GENERATE_TREEVIEW");
bool generateDocSet = Config_getBool("GENERATE_DOCSET");
if (generateEclipseHelp) Doxygen::indexList.addIndex(new EclipseHelp);
if (generateHtmlHelp) Doxygen::indexList.addIndex(new HtmlHelp);
if (generateQhp) Doxygen::indexList.addIndex(new Qhp);
if (generateTreeView) Doxygen::indexList.addIndex(new FTVHelp);
if (generateDocSet) Doxygen::indexList.addIndex(new DocSets);
Doxygen::indexList.initialize();
Doxygen::indexList.addImageFile("tab_r.gif");
Doxygen::indexList.addImageFile("tab_l.gif");
Doxygen::indexList.addImageFile("tab_b.gif");
Doxygen::indexList.addStyleSheetFile("tabs.css");
Doxygen::indexList.addImageFile("doxygen.png");
if (Config_getBool("HTML_DYNAMIC_SECTIONS")) HtmlGenerator::generateSectionImages();
copyStyleSheet();
}
if (Config_getBool("GENERATE_LATEX"))
{
g_outputList->add(new LatexGenerator);
LatexGenerator::init();
}
if (Config_getBool("GENERATE_MAN"))
{
g_outputList->add(new ManGenerator);
ManGenerator::init();
}
if (Config_getBool("GENERATE_RTF"))
{
g_outputList->add(new RTFGenerator);
RTFGenerator::init();
}
if (Config_getBool("USE_HTAGS"))
{
Htags::useHtags = TRUE;
QCString htmldir = Config_getString("HTML_OUTPUT");
if (!Htags::execute(htmldir))
err("Error: USE_HTAGS is YES but htags(1) failed. \n");
if (!Htags::loadFilemap(htmldir))
err("Error: htags(1) ended normally but failed to load the filemap. \n");
}
/**************************************************************************
* Generate documentation *
**************************************************************************/
QFile *tag=0;
QCString &generateTagFile = Config_getString("GENERATE_TAGFILE");
if (!generateTagFile.isEmpty())
{
tag=new QFile(generateTagFile);
if (!tag->open(IO_WriteOnly))
{
err("Error: cannot open tag file %s for writing\n",
generateTagFile.data()
);
cleanUpDoxygen();
exit(1);
}
Doxygen::tagFile.setDevice(tag);
Doxygen::tagFile.setEncoding(QTextStream::UnicodeUTF8);
Doxygen::tagFile << "<?xml version='1.0' encoding='ISO-8859-1' standalone='yes' ?>" << endl;
Doxygen::tagFile << "<tagfile>" << endl;
}
if (Config_getBool("GENERATE_HTML")) writeDoxFont(Config_getString("HTML_OUTPUT"));
if (Config_getBool("GENERATE_LATEX")) writeDoxFont(Config_getString("LATEX_OUTPUT"));
if (Config_getBool("GENERATE_RTF")) writeDoxFont(Config_getString("RTF_OUTPUT"));
msg("Generating style sheet...\n");
//printf("writing style info\n");
QCString genString =
theTranslator->trGeneratedAt(dateToString(TRUE),Config_getString("PROJECT_NAME"));
g_outputList->writeStyleInfo(0); // write first part
g_outputList->disableAllBut(OutputGenerator::Latex);
g_outputList->parseText(genString);
g_outputList->writeStyleInfo(1); // write second part
//parseText(*g_outputList,theTranslator->trWrittenBy());
g_outputList->writeStyleInfo(2); // write third part
g_outputList->parseText(genString);
g_outputList->writeStyleInfo(3); // write fourth part
//parseText(*g_outputList,theTranslator->trWrittenBy());
g_outputList->writeStyleInfo(4); // write last part
g_outputList->enableAll();
static bool searchEngine = Config_getBool("SEARCHENGINE");
static bool serverBasedSearch = Config_getBool("SERVER_BASED_SEARCH");
// generate search indices (need to do this before writing other HTML
// pages as these contain a drop down menu with options depending on
// what categories we find in this function.
if (searchEngine)
{
QCString searchDirName = Config_getString("HTML_OUTPUT")+"/search";
QDir searchDir(searchDirName);
if (!searchDir.exists() && !searchDir.mkdir(searchDirName))
{
err("Could not create search results directory '%s/search'\n",searchDirName.data());
return;
}
HtmlGenerator::writeSearchData(searchDirName);
writeSearchStyleSheet();
if (serverBasedSearch)
{
}
else
{
writeJavascriptSearchIndex();
}
}
//statistics();
// count the number of documented elements in the lists we have built.
// If the result is 0 we do not generate the lists and omit the
// corresponding links in the index.
msg("Generating index page...\n");
writeIndex(*g_outputList);
msg("Generating page index...\n");
writePageIndex(*g_outputList);
msg("Generating example documentation...\n");
generateExampleDocs();
msg("Generating file sources...\n");
if (!Htags::useHtags)
{
generateFileSources();
}
msg("Generating file documentation...\n");
generateFileDocs();
msg("Generating page documentation...\n");
generatePageDocs();
msg("Generating group documentation...\n");
generateGroupDocs();
msg("Generating group index...\n");
writeGroupIndex(*g_outputList);
msg("Generating class documentation...\n");
generateClassDocs();
if (Config_getBool("HAVE_DOT") && Config_getBool("GRAPHICAL_HIERARCHY"))
{
msg("Generating graphical class hierarchy...\n");
writeGraphicalClassHierarchy(*g_outputList);
}
msg("Generating namespace index...\n");
generateNamespaceDocs();
msg("Generating namespace member index...\n");
writeNamespaceMemberIndex(*g_outputList);
if (Config_getBool("GENERATE_LEGEND"))
{
msg("Generating graph info page...\n");
writeGraphInfo(*g_outputList);
}
if (Config_getBool("SHOW_DIRECTORIES"))
{
msg("Generating directory documentation...\n");
generateDirDocs(*g_outputList);
}
msg("Generating file index...\n");
writeFileIndex(*g_outputList);
if (Config_getBool("SHOW_DIRECTORIES"))
{
msg("Generating directory index...\n");
writeDirIndex(*g_outputList);
}
msg("Generating example index...\n");
writeExampleIndex(*g_outputList);
msg("Generating file member index...\n");
writeFileMemberIndex(*g_outputList);
//writeDirDependencyGraph(Config_getString("HTML_OUTPUT"));
if (Config_getBool("GENERATE_RTF"))
{
msg("Combining RTF output...\n");
if (!RTFGenerator::preProcessFileInplace(Config_getString("RTF_OUTPUT"),"refman.rtf"))
{
err("An error occurred during post-processing the RTF files!\n");
}
}
if (Doxygen::formulaList.count()>0 && Config_getBool("GENERATE_HTML"))
{
msg("Generating bitmaps for formulas in HTML...\n");
Doxygen::formulaList.generateBitmaps(Config_getString("HTML_OUTPUT"));
}
//if (Config_getBool("GENERATE_HTML") && Config_getBool("GENERATE_HTMLHELP"))
//{
// HtmlHelp::getInstance()->finalize();
//}
//if (Config_getBool("GENERATE_HTML") && Config_getBool("GENERATE_TREEVIEW"))
//{
// FTVHelp::getInstance()->finalize();
//}
Doxygen::indexList.finalize();
if (!Config_getString("GENERATE_TAGFILE").isEmpty())
{
Doxygen::tagFile << "</tagfile>" << endl;
delete tag;
}
if (Config_getBool("DOT_CLEANUP"))
{
if (Config_getBool("GENERATE_HTML"))
removeDoxFont(Config_getString("HTML_OUTPUT"));
if (Config_getBool("GENERATE_RTF"))
removeDoxFont(Config_getString("RTF_OUTPUT"));
if (Config_getBool("GENERATE_LATEX"))
removeDoxFont(Config_getString("LATEX_OUTPUT"));
}
if (Config_getBool("GENERATE_XML"))
{
msg("Generating XML output...\n");
generateXML();
}
if (Config_getBool("GENERATE_XML_DITA"))
{
msg("Generating XML DITA output...\n");
generateXMLDITA();
}
if (Config_getBool("GENERATE_AUTOGEN_DEF"))
{
msg("Generating AutoGen DEF output...\n");
generateDEF();
}
if (Config_getBool("GENERATE_PERLMOD"))
{
msg("Generating Perl module output...\n");
generatePerlMod();
}
if (Config_getBool("GENERATE_HTML") &&
Config_getBool("GENERATE_HTMLHELP") &&
!Config_getString("HHC_LOCATION").isEmpty())
{
msg("Running html help compiler...\n");
QString oldDir = QDir::currentDirPath();
QDir::setCurrent(Config_getString("HTML_OUTPUT"));
if (portable_system(Config_getString("HHC_LOCATION"), "index.hhp", FALSE))
{
err("Error: failed to run html help compiler on index.hhp\n");
}
QDir::setCurrent(oldDir);
}
if ( Config_getBool("GENERATE_HTML") &&
Config_getBool("GENERATE_QHP") &&
!Config_getString("QHG_LOCATION").isEmpty())
{
msg("Running qhelpgenerator...\n");
QCString const qhpFileName = Qhp::getQhpFileName();
QCString const qchFileName = getQchFileName();
QCString const args = QCString().sprintf("%s -o \"%s\"", qhpFileName.data(), qchFileName.data());
QString const oldDir = QDir::currentDirPath();
QDir::setCurrent(Config_getString("HTML_OUTPUT"));
if (portable_system(Config_getString("QHG_LOCATION"), args.data(), FALSE))
{
err("Error: failed to run qhelpgenerator on index.qhp\n");
}
QDir::setCurrent(oldDir);
}
if (Config_getBool("GENERATE_HTML") && searchEngine && serverBasedSearch)
{
msg("Generating search index\n");
HtmlGenerator::writeSearchPage();
Doxygen::searchIndex->write(Config_getString("HTML_OUTPUT")+"/search/search.idx");
}
if (Debug::isFlagSet(Debug::Time))
{
msg("Total elapsed time: %.3f seconds\n(of which %.3f seconds waiting for external tools to finish)\n",
((double)Doxygen::runningTime.elapsed())/1000.0,
portable_getSysElapsedTime()
);
}
/**************************************************************************
* Start cleaning up *
**************************************************************************/
//Doxygen::symbolCache->printStats();
//Doxygen::symbolStorage->printStats();
cleanUpDoxygen();
finializeDocParser();
Doxygen::symbolStorage->close();
QDir thisDir;
thisDir.remove(Doxygen::objDBFileName);
Config::deleteInstance();
QTextCodec::deleteAllCodecs();
delete Doxygen::symbolCache;
delete Doxygen::symbolMap;
delete Doxygen::symbolStorage;
g_successfulRun=TRUE;
msg("generateOutput() exiting.\n");
}