/****************************************************************************** * * * * * Copyright (C) 1997-2015 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 "tagreader.h" #include #include #include #include #include #include #include #include #include #include #include #include "entry.h" #include "classdef.h" #include "doxygen.h" #include "util.h" #include "message.h" #include "defargs.h" #include "arguments.h" #include "filedef.h" #include "filename.h" #include "section.h" #include "groupdef.h" /** Information about an linkable anchor */ class TagAnchorInfo { public: TagAnchorInfo(const QCString &f, const QCString &l, const QCString &t=QCString()) : label(l), fileName(f), title(t) {} QCString label; QCString fileName; QCString title; }; /** List of TagAnchorInfo objects. */ class TagAnchorInfoList : public QList { public: TagAnchorInfoList() : QList() { setAutoDelete(TRUE); } virtual ~TagAnchorInfoList() {} }; /** Container for enum values that are scoped within an enum */ class TagEnumValueInfo { public: QCString name; QCString file; QCString anchor; QCString clangid; }; /** Container for member specific info that can be read from a tagfile */ class TagMemberInfo { public: TagMemberInfo() : prot(Public), virt(Normal), isStatic(FALSE) { enumValues.setAutoDelete(TRUE); } QCString type; QCString name; QCString anchorFile; QCString anchor; QCString arglist; QCString kind; QCString clangId; TagAnchorInfoList docAnchors; Protection prot; Specifier virt; bool isStatic; QList enumValues; }; /** Container for class specific info that can be read from a tagfile */ class TagClassInfo { public: enum Kind { None=-1, Class, Struct, Union, Interface, Exception, Protocol, Category, Enum, Service, Singleton }; TagClassInfo() { templateArguments=0; members.setAutoDelete(TRUE); isObjC=FALSE; kind = None; } ~TagClassInfo() { delete templateArguments; } QCString name; QCString filename; QCString clangId; QCString anchor; TagAnchorInfoList docAnchors; std::vector bases; QList members; QList *templateArguments; QCStringList classList; Kind kind; bool isObjC; }; /** Container for namespace specific info that can be read from a tagfile */ class TagNamespaceInfo { public: TagNamespaceInfo() { members.setAutoDelete(TRUE); } QCString name; QCString filename; QCString clangId; QCStringList classList; QCStringList namespaceList; TagAnchorInfoList docAnchors; QList members; }; /** Container for package specific info that can be read from a tagfile */ class TagPackageInfo { public: TagPackageInfo() { members.setAutoDelete(TRUE); } QCString name; QCString filename; TagAnchorInfoList docAnchors; QList members; QCStringList classList; }; /** Container for include info that can be read from a tagfile */ class TagIncludeInfo { public: QCString id; QCString name; QCString text; bool isLocal; bool isImported; }; /** Container for file specific info that can be read from a tagfile */ class TagFileInfo { public: TagFileInfo() { members.setAutoDelete(TRUE); includes.setAutoDelete(TRUE); } QCString name; QCString path; QCString filename; TagAnchorInfoList docAnchors; QList members; QCStringList classList; QCStringList namespaceList; QList includes; }; /** Container for group specific info that can be read from a tagfile */ class TagGroupInfo { public: TagGroupInfo() { members.setAutoDelete(TRUE); } QCString name; QCString title; QCString filename; TagAnchorInfoList docAnchors; QList members; QCStringList subgroupList; QCStringList classList; QCStringList namespaceList; QCStringList fileList; QCStringList pageList; QCStringList dirList; }; /** Container for page specific info that can be read from a tagfile */ class TagPageInfo { public: QCString name; QCString title; QCString filename; TagAnchorInfoList docAnchors; }; /** Container for directory specific info that can be read from a tagfile */ class TagDirInfo { public: QCString name; QCString filename; QCString path; QCStringList subdirList; QCStringList fileList; TagAnchorInfoList docAnchors; }; /** Tag file parser. * * Reads an XML-structured tagfile and builds up the structure in * memory. The method buildLists() is used to transfer/translate * the structures to the doxygen engine. */ class TagFileParser : public QXmlDefaultHandler { enum State { Invalid, InClass, InFile, InNamespace, InGroup, InPage, InMember, InEnumValue, InPackage, InDir, InTempArgList }; class StartElementHandler { typedef void (TagFileParser::*Handler)(const QXmlAttributes &attrib); public: StartElementHandler(TagFileParser *parent, Handler h) : m_parent(parent), m_handler(h) {} void operator()(const QXmlAttributes &attrib) { (m_parent->*m_handler)(attrib); } private: TagFileParser *m_parent; Handler m_handler; }; class EndElementHandler { typedef void (TagFileParser::*Handler)(); public: EndElementHandler(TagFileParser *parent, Handler h) : m_parent(parent), m_handler(h) {} void operator()() { (m_parent->*m_handler)(); } private: TagFileParser *m_parent; Handler m_handler; }; public: TagFileParser(const char *tagName) : m_startElementHandlers(17), m_endElementHandlers(17), m_tagName(tagName) { m_startElementHandlers.setAutoDelete(TRUE); m_endElementHandlers.setAutoDelete(TRUE); m_curClass=0; m_curFile=0; m_curNamespace=0; m_curPackage=0; m_curGroup=0; m_curPage=0; m_curDir=0; m_curMember=0; m_curEnumValue=0; m_curIncludes=0; m_state = Invalid; m_locator = 0; } void setDocumentLocator ( QXmlLocator * locator ) { m_locator = locator; } void setFileName( const QString &fileName ) { m_inputFileName = fileName.utf8(); } void warn(const char *fmt) { ::warn(m_inputFileName,m_locator->lineNumber(),"%s", fmt); } void warn(const char *fmt,const char *s) { ::warn(m_inputFileName,m_locator->lineNumber(),fmt,s); } void startCompound( const QXmlAttributes& attrib ) { m_curString = ""; QString kind = attrib.value("kind"); QString isObjC = attrib.value("objc"); if (kind=="class") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Class; m_state = InClass; } else if (kind=="struct") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Struct; m_state = InClass; } else if (kind=="union") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Union; m_state = InClass; } else if (kind=="interface") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Interface; m_state = InClass; } else if (kind=="enum") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Enum; m_state = InClass; } else if (kind=="exception") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Exception; m_state = InClass; } else if (kind=="protocol") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Protocol; m_state = InClass; } else if (kind=="category") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Category; m_state = InClass; } else if (kind=="service") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Service; m_state = InClass; } else if (kind=="singleton") { m_curClass = new TagClassInfo; m_curClass->kind = TagClassInfo::Singleton; m_state = InClass; } else if (kind=="file") { m_curFile = new TagFileInfo; m_state = InFile; } else if (kind=="namespace") { m_curNamespace = new TagNamespaceInfo; m_state = InNamespace; } else if (kind=="group") { m_curGroup = new TagGroupInfo; m_state = InGroup; } else if (kind=="page") { m_curPage = new TagPageInfo; m_state = InPage; } else if (kind=="package") { m_curPackage = new TagPackageInfo; m_state = InPackage; } else if (kind=="dir") { m_curDir = new TagDirInfo; m_state = InDir; } else { warn("Unknown compound attribute '%s' found!",kind.data()); m_state = Invalid; } if (isObjC=="yes" && m_curClass) { m_curClass->isObjC = TRUE; } } void endCompound() { switch (m_state) { case InClass: m_tagFileClasses.append(m_curClass); m_curClass=0; break; case InFile: m_tagFileFiles.append(m_curFile); m_curFile=0; break; case InNamespace: m_tagFileNamespaces.append(m_curNamespace); m_curNamespace=0; break; case InGroup: m_tagFileGroups.append(m_curGroup); m_curGroup=0; break; case InPage: m_tagFilePages.append(m_curPage); m_curPage=0; break; case InDir: m_tagFileDirs.append(m_curDir); m_curDir=0; break; case InPackage: m_tagFilePackages.append(m_curPackage); m_curPackage=0; break; default: warn("tag 'compound' was not expected!"); } } void startMember( const QXmlAttributes& attrib) { m_curMember = new TagMemberInfo; m_curMember->kind = attrib.value("kind").utf8(); QCString protStr = attrib.value("protection").utf8(); QCString virtStr = attrib.value("virtualness").utf8(); QCString staticStr = attrib.value("static").utf8(); if (protStr=="protected") { m_curMember->prot = Protected; } else if (protStr=="private") { m_curMember->prot = Private; } if (virtStr=="virtual") { m_curMember->virt = Virtual; } else if (virtStr=="pure") { m_curMember->virt = Pure; } if (staticStr=="yes") { m_curMember->isStatic = TRUE; } m_stateStack.push(new State(m_state)); m_state = InMember; } void endMember() { m_state = *m_stateStack.top(); m_stateStack.remove(); switch(m_state) { case InClass: m_curClass->members.append(m_curMember); break; case InFile: m_curFile->members.append(m_curMember); break; case InNamespace: m_curNamespace->members.append(m_curMember); break; case InGroup: m_curGroup->members.append(m_curMember); break; case InPackage: m_curPackage->members.append(m_curMember); break; default: warn("Unexpected tag 'member' found"); break; } } void startEnumValue( const QXmlAttributes& attrib) { if (m_state==InMember) { m_curString = ""; m_curEnumValue = new TagEnumValueInfo; m_curEnumValue->file = attrib.value("file").utf8(); m_curEnumValue->anchor = attrib.value("anchor").utf8(); m_curEnumValue->clangid = attrib.value("clangid").utf8(); m_stateStack.push(new State(m_state)); m_state = InEnumValue; } else { warn("Found 'enumvalue' tag outside of member tag"); } } void endEnumValue() { m_curEnumValue->name = m_curString.stripWhiteSpace(); m_state = *m_stateStack.top(); m_stateStack.remove(); if (m_state==InMember) { m_curMember->enumValues.append(m_curEnumValue); m_curEnumValue=0; } } void endDocAnchor() { // Check whether or not the tag is automatically generate, in that case ignore the tag. switch(m_state) { case InClass: case InFile: case InNamespace: case InGroup: case InPage: case InMember: case InPackage: case InDir: if (QString(m_curString).startsWith("autotoc_md")) return; break; default: warn("Unexpected tag 'docanchor' found"); return; } switch(m_state) { case InClass: m_curClass->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InFile: m_curFile->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InNamespace: m_curNamespace->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InGroup: m_curGroup->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InPage: m_curPage->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InMember: m_curMember->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InPackage: m_curPackage->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InDir: m_curDir->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString,m_title)); break; default: break; // will not be reached } } void endClass() { switch(m_state) { case InClass: m_curClass->classList.append(m_curString); break; case InFile: m_curFile->classList.append(m_curString); break; case InNamespace: m_curNamespace->classList.append(m_curString); break; case InGroup: m_curGroup->classList.append(m_curString); break; case InPackage: m_curPackage->classList.append(m_curString); break; default: warn("Unexpected tag 'class' found"); break; } } void endNamespace() { switch(m_state) { case InNamespace: m_curNamespace->classList.append(m_curString); break; case InFile: m_curFile->namespaceList.append(m_curString); break; case InGroup: m_curGroup->namespaceList.append(m_curString); break; default: warn("Unexpected tag 'namespace' found"); break; } } void endFile() { switch(m_state) { case InGroup: m_curGroup->fileList.append(m_curString); break; case InDir: m_curDir->fileList.append(m_curString); break; default: warn("Unexpected tag 'file' found"); break; } } void endPage() { switch(m_state) { case InGroup: m_curGroup->fileList.append(m_curString); break; default: warn("Unexpected tag 'page' found"); break; } } void endDir() { switch(m_state) { case InDir: m_curDir->subdirList.append(m_curString); break; default: warn("Unexpected tag 'dir' found"); break; } } void startStringValue(const QXmlAttributes& ) { m_curString = ""; } void startDocAnchor(const QXmlAttributes& attrib ) { m_fileName = attrib.value("file").utf8(); m_title = attrib.value("title").utf8(); m_curString = ""; } void endType() { if (m_state==InMember) { m_curMember->type = m_curString; } else { warn("Unexpected tag 'type' found"); } } void endName() { switch (m_state) { case InClass: m_curClass->name = m_curString; break; case InFile: m_curFile->name = m_curString; break; case InNamespace: m_curNamespace->name = m_curString; break; case InGroup: m_curGroup->name = m_curString; break; case InPage: m_curPage->name = m_curString; break; case InDir: m_curDir->name = m_curString; break; case InMember: m_curMember->name = m_curString; break; case InPackage: m_curPackage->name = m_curString; break; default: warn("Unexpected tag 'name' found"); break; } } void startBase(const QXmlAttributes& attrib ) { m_curString=""; if (m_state==InClass && m_curClass) { QString protStr = attrib.value("protection"); QString virtStr = attrib.value("virtualness"); Protection prot = Public; Specifier virt = Normal; if (protStr=="protected") { prot = Protected; } else if (protStr=="private") { prot = Private; } if (virtStr=="virtual") { virt = Virtual; } m_curClass->bases.push_back(BaseInfo(m_curString,prot,virt)); } else { warn("Unexpected tag 'base' found"); } } void endBase() { if (m_state==InClass && m_curClass) { m_curClass->bases.back().name = m_curString; } else { warn("Unexpected tag 'base' found"); } } void startIncludes(const QXmlAttributes& attrib ) { if (m_state==InFile && m_curFile) { m_curIncludes = new TagIncludeInfo; m_curIncludes->id = attrib.value("id").utf8(); m_curIncludes->name = attrib.value("name").utf8(); m_curIncludes->isLocal = attrib.value("local").utf8()=="yes" ? TRUE : FALSE; m_curIncludes->isImported = attrib.value("imported").utf8()=="yes" ? TRUE : FALSE; m_curFile->includes.append(m_curIncludes); } else { warn("Unexpected tag 'includes' found"); } m_curString=""; } void endIncludes() { m_curIncludes->text = m_curString; } void endTemplateArg() { if (m_state==InClass && m_curClass) { if (m_curClass->templateArguments==0) { m_curClass->templateArguments = new QList; m_curClass->templateArguments->setAutoDelete(TRUE); } m_curClass->templateArguments->append(new QCString(m_curString)); } else { warn("Unexpected tag 'templarg' found"); } } void endFilename() { switch (m_state) { case InClass: m_curClass->filename = m_curString; break; case InNamespace: m_curNamespace->filename = m_curString; break; case InFile: m_curFile->filename = m_curString; break; case InGroup: m_curGroup->filename = m_curString; break; case InPage: m_curPage->filename = m_curString; break; case InPackage: m_curPackage->filename = m_curString; break; case InDir: m_curDir->filename = m_curString; break; default: warn("Unexpected tag 'filename' found"); break; } } void endPath() { switch (m_state) { case InFile: m_curFile->path = m_curString; break; case InDir: m_curDir->path = m_curString; break; default: warn("Unexpected tag 'path' found"); break; } } void endAnchor() { if (m_state==InMember) { m_curMember->anchor = m_curString; } else if (m_state==InClass) { m_curClass->anchor = m_curString; } else { warn("Unexpected tag 'anchor' found"); } } void endClangId() { if (m_state==InMember) { m_curMember->clangId = m_curString; } else if (m_state==InClass) { m_curClass->clangId = m_curString; } else if (m_state==InNamespace) { m_curNamespace->clangId = m_curString; } else { warn("Unexpected tag 'clangid' found"); } } void endAnchorFile() { if (m_state==InMember) { m_curMember->anchorFile = m_curString; } else { warn("Unexpected tag 'anchorfile' found"); } } void endArglist() { if (m_state==InMember) { m_curMember->arglist = m_curString; } else { warn("Unexpected tag 'arglist' found"); } } void endTitle() { switch (m_state) { case InGroup: m_curGroup->title = m_curString; break; case InPage: m_curPage->title = m_curString; break; default: warn("Unexpected tag 'title' found"); break; } } void endSubgroup() { if (m_state==InGroup) { m_curGroup->subgroupList.append(m_curString); } else { warn("Unexpected tag 'subgroup' found"); } } void startIgnoreElement(const QXmlAttributes& ) { } void endIgnoreElement() { } bool startDocument() { m_state = Invalid; m_curClass=0; m_curNamespace=0; m_curFile=0; m_curGroup=0; m_curPage=0; m_curPackage=0; m_curDir=0; m_stateStack.setAutoDelete(TRUE); m_tagFileClasses.setAutoDelete(TRUE); m_tagFileFiles.setAutoDelete(TRUE); m_tagFileNamespaces.setAutoDelete(TRUE); m_tagFileGroups.setAutoDelete(TRUE); m_tagFilePages.setAutoDelete(TRUE); m_tagFilePackages.setAutoDelete(TRUE); m_tagFileDirs.setAutoDelete(TRUE); m_startElementHandlers.insert("compound", new StartElementHandler(this,&TagFileParser::startCompound)); m_startElementHandlers.insert("member", new StartElementHandler(this,&TagFileParser::startMember)); m_startElementHandlers.insert("enumvalue", new StartElementHandler(this,&TagFileParser::startEnumValue)); m_startElementHandlers.insert("name", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("base", new StartElementHandler(this,&TagFileParser::startBase)); m_startElementHandlers.insert("filename", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("includes", new StartElementHandler(this,&TagFileParser::startIncludes)); m_startElementHandlers.insert("path", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("anchorfile", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("anchor", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("clangid", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("arglist", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("title", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("subgroup", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("class", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("namespace", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("file", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("dir", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("page", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("docanchor", new StartElementHandler(this,&TagFileParser::startDocAnchor)); m_startElementHandlers.insert("tagfile", new StartElementHandler(this,&TagFileParser::startIgnoreElement)); m_startElementHandlers.insert("templarg", new StartElementHandler(this,&TagFileParser::startStringValue)); m_startElementHandlers.insert("type", new StartElementHandler(this,&TagFileParser::startStringValue)); m_endElementHandlers.insert("compound", new EndElementHandler(this,&TagFileParser::endCompound)); m_endElementHandlers.insert("member", new EndElementHandler(this,&TagFileParser::endMember)); m_endElementHandlers.insert("enumvalue", new EndElementHandler(this,&TagFileParser::endEnumValue)); m_endElementHandlers.insert("name", new EndElementHandler(this,&TagFileParser::endName)); m_endElementHandlers.insert("base", new EndElementHandler(this,&TagFileParser::endBase)); m_endElementHandlers.insert("filename", new EndElementHandler(this,&TagFileParser::endFilename)); m_endElementHandlers.insert("includes", new EndElementHandler(this,&TagFileParser::endIncludes)); m_endElementHandlers.insert("path", new EndElementHandler(this,&TagFileParser::endPath)); m_endElementHandlers.insert("anchorfile", new EndElementHandler(this,&TagFileParser::endAnchorFile)); m_endElementHandlers.insert("anchor", new EndElementHandler(this,&TagFileParser::endAnchor)); m_endElementHandlers.insert("clangid", new EndElementHandler(this,&TagFileParser::endClangId)); m_endElementHandlers.insert("arglist", new EndElementHandler(this,&TagFileParser::endArglist)); m_endElementHandlers.insert("title", new EndElementHandler(this,&TagFileParser::endTitle)); m_endElementHandlers.insert("subgroup", new EndElementHandler(this,&TagFileParser::endSubgroup)); m_endElementHandlers.insert("class" , new EndElementHandler(this,&TagFileParser::endClass)); m_endElementHandlers.insert("namespace", new EndElementHandler(this,&TagFileParser::endNamespace)); m_endElementHandlers.insert("file", new EndElementHandler(this,&TagFileParser::endFile)); m_endElementHandlers.insert("dir", new EndElementHandler(this,&TagFileParser::endDir)); m_endElementHandlers.insert("page", new EndElementHandler(this,&TagFileParser::endPage)); m_endElementHandlers.insert("docanchor", new EndElementHandler(this,&TagFileParser::endDocAnchor)); m_endElementHandlers.insert("tagfile", new EndElementHandler(this,&TagFileParser::endIgnoreElement)); m_endElementHandlers.insert("templarg", new EndElementHandler(this,&TagFileParser::endTemplateArg)); m_endElementHandlers.insert("type", new EndElementHandler(this,&TagFileParser::endType)); return TRUE; } bool startElement( const QString&, const QString&, const QString&name, const QXmlAttributes& attrib ) { //printf("startElement '%s'\n",name.data()); StartElementHandler *handler = m_startElementHandlers[name.utf8()]; if (handler) { (*handler)(attrib); } else { warn("Unknown tag '%s' found!",name.data()); } return TRUE; } bool endElement( const QString&, const QString&, const QString& name ) { //printf("endElement '%s'\n",name.data()); EndElementHandler *handler = m_endElementHandlers[name.utf8()]; if (handler) { (*handler)(); } else { warn("Unknown tag '%s' found!",name.data()); } return TRUE; } bool characters ( const QString & ch ) { m_curString+=ch.utf8(); return TRUE; } void dump(); void buildLists(const std::shared_ptr &root); void addIncludes(); private: void buildMemberList(const std::shared_ptr &ce,QList &members); void addDocAnchors(const std::shared_ptr &e,const TagAnchorInfoList &l); QList m_tagFileClasses; QList m_tagFileFiles; QList m_tagFileNamespaces; QList m_tagFileGroups; QList m_tagFilePages; QList m_tagFilePackages; QList m_tagFileDirs; QDict m_startElementHandlers; QDict m_endElementHandlers; TagClassInfo *m_curClass; TagFileInfo *m_curFile; TagNamespaceInfo *m_curNamespace; TagPackageInfo *m_curPackage; TagGroupInfo *m_curGroup; TagPageInfo *m_curPage; TagDirInfo *m_curDir; TagMemberInfo *m_curMember; TagEnumValueInfo *m_curEnumValue; TagIncludeInfo *m_curIncludes; QCString m_curString; QCString m_tagName; QCString m_fileName; QCString m_title; State m_state; QStack m_stateStack; QXmlLocator *m_locator; QCString m_inputFileName; }; /** Error handler for the XML tag file parser. * * Basically dumps all fatal error to stderr using err(). */ class TagFileErrorHandler : public QXmlErrorHandler { public: virtual ~TagFileErrorHandler() {} bool warning( const QXmlParseException & ) { return FALSE; } bool error( const QXmlParseException & ) { return FALSE; } bool fatalError( const QXmlParseException &exception ) { err("Fatal error at line %d column %d: %s\n", exception.lineNumber(),exception.columnNumber(), exception.message().data()); return FALSE; } QString errorString() { return ""; } private: QString errorMsg; }; /*! Dumps the internal structures. For debugging only! */ void TagFileParser::dump() { msg("Result:\n"); QListIterator lci(m_tagFileClasses); //============== CLASSES TagClassInfo *cd; for (;(cd=lci.current());++lci) { msg("class '%s'\n",cd->name.data()); msg(" filename '%s'\n",cd->filename.data()); for (const BaseInfo &bi : cd->bases) { msg( " base: %s \n", bi.name.data() ); } QListIterator mci(cd->members); TagMemberInfo *md; for (;(md=mci.current());++mci) { msg(" member:\n"); msg(" kind: '%s'\n",md->kind.data()); msg(" name: '%s'\n",md->name.data()); msg(" anchor: '%s'\n",md->anchor.data()); msg(" arglist: '%s'\n",md->arglist.data()); } } //============== NAMESPACES QListIterator lni(m_tagFileNamespaces); TagNamespaceInfo *nd; for (;(nd=lni.current());++lni) { msg("namespace '%s'\n",nd->name.data()); msg(" filename '%s'\n",nd->filename.data()); QCStringList::Iterator it; for ( it = nd->classList.begin(); it != nd->classList.end(); ++it ) { msg( " class: %s \n", (*it).data() ); } QListIterator mci(nd->members); TagMemberInfo *md; for (;(md=mci.current());++mci) { msg(" member:\n"); msg(" kind: '%s'\n",md->kind.data()); msg(" name: '%s'\n",md->name.data()); msg(" anchor: '%s'\n",md->anchor.data()); msg(" arglist: '%s'\n",md->arglist.data()); } } //============== FILES QListIterator lfi(m_tagFileFiles); TagFileInfo *fd; for (;(fd=lfi.current());++lfi) { msg("file '%s'\n",fd->name.data()); msg(" filename '%s'\n",fd->filename.data()); QCStringList::Iterator it; for ( it = fd->namespaceList.begin(); it != fd->namespaceList.end(); ++it ) { msg( " namespace: %s \n", (*it).data() ); } for ( it = fd->classList.begin(); it != fd->classList.end(); ++it ) { msg( " class: %s \n", (*it).data() ); } QListIterator mci(fd->members); TagMemberInfo *md; for (;(md=mci.current());++mci) { msg(" member:\n"); msg(" kind: '%s'\n",md->kind.data()); msg(" name: '%s'\n",md->name.data()); msg(" anchor: '%s'\n",md->anchor.data()); msg(" arglist: '%s'\n",md->arglist.data()); } QListIterator mii(fd->includes); TagIncludeInfo *ii; for (;(ii=mii.current());++mii) { msg(" includes id: %s name: %s\n",ii->id.data(),ii->name.data()); } } //============== GROUPS QListIterator lgi(m_tagFileGroups); TagGroupInfo *gd; for (;(gd=lgi.current());++lgi) { msg("group '%s'\n",gd->name.data()); msg(" filename '%s'\n",gd->filename.data()); QCStringList::Iterator it; for ( it = gd->namespaceList.begin(); it != gd->namespaceList.end(); ++it ) { msg( " namespace: %s \n", (*it).data() ); } for ( it = gd->classList.begin(); it != gd->classList.end(); ++it ) { msg( " class: %s \n", (*it).data() ); } for ( it = gd->fileList.begin(); it != gd->fileList.end(); ++it ) { msg( " file: %s \n", (*it).data() ); } for ( it = gd->subgroupList.begin(); it != gd->subgroupList.end(); ++it ) { msg( " subgroup: %s \n", (*it).data() ); } for ( it = gd->pageList.begin(); it != gd->pageList.end(); ++it ) { msg( " page: %s \n", (*it).data() ); } QListIterator mci(gd->members); TagMemberInfo *md; for (;(md=mci.current());++mci) { msg(" member:\n"); msg(" kind: '%s'\n",md->kind.data()); msg(" name: '%s'\n",md->name.data()); msg(" anchor: '%s'\n",md->anchor.data()); msg(" arglist: '%s'\n",md->arglist.data()); } } //============== PAGES QListIterator lpi(m_tagFilePages); TagPageInfo *pd; for (;(pd=lpi.current());++lpi) { msg("page '%s'\n",pd->name.data()); msg(" title '%s'\n",pd->title.data()); msg(" filename '%s'\n",pd->filename.data()); } //============== DIRS QListIterator ldi(m_tagFileDirs); TagDirInfo *dd; for (;(dd=ldi.current());++ldi) { msg("dir '%s'\n",dd->name.data()); msg(" path '%s'\n",dd->path.data()); QCStringList::Iterator it; for ( it = dd->fileList.begin(); it != dd->fileList.end(); ++it ) { msg( " file: %s \n", (*it).data() ); } for ( it = dd->subdirList.begin(); it != dd->subdirList.end(); ++it ) { msg( " subdir: %s \n", (*it).data() ); } } } void TagFileParser::addDocAnchors(const std::shared_ptr &e,const TagAnchorInfoList &l) { QListIterator tli(l); TagAnchorInfo *ta; for (tli.toFirst();(ta=tli.current());++tli) { if (SectionManager::instance().find(ta->label)==0) { //printf("New sectionInfo file=%s anchor=%s\n", // ta->fileName.data(),ta->label.data()); SectionInfo *si=SectionManager::instance().add( ta->label,ta->fileName,-1,ta->title, SectionType::Anchor,0,m_tagName); e->anchors.push_back(si); } else { warn("Duplicate anchor %s found",ta->label.data()); } } } void TagFileParser::buildMemberList(const std::shared_ptr &ce,QList &members) { QListIterator mii(members); TagMemberInfo *tmi; for (;(tmi=mii.current());++mii) { std::shared_ptr me = std::make_shared(); me->type = tmi->type; me->name = tmi->name; me->args = tmi->arglist; if (!me->args.isEmpty()) { me->argList = *stringToArgumentList(SrcLangExt_Cpp,me->args); } if (tmi->enumValues.count()>0) { me->spec |= Entry::Strong; QListIterator evii(tmi->enumValues); TagEnumValueInfo *evi; for (evii.toFirst();(evi=evii.current());++evii) { std::shared_ptr ev = std::make_shared(); ev->type = "@"; ev->name = evi->name; ev->id = evi->clangid; ev->section = Entry::VARIABLE_SEC; ev->tagInfoData.tagName = m_tagName; ev->tagInfoData.anchor = evi->anchor; ev->tagInfoData.fileName = evi->file; ev->hasTagInfo = TRUE; me->moveToSubEntryAndKeep(ev); } } me->protection = tmi->prot; me->virt = tmi->virt; me->stat = tmi->isStatic; me->fileName = ce->fileName; me->id = tmi->clangId; if (ce->section == Entry::GROUPDOC_SEC) { me->groups.push_back(Grouping(ce->name,Grouping::GROUPING_INGROUP)); } addDocAnchors(me,tmi->docAnchors); me->tagInfoData.tagName = m_tagName; me->tagInfoData.anchor = tmi->anchor; me->tagInfoData.fileName = tmi->anchorFile; me->hasTagInfo = TRUE; if (tmi->kind=="define") { me->type="#define"; me->section = Entry::DEFINE_SEC; } else if (tmi->kind=="enumvalue") { me->section = Entry::VARIABLE_SEC; me->mtype = Method; } else if (tmi->kind=="property") { me->section = Entry::VARIABLE_SEC; me->mtype = Property; } else if (tmi->kind=="event") { me->section = Entry::VARIABLE_SEC; me->mtype = Event; } else if (tmi->kind=="variable") { me->section = Entry::VARIABLE_SEC; me->mtype = Method; } else if (tmi->kind=="typedef") { me->section = Entry::VARIABLE_SEC; //Entry::TYPEDEF_SEC; me->type.prepend("typedef "); me->mtype = Method; } else if (tmi->kind=="enumeration") { me->section = Entry::ENUM_SEC; me->mtype = Method; } else if (tmi->kind=="function") { me->section = Entry::FUNCTION_SEC; me->mtype = Method; } else if (tmi->kind=="signal") { me->section = Entry::FUNCTION_SEC; me->mtype = Signal; } else if (tmi->kind=="prototype") { me->section = Entry::FUNCTION_SEC; me->mtype = Method; } else if (tmi->kind=="friend") { me->section = Entry::FUNCTION_SEC; me->type.prepend("friend "); me->mtype = Method; } else if (tmi->kind=="dcop") { me->section = Entry::FUNCTION_SEC; me->mtype = DCOP; } else if (tmi->kind=="slot") { me->section = Entry::FUNCTION_SEC; me->mtype = Slot; } ce->moveToSubEntryAndKeep(me); } } static QCString stripPath(const QCString &s) { int i=s.findRev('/'); if (i!=-1) { return s.right(s.length()-i-1); } else { return s; } } /*! Injects the info gathered by the XML parser into the Entry tree. * This tree contains the information extracted from the input in a * "unrelated" form. */ void TagFileParser::buildLists(const std::shared_ptr &root) { // build class list QListIterator cit(m_tagFileClasses); TagClassInfo *tci; for (cit.toFirst();(tci=cit.current());++cit) { std::shared_ptr ce = std::make_shared(); ce->section = Entry::CLASS_SEC; switch (tci->kind) { case TagClassInfo::Class: break; case TagClassInfo::Struct: ce->spec = Entry::Struct; break; case TagClassInfo::Union: ce->spec = Entry::Union; break; case TagClassInfo::Interface: ce->spec = Entry::Interface; break; case TagClassInfo::Enum: ce->spec = Entry::Enum; break; case TagClassInfo::Exception: ce->spec = Entry::Exception; break; case TagClassInfo::Protocol: ce->spec = Entry::Protocol; break; case TagClassInfo::Category: ce->spec = Entry::Category; break; case TagClassInfo::Service: ce->spec = Entry::Service; break; case TagClassInfo::Singleton: ce->spec = Entry::Singleton; break; case TagClassInfo::None: // should never happen, means not properly initialized assert(tci->kind != TagClassInfo::None); break; } ce->name = tci->name; if (tci->kind==TagClassInfo::Protocol) { ce->name+="-p"; } addDocAnchors(ce,tci->docAnchors); ce->tagInfoData.tagName = m_tagName; ce->tagInfoData.anchor = tci->anchor; ce->tagInfoData.fileName = tci->filename; ce->hasTagInfo = TRUE; ce->id = tci->clangId; ce->lang = tci->isObjC ? SrcLangExt_ObjC : SrcLangExt_Unknown; // transfer base class list ce->extends = tci->bases; tci->bases.clear(); if (tci->templateArguments) { ArgumentList al; QListIterator sli(*tci->templateArguments); QCString *argName; for (;(argName=sli.current());++sli) { Argument a; a.type = "class"; a.name = *argName; al.push_back(a); } ce->tArgLists.push_back(al); } buildMemberList(ce,tci->members); root->moveToSubEntryAndKeep(ce); } // build file list QListIterator fit(m_tagFileFiles); TagFileInfo *tfi; for (fit.toFirst();(tfi=fit.current());++fit) { std::shared_ptr fe = std::make_shared(); fe->section = guessSection(tfi->name); fe->name = tfi->name; addDocAnchors(fe,tfi->docAnchors); fe->tagInfoData.tagName = m_tagName; fe->tagInfoData.fileName = tfi->filename; fe->hasTagInfo = TRUE; QCString fullName = m_tagName+":"+tfi->path+stripPath(tfi->name); fe->fileName = fullName; //printf("createFileDef() filename=%s\n",tfi->filename.data()); std::unique_ptr fd { createFileDef(m_tagName+":"+tfi->path, tfi->name,m_tagName, tfi->filename) }; FileName *mn; if ((mn=Doxygen::inputNameLinkedMap->find(tfi->name))) { mn->push_back(std::move(fd)); } else { mn = Doxygen::inputNameLinkedMap->add(tfi->name,fullName); mn->push_back(std::move(fd)); } buildMemberList(fe,tfi->members); root->moveToSubEntryAndKeep(fe); } // build namespace list QListIterator nit(m_tagFileNamespaces); TagNamespaceInfo *tni; for (nit.toFirst();(tni=nit.current());++nit) { std::shared_ptr ne = std::make_shared(); ne->section = Entry::NAMESPACE_SEC; ne->name = tni->name; addDocAnchors(ne,tni->docAnchors); ne->tagInfoData.tagName = m_tagName; ne->tagInfoData.fileName = tni->filename; ne->hasTagInfo = TRUE; ne->id = tni->clangId; buildMemberList(ne,tni->members); root->moveToSubEntryAndKeep(ne); } // build package list QListIterator pit(m_tagFilePackages); TagPackageInfo *tpgi; for (pit.toFirst();(tpgi=pit.current());++pit) { std::shared_ptr pe = std::make_shared(); pe->section = Entry::PACKAGE_SEC; pe->name = tpgi->name; addDocAnchors(pe,tpgi->docAnchors); pe->tagInfoData.tagName = m_tagName; pe->tagInfoData.fileName = tpgi->filename; pe->hasTagInfo = TRUE; buildMemberList(pe,tpgi->members); root->moveToSubEntryAndKeep(pe); } // build group list QListIterator git(m_tagFileGroups); TagGroupInfo *tgi; for (git.toFirst();(tgi=git.current());++git) { std::shared_ptr ge = std::make_shared(); ge->section = Entry::GROUPDOC_SEC; ge->name = tgi->name; ge->type = tgi->title; addDocAnchors(ge,tgi->docAnchors); ge->tagInfoData.tagName = m_tagName; ge->tagInfoData.fileName = tgi->filename; ge->hasTagInfo = TRUE; buildMemberList(ge,tgi->members); root->moveToSubEntryAndKeep(ge); } // set subgroup relations bug_774118 for (git.toFirst();(tgi=git.current());++git) { QCStringList::Iterator it; for ( it = tgi->subgroupList.begin(); it != tgi->subgroupList.end(); ++it ) { //QListIterator eli(*(root->children())); //Entry *childNode; //for (eli.toFirst();(childNode=eli.current());++eli) const auto &children = root->children(); auto i = std::find_if(children.begin(),children.end(), [&](const std::shared_ptr &e) { return e->name = *it; }); if (i!=children.end()) { (*i)->groups.push_back(Grouping(tgi->name,Grouping::GROUPING_INGROUP)); } } } // build page list QListIterator pgit(m_tagFilePages); TagPageInfo *tpi; for (pgit.toFirst();(tpi=pgit.current());++pgit) { std::shared_ptr pe = std::make_shared(); bool isIndex = (stripExtensionGeneral(tpi->filename,getFileNameExtension(tpi->filename))=="index"); pe->section = isIndex ? Entry::MAINPAGEDOC_SEC : Entry::PAGEDOC_SEC; pe->name = tpi->name; pe->args = tpi->title; addDocAnchors(pe,tpi->docAnchors); pe->tagInfoData.tagName = m_tagName; pe->tagInfoData.fileName = tpi->filename; pe->hasTagInfo = TRUE; root->moveToSubEntryAndKeep(pe); } } void TagFileParser::addIncludes() { QListIterator fit(m_tagFileFiles); TagFileInfo *tfi; for (fit.toFirst();(tfi=fit.current());++fit) { //printf("tag file tagName=%s path=%s name=%s\n",m_tagName.data(),tfi->path.data(),tfi->name.data()); FileName *fn = Doxygen::inputNameLinkedMap->find(tfi->name); if (fn) { for (const auto &fd : *fn) { //printf("input file path=%s name=%s\n",fd->getPath().data(),fd->name().data()); if (fd->getPath()==QCString(m_tagName+":"+tfi->path)) { //printf("found\n"); QListIterator mii(tfi->includes); TagIncludeInfo *ii; for (;(ii=mii.current());++mii) { //printf("ii->name='%s'\n",ii->name.data()); FileName *ifn = Doxygen::inputNameLinkedMap->find(ii->name); ASSERT(ifn!=0); if (ifn) { for (const auto &ifd : *ifn) { //printf("ifd->getOutputFileBase()=%s ii->id=%s\n", // ifd->getOutputFileBase().data(),ii->id.data()); if (ifd->getOutputFileBase()==QCString(ii->id)) { fd->addIncludeDependency(ifd.get(),ii->text,ii->isLocal,ii->isImported); } } } } } } } } } void parseTagFile(const std::shared_ptr &root,const char *fullName) { QFileInfo fi(fullName); if (!fi.exists()) return; TagFileParser handler( fullName ); // tagName handler.setFileName(fullName); TagFileErrorHandler errorHandler; QFile xmlFile( fullName ); QXmlInputSource source( xmlFile ); QXmlSimpleReader reader; reader.setContentHandler( &handler ); reader.setErrorHandler( &errorHandler ); reader.parse( source ); handler.buildLists(root); handler.addIncludes(); //handler.dump(); }