/****************************************************************************** * * * * * 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 "xml.h" #include "entry.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 "containers.h" #include "debug.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; }; /** Container for enum values that are scoped within an enum */ class TagEnumValueInfo { public: QCString name; QCString file; QCString anchor; QCString clangid; }; /** 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 member specific info that can be read from a tagfile */ class TagMemberInfo { public: QCString type; QCString name; QCString anchorFile; QCString anchor; QCString arglist; QCString kind; QCString clangId; std::vector docAnchors; Protection prot = Public; Specifier virt = Normal; bool isStatic = false; std::vector enumValues; int lineNr; }; /** Base class for all compound types */ class TagCompoundInfo { public: enum class CompoundType { Class, Concept, Namespace, Package, File, Group, Page, Dir }; explicit TagCompoundInfo(CompoundType type) : m_type(type) {} virtual ~TagCompoundInfo() {} CompoundType compoundType() const { return m_type; } std::vector members; QCString name; QCString filename; std::vector docAnchors; int lineNr; private: CompoundType m_type; }; /** Container for class specific info that can be read from a tagfile */ class TagClassInfo : public TagCompoundInfo { public: enum class Kind { None=-1, Class, Struct, Union, Interface, Exception, Protocol, Category, Enum, Service, Singleton }; TagClassInfo(Kind k) : TagCompoundInfo(CompoundType::Class), kind(k) {} QCString clangId; QCString anchor; std::vector bases; StringVector templateArguments; StringVector classList; Kind kind; bool isObjC = false; static TagClassInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagClassInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** Container for concept specific info that can be read from a tagfile */ class TagConceptInfo : public TagCompoundInfo { public: TagConceptInfo() :TagCompoundInfo(CompoundType::Concept) {} QCString clangId; static TagConceptInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagConceptInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** Container for namespace specific info that can be read from a tagfile */ class TagNamespaceInfo : public TagCompoundInfo { public: TagNamespaceInfo() :TagCompoundInfo(CompoundType::Namespace) {} QCString clangId; StringVector classList; StringVector conceptList; StringVector namespaceList; static TagNamespaceInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagNamespaceInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** Container for package specific info that can be read from a tagfile */ class TagPackageInfo : public TagCompoundInfo { public: TagPackageInfo() : TagCompoundInfo(CompoundType::Package) { } StringVector classList; static TagPackageInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagPackageInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** Container for file specific info that can be read from a tagfile */ class TagFileInfo : public TagCompoundInfo { public: TagFileInfo() : TagCompoundInfo(CompoundType::File) { } QCString path; StringVector classList; StringVector conceptList; StringVector namespaceList; std::vector includes; static TagFileInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagFileInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** Container for group specific info that can be read from a tagfile */ class TagGroupInfo : public TagCompoundInfo { public: TagGroupInfo() : TagCompoundInfo(CompoundType::Group) { } QCString title; StringVector subgroupList; StringVector classList; StringVector conceptList; StringVector namespaceList; StringVector fileList; StringVector pageList; StringVector dirList; static TagGroupInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagGroupInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** Container for page specific info that can be read from a tagfile */ class TagPageInfo : public TagCompoundInfo { public: TagPageInfo() : TagCompoundInfo(CompoundType::Page) {} QCString title; static TagPageInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagPageInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** Container for directory specific info that can be read from a tagfile */ class TagDirInfo : public TagCompoundInfo { public: TagDirInfo() : TagCompoundInfo(CompoundType::Dir) {} QCString path; StringVector subdirList; StringVector fileList; static TagDirInfo *get(std::unique_ptr &t) { return dynamic_cast(t.get()); } static const TagDirInfo *get(const std::unique_ptr &t) { return dynamic_cast(t.get()); } }; /** 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: TagFileParser(const char *tagName) : m_tagName(tagName) {} void setDocumentLocator ( const XMLLocator * locator ) { m_locator = locator; } void startDocument() { m_state = Invalid; } void startElement( const QCString &name, const XMLHandlers::Attributes& attrib ); void endElement( const QCString &name ); void characters ( const QCString & ch ) { m_curString+=ch; } void error( const QCString &fileName,int lineNr,const QCString &msg) { ::warn(fileName,lineNr,"%s",qPrint(msg)); } void dump(); void buildLists(const std::shared_ptr &root); void addIncludes(); void startCompound( const XMLHandlers::Attributes& attrib ); void endCompound() { switch (m_state) { case InClass: case InConcept: case InFile: case InNamespace: case InGroup: case InPage: case InDir: case InPackage: m_tagFileCompounds.push_back(std::move(m_curCompound)); break; default: warn("tag 'compound' was not expected!"); break; } } void startMember( const XMLHandlers::Attributes& attrib) { m_curMember = TagMemberInfo(); m_curMember.kind = XMLHandlers::value(attrib,"kind"); QCString protStr = XMLHandlers::value(attrib,"protection"); QCString virtStr = XMLHandlers::value(attrib,"virtualness"); QCString staticStr = XMLHandlers::value(attrib,"static"); m_curMember.lineNr = m_locator->lineNr(); 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(m_state); m_state = InMember; } void endMember() { m_state = m_stateStack.top(); m_stateStack.pop(); switch(m_state) { case InClass: case InFile: case InNamespace: case InGroup: case InPackage: m_curCompound->members.push_back(m_curMember); break; default: warn("Unexpected tag 'member' found"); break; } } void startEnumValue( const XMLHandlers::Attributes& attrib) { if (m_state==InMember) { m_curString = ""; m_curEnumValue = TagEnumValueInfo(); m_curEnumValue.file = XMLHandlers::value(attrib,"file"); m_curEnumValue.anchor = XMLHandlers::value(attrib,"anchor"); m_curEnumValue.clangid = XMLHandlers::value(attrib,"clangid"); m_stateStack.push(m_state); m_state = InEnumValue; } else { warn("Found 'enumvalue' tag outside of member tag"); } } void endEnumValue() { m_curEnumValue.name = QCString(m_curString).stripWhiteSpace().str(); m_state = m_stateStack.top(); m_stateStack.pop(); if (m_state==InMember) { m_curMember.enumValues.push_back(m_curEnumValue); m_curEnumValue=TagEnumValueInfo(); } } void endDocAnchor() { // Check whether or not the tag is automatically generate, in that case ignore the tag. switch(m_state) { case InClass: case InConcept: case InFile: case InNamespace: case InGroup: case InPage: case InMember: case InPackage: case InDir: if (m_curString.right(10)=="autotoc_md") return; break; default: warn("Unexpected tag 'docanchor' found"); return; } switch(m_state) { case InClass: case InConcept: case InFile: case InNamespace: case InGroup: case InPage: case InPackage: case InDir: m_curCompound->docAnchors.push_back(TagAnchorInfo(m_fileName,m_curString,m_title)); break; case InMember: m_curMember.docAnchors.push_back(TagAnchorInfo(m_fileName,m_curString,m_title)); break; default: break; // will not be reached } } void endClass() { switch(m_state) { case InClass: TagClassInfo::get(m_curCompound)->classList.push_back(m_curString.str()); break; case InFile: TagFileInfo::get(m_curCompound)->classList.push_back(m_curString.str()); break; case InNamespace: TagNamespaceInfo::get(m_curCompound)->classList.push_back(m_curString.str()); break; case InGroup: TagGroupInfo::get(m_curCompound)->classList.push_back(m_curString.str()); break; case InPackage: TagPackageInfo::get(m_curCompound)->classList.push_back(m_curString.str()); break; default: warn("Unexpected tag 'class' found"); break; } } void endConcept() { switch(m_state) { case InNamespace: TagNamespaceInfo::get(m_curCompound)->conceptList.push_back(m_curString.str()); break; case InFile: TagFileInfo::get(m_curCompound)->conceptList.push_back(m_curString.str()); break; case InGroup: TagGroupInfo::get(m_curCompound)->conceptList.push_back(m_curString.str()); break; default: warn("Unexpected tag 'concept' found"); break; } } void endNamespace() { switch(m_state) { case InNamespace: TagNamespaceInfo::get(m_curCompound)->namespaceList.push_back(m_curString.str()); break; case InFile: TagFileInfo::get(m_curCompound)->namespaceList.push_back(m_curString.str()); break; case InGroup: TagGroupInfo::get(m_curCompound)->namespaceList.push_back(m_curString.str()); break; default: warn("Unexpected tag 'namespace' found"); break; } } void endFile() { switch(m_state) { case InGroup: TagGroupInfo::get(m_curCompound)->fileList.push_back(m_curString.str()); break; case InDir: TagDirInfo::get(m_curCompound)->fileList.push_back(m_curString.str()); break; default: warn("Unexpected tag 'file' found"); break; } } void endPage() { switch(m_state) { case InGroup: TagGroupInfo::get(m_curCompound)->fileList.push_back(m_curString.str()); break; default: warn("Unexpected tag 'page' found"); break; } } void endDir() { switch(m_state) { case InDir: TagDirInfo::get(m_curCompound)->subdirList.push_back(m_curString.str()); break; default: warn("Unexpected tag 'dir' found"); break; } } void startStringValue(const XMLHandlers::Attributes& ) { m_curString = ""; } void startDocAnchor(const XMLHandlers::Attributes& attrib ) { m_fileName = XMLHandlers::value(attrib,"file"); m_title = XMLHandlers::value(attrib,"title"); 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: case InConcept: case InFile: case InNamespace: case InGroup: case InPage: case InDir: case InPackage: m_curCompound->name = m_curString; break; case InMember: m_curMember.name = m_curString; break; default: warn("Unexpected tag 'name' found"); break; } } void startBase(const XMLHandlers::Attributes& attrib ) { m_curString=""; if (m_state==InClass && m_curCompound) { QCString protStr = XMLHandlers::value(attrib,"protection"); QCString virtStr = XMLHandlers::value(attrib,"virtualness"); Protection prot = Public; Specifier virt = Normal; if (protStr=="protected") { prot = Protected; } else if (protStr=="private") { prot = Private; } if (virtStr=="virtual") { virt = Virtual; } TagClassInfo::get(m_curCompound)->bases.push_back(BaseInfo(m_curString,prot,virt)); } else { warn("Unexpected tag 'base' found"); } } void endBase() { if (m_state==InClass && m_curCompound) { TagClassInfo::get(m_curCompound)->bases.back().name = m_curString; } else { warn("Unexpected tag 'base' found"); } } void startIncludes(const XMLHandlers::Attributes& attrib ) { m_curIncludes = TagIncludeInfo(); m_curIncludes.id = XMLHandlers::value(attrib,"id"); m_curIncludes.name = XMLHandlers::value(attrib,"name"); m_curIncludes.isLocal = XMLHandlers::value(attrib,"local")=="yes"; m_curIncludes.isImported = XMLHandlers::value(attrib,"imported")=="yes"; m_curString=""; } void endIncludes() { m_curIncludes.text = m_curString; if (m_state==InFile && m_curCompound) { TagFileInfo::get(m_curCompound)->includes.push_back(m_curIncludes); } else { warn("Unexpected tag 'includes' found"); } } void endTemplateArg() { if (m_state==InClass && m_curCompound) { TagClassInfo::get(m_curCompound)->templateArguments.push_back(m_curString.str()); } else { warn("Unexpected tag 'templarg' found"); } } void endFilename() { switch (m_state) { case InClass: case InConcept: case InNamespace: case InFile: case InGroup: case InPage: case InPackage: case InDir: m_curCompound->filename = m_curString; break; default: warn("Unexpected tag 'filename' found"); break; } } void endPath() { switch (m_state) { case InFile: TagFileInfo::get(m_curCompound)->path = m_curString; break; case InDir: TagDirInfo::get(m_curCompound)->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) { TagClassInfo::get(m_curCompound)->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) { TagClassInfo::get(m_curCompound)->clangId = m_curString; } else if (m_state==InNamespace) { TagNamespaceInfo::get(m_curCompound)->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: TagGroupInfo::get(m_curCompound)->title = m_curString; break; case InPage: TagPageInfo::get(m_curCompound)->title = m_curString; break; default: warn("Unexpected tag 'title' found"); break; } } void endSubgroup() { if (m_state==InGroup) { TagGroupInfo::get(m_curCompound)->subgroupList.push_back(m_curString.str()); } else { warn("Unexpected tag 'subgroup' found"); } } void startIgnoreElement(const XMLHandlers::Attributes& ) { } void endIgnoreElement() { } void buildMemberList(const std::shared_ptr &ce,const std::vector &members); void addDocAnchors(const std::shared_ptr &e,const std::vector &l); enum State { Invalid, InClass, InConcept, InFile, InNamespace, InGroup, InPage, InMember, InEnumValue, InPackage, InDir, InTempArgList }; private: void warn(const char *fmt) { QCString fileName = m_locator->fileName(); ::warn(fileName,m_locator->lineNr(),"%s", fmt); } void warn(const char *fmt,const char *s) { QCString fileName = m_locator->fileName(); ::warn(fileName,m_locator->lineNr(),fmt,s); } //------------------------------------ std::vector< std::unique_ptr > m_tagFileCompounds; std::unique_ptr m_curCompound; TagMemberInfo m_curMember; TagEnumValueInfo m_curEnumValue; TagIncludeInfo m_curIncludes; QCString m_curString; QCString m_tagName; QCString m_fileName; QCString m_title; State m_state = Invalid; std::stack m_stateStack; const XMLLocator *m_locator = nullptr; }; //--------------------------------------------------------------------------------------------------------------- struct ElementCallbacks { using StartCallback = std::function; using EndCallback = std::function; StartCallback startCb; EndCallback endCb; }; ElementCallbacks::StartCallback startCb(void (TagFileParser::*fn)(const XMLHandlers::Attributes &)) { return [fn](TagFileParser &parser,const XMLHandlers::Attributes &attr) { (parser.*fn)(attr); }; } ElementCallbacks::EndCallback endCb(void (TagFileParser::*fn)()) { return [fn](TagFileParser &parser) { (parser.*fn)(); }; } static const std::map< std::string, ElementCallbacks > g_elementHandlers = { // name, start element callback, end element callback { "compound", { startCb(&TagFileParser::startCompound ), endCb(&TagFileParser::endCompound ) } }, { "member", { startCb(&TagFileParser::startMember ), endCb(&TagFileParser::endMember ) } }, { "enumvalue", { startCb(&TagFileParser::startEnumValue ), endCb(&TagFileParser::endEnumValue ) } }, { "name", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endName ) } }, { "base", { startCb(&TagFileParser::startBase ), endCb(&TagFileParser::endBase ) } }, { "filename", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endFilename ) } }, { "includes", { startCb(&TagFileParser::startIncludes ), endCb(&TagFileParser::endIncludes ) } }, { "path", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endPath ) } }, { "anchorfile", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endAnchorFile ) } }, { "anchor", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endAnchor ) } }, { "clangid", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endClangId ) } }, { "arglist", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endArglist ) } }, { "title", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endTitle ) } }, { "subgroup", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endSubgroup ) } }, { "class", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endClass ) } }, { "concept", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endConcept ) } }, { "namespace", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endNamespace ) } }, { "file", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endFile ) } }, { "dir", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endDir ) } }, { "page", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endPage ) } }, { "docanchor", { startCb(&TagFileParser::startDocAnchor ), endCb(&TagFileParser::endDocAnchor ) } }, { "tagfile", { startCb(&TagFileParser::startIgnoreElement), endCb(&TagFileParser::endIgnoreElement) } }, { "templarg", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endTemplateArg ) } }, { "type", { startCb(&TagFileParser::startStringValue ), endCb(&TagFileParser::endType ) } } }; //--------------------------------------------------------------------------------------------------------------- struct CompoundFactory { using CreateFunc = std::function()>; CompoundFactory(TagFileParser::State s,CreateFunc f) : state(s), make_instance(f) {} TagFileParser::State state; CreateFunc make_instance; }; static const std::map< std::string, CompoundFactory > g_compoundFactory = { // kind tag state creation function { "class", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Class); } } }, { "struct", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Struct); } } }, { "union", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Union); } } }, { "interface", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Interface); } } }, { "enum", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Enum); } } }, { "exception", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Exception); } } }, { "protocol", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Protocol); } } }, { "category", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Category); } } }, { "service", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Service); } } }, { "singleton", { TagFileParser::InClass, []() { return std::make_unique(TagClassInfo::Kind::Singleton); } } }, { "file", { TagFileParser::InFile, []() { return std::make_unique(); } } }, { "namespace", { TagFileParser::InNamespace, []() { return std::make_unique(); } } }, { "concept", { TagFileParser::InConcept, []() { return std::make_unique(); } } }, { "group", { TagFileParser::InGroup, []() { return std::make_unique(); } } }, { "page", { TagFileParser::InPage, []() { return std::make_unique(); } } }, { "package", { TagFileParser::InPackage, []() { return std::make_unique(); } } }, { "dir", { TagFileParser::InDir, []() { return std::make_unique(); } } } }; //--------------------------------------------------------------------------------------------------------------- void TagFileParser::startElement( const QCString &name, const XMLHandlers::Attributes& attrib ) { //printf("startElement '%s'\n",qPrint(name)); auto it = g_elementHandlers.find(name.str()); if (it!=std::end(g_elementHandlers)) { it->second.startCb(*this,attrib); } else { warn("Unknown start tag '%s' found!",qPrint(name)); } } void TagFileParser::endElement( const QCString &name ) { //printf("endElement '%s'\n",qPrint(name)); auto it = g_elementHandlers.find(name.str()); if (it!=std::end(g_elementHandlers)) { it->second.endCb(*this); } else { warn("Unknown end tag '%s' found!",qPrint(name)); } } void TagFileParser::startCompound( const XMLHandlers::Attributes& attrib ) { m_curString = ""; std::string kind = XMLHandlers::value(attrib,"kind"); std::string isObjC = XMLHandlers::value(attrib,"objc"); auto it = g_compoundFactory.find(kind); if (it!=g_compoundFactory.end()) { m_curCompound = it->second.make_instance(); m_state = it->second.state; m_curCompound->lineNr = m_locator->lineNr(); } else { warn("Unknown compound attribute '%s' found!",kind.c_str()); m_state = Invalid; } if (isObjC=="yes" && m_curCompound && m_curCompound->compoundType()==TagCompoundInfo::CompoundType::Class) { TagClassInfo::get(m_curCompound)->isObjC = TRUE; } } /*! Dumps the internal structures. For debugging only! */ void TagFileParser::dump() { msg("Result:\n"); //============== CLASSES for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Class) { const TagClassInfo *cd = TagClassInfo::get(comp); msg("class '%s'\n",qPrint(cd->name)); msg(" filename '%s'\n",qPrint(cd->filename)); for (const BaseInfo &bi : cd->bases) { msg( " base: %s \n", bi.name.isEmpty() ? "" : qPrint(bi.name) ); } for (const auto &md : cd->members) { msg(" member:\n"); msg(" kind: '%s'\n",qPrint(md.kind)); msg(" name: '%s'\n",qPrint(md.name)); msg(" anchor: '%s'\n",qPrint(md.anchor)); msg(" arglist: '%s'\n",qPrint(md.arglist)); } } } //============== CONCEPTS for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Concept) { const TagConceptInfo *cd = TagConceptInfo::get(comp); msg("concept '%s'\n",qPrint(cd->name)); msg(" filename '%s'\n",qPrint(cd->filename)); } } //============== NAMESPACES for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Namespace) { const TagNamespaceInfo *nd = TagNamespaceInfo::get(comp); msg("namespace '%s'\n",qPrint(nd->name)); msg(" filename '%s'\n",qPrint(nd->filename)); for (const auto &cls : nd->classList) { msg( " class: %s \n", cls.c_str() ); } for (const auto &md : nd->members) { msg(" member:\n"); msg(" kind: '%s'\n",qPrint(md.kind)); msg(" name: '%s'\n",qPrint(md.name)); msg(" anchor: '%s'\n",qPrint(md.anchor)); msg(" arglist: '%s'\n",qPrint(md.arglist)); } } } //============== FILES for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::File) { const TagFileInfo *fd = TagFileInfo::get(comp); msg("file '%s'\n",qPrint(fd->name)); msg(" filename '%s'\n",qPrint(fd->filename)); for (const auto &ns : fd->namespaceList) { msg( " namespace: %s \n", ns.c_str() ); } for (const auto &cs : fd->classList) { msg( " class: %s \n", cs.c_str() ); } for (const auto &md : fd->members) { msg(" member:\n"); msg(" kind: '%s'\n",qPrint(md.kind)); msg(" name: '%s'\n",qPrint(md.name)); msg(" anchor: '%s'\n",qPrint(md.anchor)); msg(" arglist: '%s'\n",qPrint(md.arglist)); } for (const auto &ii : fd->includes) { msg(" includes id: %s name: %s\n",qPrint(ii.id),qPrint(ii.name)); } } } //============== GROUPS for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Group) { const TagGroupInfo *gd = TagGroupInfo::get(comp); msg("group '%s'\n",qPrint(gd->name)); msg(" filename '%s'\n",qPrint(gd->filename)); for (const auto &ns : gd->namespaceList) { msg( " namespace: %s \n", ns.c_str() ); } for (const auto &cs : gd->classList) { msg( " class: %s \n", cs.c_str() ); } for (const auto &fi : gd->fileList) { msg( " file: %s \n", fi.c_str() ); } for (const auto &sg : gd->subgroupList) { msg( " subgroup: %s \n", sg.c_str() ); } for (const auto &pg : gd->pageList) { msg( " page: %s \n", pg.c_str() ); } for (const auto &md : gd->members) { msg(" member:\n"); msg(" kind: '%s'\n",qPrint(md.kind)); msg(" name: '%s'\n",qPrint(md.name)); msg(" anchor: '%s'\n",qPrint(md.anchor)); msg(" arglist: '%s'\n",qPrint(md.arglist)); } } } //============== PAGES for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Page) { const TagPageInfo *pd = TagPageInfo::get(comp); msg("page '%s'\n",qPrint(pd->name)); msg(" title '%s'\n",qPrint(pd->title)); msg(" filename '%s'\n",qPrint(pd->filename)); } } //============== DIRS for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Dir) { const TagDirInfo *dd = TagDirInfo::get(comp); { msg("dir '%s'\n",qPrint(dd->name)); msg(" path '%s'\n",qPrint(dd->path)); for (const auto &fi : dd->fileList) { msg( " file: %s \n", fi.c_str() ); } for (const auto &sd : dd->subdirList) { msg( " subdir: %s \n", sd.c_str() ); } } } } } void TagFileParser::addDocAnchors(const std::shared_ptr &e,const std::vector &l) { for (const auto &ta : l) { if (SectionManager::instance().find(QCString(ta.label))==0) { //printf("New sectionInfo file=%s anchor=%s\n", // qPrint(ta->fileName),qPrint(ta->label)); 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",qPrint(ta.label)); } } } void TagFileParser::buildMemberList(const std::shared_ptr &ce,const std::vector &members) { for (const auto &tmi : members) { 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.size()>0) { me->spec |= Entry::Strong; for (const auto &evi : tmi.enumValues) { 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; me->startLine = tmi.lineNr; 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); } } /*! 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 for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Class) { const TagClassInfo *tci = TagClassInfo::get(comp); std::shared_ptr ce = std::make_shared(); ce->section = Entry::CLASS_SEC; switch (tci->kind) { case TagClassInfo::Kind::Class: break; case TagClassInfo::Kind::Struct: ce->spec = Entry::Struct; break; case TagClassInfo::Kind::Union: ce->spec = Entry::Union; break; case TagClassInfo::Kind::Interface: ce->spec = Entry::Interface; break; case TagClassInfo::Kind::Enum: ce->spec = Entry::Enum; break; case TagClassInfo::Kind::Exception: ce->spec = Entry::Exception; break; case TagClassInfo::Kind::Protocol: ce->spec = Entry::Protocol; break; case TagClassInfo::Kind::Category: ce->spec = Entry::Category; break; case TagClassInfo::Kind::Service: ce->spec = Entry::Service; break; case TagClassInfo::Kind::Singleton: ce->spec = Entry::Singleton; break; case TagClassInfo::Kind::None: // should never happen, means not properly initialized assert(tci->kind != TagClassInfo::Kind::None); break; } ce->name = tci->name; if (tci->kind==TagClassInfo::Kind::Protocol) { ce->name+="-p"; } addDocAnchors(ce,tci->docAnchors); ce->tagInfoData.tagName = m_tagName; ce->tagInfoData.anchor = tci->anchor; ce->tagInfoData.fileName = tci->filename; ce->startLine = tci->lineNr; ce->hasTagInfo = TRUE; ce->id = tci->clangId; ce->lang = tci->isObjC ? SrcLangExt_ObjC : SrcLangExt_Unknown; // transfer base class list ce->extends = tci->bases; if (!tci->templateArguments.empty()) { ArgumentList al; for (const auto &argName : tci->templateArguments) { Argument a; a.type = "class"; a.name = argName.c_str(); al.push_back(a); } ce->tArgLists.push_back(al); } buildMemberList(ce,tci->members); root->moveToSubEntryAndKeep(ce); } } // build file list for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::File) { const TagFileInfo *tfi = TagFileInfo::get(comp); 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; fe->startLine = tfi->lineNr; //printf("createFileDef() filename=%s\n",qPrint(tfi->filename)); QCString tagid = m_tagName+":"+tfi->path; std::unique_ptr fd { createFileDef(tagid, 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 concept list for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Concept) { const TagConceptInfo *tci = TagConceptInfo::get(comp); std::shared_ptr ce = std::make_shared(); ce->section = Entry::CONCEPT_SEC; ce->name = tci->name; addDocAnchors(ce,tci->docAnchors); ce->tagInfoData.tagName = m_tagName; ce->tagInfoData.fileName = tci->filename; ce->startLine = tci->lineNr; ce->hasTagInfo = TRUE; ce->id = tci->clangId; root->moveToSubEntryAndKeep(ce); } } // build namespace list for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Namespace) { const TagNamespaceInfo *tni = TagNamespaceInfo::get(comp); 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->startLine = tni->lineNr; ne->hasTagInfo = TRUE; ne->id = tni->clangId; buildMemberList(ne,tni->members); root->moveToSubEntryAndKeep(ne); } } // build package list for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Package) { const TagPackageInfo *tpgi = TagPackageInfo::get(comp); 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->startLine = tpgi->lineNr; pe->hasTagInfo = TRUE; buildMemberList(pe,tpgi->members); root->moveToSubEntryAndKeep(pe); } } // build group list for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Group) { const TagGroupInfo *tgi = TagGroupInfo::get(comp); 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->startLine = tgi->lineNr; ge->hasTagInfo = TRUE; buildMemberList(ge,tgi->members); root->moveToSubEntryAndKeep(ge); } } for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Group) { const TagGroupInfo *tgi = TagGroupInfo::get(comp); // set subgroup relations bug_774118 for (const auto &sg : tgi->subgroupList) { const auto &children = root->children(); auto i = std::find_if(children.begin(),children.end(), [&](const std::shared_ptr &e) { return e->name == sg.c_str(); }); if (i!=children.end()) { (*i)->groups.push_back(Grouping(tgi->name,Grouping::GROUPING_INGROUP)); } } } } // build page list for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::Page) { const TagPageInfo *tpi = TagPageInfo::get(comp); 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->startLine = tpi->lineNr; pe->hasTagInfo = TRUE; root->moveToSubEntryAndKeep(pe); } } } void TagFileParser::addIncludes() { for (const auto &comp : m_tagFileCompounds) { if (comp->compoundType()==TagCompoundInfo::CompoundType::File) { const TagFileInfo *tfi = TagFileInfo::get(comp); //printf("tag file tagName=%s path=%s name=%s\n",qPrint(m_tagName),qPrint(tfi->path),qPrint(tfi->name)); FileName *fn = Doxygen::inputNameLinkedMap->find(tfi->name); if (fn) { for (const auto &fd : *fn) { //printf("input file path=%s name=%s\n",qPrint(fd->getPath()),qPrint(fd->name())); if (fd->getPath()==QCString(m_tagName+":"+tfi->path)) { //printf("found\n"); for (const auto &ii : tfi->includes) { //printf("ii->name='%s'\n",qPrint(ii->name)); 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", // qPrint(ifd->getOutputFileBase()),qPrint(ii->id)); 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) { TagFileParser tagFileParser(fullName); QCString inputStr = fileToString(fullName); XMLHandlers handlers; // connect the generic events handlers of the XML parser to the specific handlers of the tagFileParser object handlers.startDocument = [&tagFileParser]() { tagFileParser.startDocument(); }; handlers.startElement = [&tagFileParser](const std::string &name,const XMLHandlers::Attributes &attrs) { tagFileParser.startElement(QCString(name),attrs); }; handlers.endElement = [&tagFileParser](const std::string &name) { tagFileParser.endElement(QCString(name)); }; handlers.characters = [&tagFileParser](const std::string &chars) { tagFileParser.characters(QCString(chars)); }; handlers.error = [&tagFileParser](const std::string &fileName,int lineNr,const std::string &msg) { tagFileParser.error(QCString(fileName),lineNr,QCString(msg)); }; XMLParser parser(handlers); tagFileParser.setDocumentLocator(&parser); parser.parse(fullName,inputStr.data(),Debug::isFlagSet(Debug::Lex)); tagFileParser.buildLists(root); tagFileParser.addIncludes(); //tagFileParser.dump(); }