From 784a67d23ff81275c95af4199179da094953be2e Mon Sep 17 00:00:00 2001 From: Dimitri van Heesch Date: Mon, 26 Aug 2013 10:18:59 +0200 Subject: Added rudimentary support for django like template system for output creation. --- src/classdef.cpp | 49 +- src/classdef.h | 8 + src/context.cpp | 2442 ++++++++++++++++++++++++++++++++++++++++ src/context.h | 535 +++++++++ src/definition.cpp | 2 +- src/definition.h | 2 +- src/dirdef.cpp | 8 +- src/dirdef.h | 1 + src/doxygen.cpp | 21 + src/filedef.cpp | 6 + src/filedef.h | 2 + src/fortranscanner.l | 0 src/ftextstream.h | 4 +- src/index.cpp | 77 +- src/libdoxygen.pro.in | 8 +- src/namespacedef.cpp | 46 +- src/namespacedef.h | 2 + src/template.cpp | 2968 +++++++++++++++++++++++++++++++++++++++++++++++++ src/template.h | 395 +++++++ src/util.cpp | 69 ++ src/util.h | 5 + 21 files changed, 6532 insertions(+), 118 deletions(-) create mode 100644 src/context.cpp create mode 100644 src/context.h mode change 100755 => 100644 src/fortranscanner.l create mode 100644 src/template.cpp create mode 100644 src/template.h diff --git a/src/classdef.cpp b/src/classdef.cpp index eab6b8e..c097aee 100644 --- a/src/classdef.cpp +++ b/src/classdef.cpp @@ -916,9 +916,15 @@ static void writeTemplateSpec(OutputList &ol,Definition *d, } } +bool ClassDef::hasBriefDescription() const +{ + static bool briefMemberDesc = Config_getBool("BRIEF_MEMBER_DESC"); + return !briefDescription().isEmpty() && briefMemberDesc; +} + void ClassDef::writeBriefDescription(OutputList &ol,bool exampleFlag) { - if (!briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC")) + if (hasBriefDescription()) { ol.startParagraph(); ol.generateDoc(briefFile(),briefLine(),this,0, @@ -929,10 +935,7 @@ void ClassDef::writeBriefDescription(OutputList &ol,bool exampleFlag) ol.enable(OutputGenerator::RTF); ol.popGeneratorState(); - if (Config_getBool("REPEAT_BRIEF") || - !documentation().isEmpty() || - exampleFlag - ) + if (hasDetailedDescription() || exampleFlag) { writeMoreLink(ol,anchor()); } @@ -990,14 +993,20 @@ void ClassDef::writeDetailedDocumentationBody(OutputList &ol) ol.endTextBlock(); } +bool ClassDef::hasDetailedDescription() const +{ + static bool repeatBrief = Config_getBool("REPEAT_BRIEF"); + static bool sourceBrowser = Config_getBool("SOURCE_BROWSER"); + return ((!briefDescription().isEmpty() && repeatBrief) || + !documentation().isEmpty() || + (sourceBrowser && getStartBodyLine()!=-1 && getBodyDef())); +} + // write the detailed description for this class void ClassDef::writeDetailedDescription(OutputList &ol, const QCString &/*pageType*/, bool exampleFlag, const QCString &title,const QCString &anchor) { - if ((!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF")) || - !documentation().isEmpty() || - (Config_getBool("SOURCE_BROWSER") && getStartBodyLine()!=-1 && getBodyDef()) || - exampleFlag) + if (hasDetailedDescription() || exampleFlag) { ol.pushGeneratorState(); ol.disable(OutputGenerator::Html); @@ -1990,12 +1999,8 @@ void ClassDef::writeDocumentationContents(OutputList &ol,const QCString & /*page ol.endContents(); } -// write all documentation for this class -void ClassDef::writeDocumentation(OutputList &ol) +QCString ClassDef::title() const { - static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW"); - //static bool fortranOpt = Config_getBool("OPTIMIZE_FOR_FORTRAN"); - //static bool vhdlOpt = Config_getBool("OPTIMIZE_OUTPUT_VHDL"); QCString pageTitle; SrcLangExt lang = getLanguage(); @@ -2027,7 +2032,17 @@ void ClassDef::writeDocumentation(OutputList &ol) m_impl->compType == Interface && getLanguage()==SrcLangExt_ObjC ? Class : m_impl->compType, m_impl->tempArgs != 0); } - + return pageTitle; +} + +// write all documentation for this class +void ClassDef::writeDocumentation(OutputList &ol) +{ + static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW"); + //static bool fortranOpt = Config_getBool("OPTIMIZE_FOR_FORTRAN"); + //static bool vhdlOpt = Config_getBool("OPTIMIZE_OUTPUT_VHDL"); + QCString pageTitle = title(); + startFile(ol,getOutputFileBase(),name(),pageTitle,HLI_ClassVisible,!generateTreeView); if (!generateTreeView) { @@ -4723,4 +4738,8 @@ bool ClassDef::isExtension() const return b; } +const ClassSDict *ClassDef::innerClasses() const +{ + return m_impl->innerClasses; +} diff --git a/src/classdef.h b/src/classdef.h index 494b8f2..537ff1f 100644 --- a/src/classdef.h +++ b/src/classdef.h @@ -125,6 +125,12 @@ class ClassDef : public Definition /** returns TRUE if this class has documentation */ bool hasDocumentation() const; + /** returns TRUE if this class has a brief description */ + bool hasBriefDescription() const; + + /** returns TRUE if this class has a non-empty detailed description */ + bool hasDetailedDescription() const; + /** Returns the name as it is appears in the documentation */ QCString displayName(bool includeScope=TRUE) const; @@ -301,6 +307,8 @@ class ClassDef : public Definition bool isJavaEnum() const; bool isGeneric() const; + const ClassSDict *innerClasses() const; + QCString title() const; //----------------------------------------------------------------------------------- // --- setters ---- diff --git a/src/context.cpp b/src/context.cpp new file mode 100644 index 0000000..c5b6903 --- /dev/null +++ b/src/context.cpp @@ -0,0 +1,2442 @@ +#include "context.h" +#include "config.h" +#include "index.h" +#include "classlist.h" +#include "doxygen.h" +#include "namespacedef.h" +#include "filedef.h" +#include "pagedef.h" +#include "groupdef.h" +#include "util.h" +#include "version.h" +#include "language.h" +#include "message.h" +#include "vhdldocgen.h" +#include "filename.h" +#include "dirdef.h" +#include "docparser.h" +#include "htmlgen.h" +#include "htmldocvisitor.h" + +// iterator support +template +class GenericConstIterator : public TemplateListIntf::ConstIterator +{ + public: + GenericConstIterator(const QList &list) + : m_it(list) { } + virtual ~GenericConstIterator() {} + void toFirst() + { + m_it.toFirst(); + } + void toLast() + { + m_it.toLast(); + } + void toNext() + { + if (m_it.current()) ++m_it; + } + void toPrev() + { + if (m_it.current()) --m_it; + } + bool current(TemplateVariant &v) const + { + if (m_it.current()) + { + v = m_it.current(); + return TRUE; + } + else + { + v = TemplateVariant(); + return FALSE; + } + } + private: + QListIterator m_it; +}; + +//------------------------------------------------------------------------ + +// standard list implementation +template +class GenericNodeListContext : public TemplateListIntf +{ + public: + GenericNodeListContext() + { + m_children.setAutoDelete(TRUE); + } + + // TemplateListIntf methods + int count() const + { + return (int)m_children.count(); + } + TemplateVariant at(int index) const + { + TemplateVariant result; + if (index>=0 && index(m_children); + } + + void append(T *ctn) + { + m_children.append(ctn); + } + bool isEmpty() const + { + return m_children.isEmpty(); + } + private: + mutable QList m_children; +}; + +//------------------------------------------------------------------------ + +/** @brief Helper class to map a property name to a handler member function */ +template +class PropertyMapper +{ + public: + struct PropertyFunc + { + typedef TemplateVariant (T::*Handler)() const; + PropertyFunc(const T *o,Handler h) : obj(o), handler(h) {} + TemplateVariant operator()() const + { + return (obj->*handler)(); + } + const T *obj; + Handler handler; + }; + PropertyMapper() { m_map.setAutoDelete(TRUE); } + TemplateVariant get(const char *n) + { + //printf("PropertyMapper::get(%s)\n",n); + TemplateVariant result; + PropertyFunc *func = m_map.find(n); + if (func) + { + result = (*func)(); + } + return result; + } + void insert(const char *name,const PropertyFunc *func) + { + m_map.insert(name,func); + } + private: + QDict m_map; +}; + + +//------------------------------------------------------------------------ + +//%% struct Config : configuration options +//%% { +class ConfigContext::Private +{ + public: + Private() { cachedLists.setAutoDelete(TRUE); } + TemplateVariant fetchList(const QCString &name,const QStrList *list) + { + TemplateList *tlist = cachedLists.find(name); + if (tlist==0) + { + tlist = new TemplateList; + cachedLists.insert(name,tlist); + QStrListIterator li(*list); + char *s; + for (li.toFirst();(s=li.current());++li) + { + tlist->append(s); + } + } + return tlist; + } + private: + QDict cachedLists; +}; +//%% } + +ConfigContext::ConfigContext() +{ + p = new Private; +} + +ConfigContext::~ConfigContext() +{ + delete p; +} + +TemplateVariant ConfigContext::get(const char *name) const +{ + TemplateVariant result; + if (name) + { + ConfigOption *option = Config::instance()->get(name); + if (option) + { + switch (option->kind()) + { + case ConfigOption::O_Bool: + return TemplateVariant(*((ConfigBool*)option)->valueRef()); + case ConfigOption::O_Int: + return TemplateVariant(*((ConfigInt*)option)->valueRef()); + case ConfigOption::O_Enum: + return TemplateVariant(*((ConfigEnum*)option)->valueRef()); + case ConfigOption::O_String: + return TemplateVariant(*((ConfigString*)option)->valueRef()); + case ConfigOption::O_List: + return p->fetchList(name,((ConfigList*)option)->valueRef()); + break; + default: + break; + } + } + } + return result; +} + +//------------------------------------------------------------------------ + +//%% struct Doxygen: global information +//%% { +class DoxygenContext::Private : public PropertyMapper +{ + public: + TemplateVariant version() const + { + return versionString; + } + TemplateVariant date() const + { + return TemplateVariant(dateToString(TRUE)); + } + Private() + { + //%% string version + insert("version",new PropertyFunc(this,&Private::version)); + //%% string date + insert("date", new PropertyFunc(this,&Private::date)); + } +}; +//%% } + +DoxygenContext::DoxygenContext() +{ + p = new Private; +} + +DoxygenContext::~DoxygenContext() +{ + delete p; +} + +TemplateVariant DoxygenContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% struct Translator: translation methods +//%% { +class TranslateContext::Private : public PropertyMapper +{ + public: + static QCString generatedAtFunc(const void *obj,const QValueList &args) + { + return ((TranslateContext::Private*)obj)->generatedAt(args); + } + QCString generatedAt(const QValueList &args) const + { + if (args.count()==2) + { + return theTranslator->trGeneratedAt(args[0].toString(),args[1].toString()); + } + else + { + err("tr.generateAt should take two parameters!\n"); + } + return QCString(); + } + + TemplateVariant generatedBy() const + { + return theTranslator->trGeneratedBy(); + } + TemplateVariant generatedAt() const + { + return TemplateVariant(this,&Private::generatedAtFunc); + } + TemplateVariant search() const + { + return theTranslator->trSearch(); + } + TemplateVariant mainPage() const + { + return theTranslator->trMainPage(); + } + TemplateVariant classes() const + { + return theTranslator->trClasses(); + } + TemplateVariant classList() const + { + return theTranslator->trCompoundList(); + } + TemplateVariant classIndex() const + { + return theTranslator->trCompoundIndex(); + } + TemplateVariant classHierarchy() const + { + return theTranslator->trClassHierarchy(); + } + TemplateVariant classMembers() const + { + return theTranslator->trCompoundMembers(); + } + TemplateVariant modules() const + { + return theTranslator->trModules(); + } + TemplateVariant namespaces() const + { + if (m_javaOpt || m_vhdlOpt) + { + return theTranslator->trPackages(); + } + else if (m_fortranOpt) + { + return theTranslator->trModules(); + } + else + { + return theTranslator->trNamespaces(); + } + } + TemplateVariant files() const + { + return theTranslator->trFile(TRUE,FALSE); + } + TemplateVariant pages() const + { + return theTranslator->trRelatedPages(); + } + TemplateVariant examples() const + { + return theTranslator->trExamples(); + } + TemplateVariant namespaceList() const + { + if (m_javaOpt || m_vhdlOpt) + { + return theTranslator->trPackages(); + } + else if (m_fortranOpt) + { + return theTranslator->trModulesList(); + } + else + { + return theTranslator->trNamespaceList(); + } + } + TemplateVariant namespaceMembers() const + { + if (m_javaOpt || m_vhdlOpt) + { + return theTranslator->trPackageMembers(); + } + else if (m_fortranOpt) + { + return theTranslator->trModulesMembers(); + } + else + { + return theTranslator->trNamespaceMembers(); + } + } + TemplateVariant fileList() const + { + return theTranslator->trFileList(); + } + TemplateVariant fileMembers() const + { + return theTranslator->trFileMembers(); + } + TemplateVariant relatedPagesDesc() const + { + return theTranslator->trRelatedPagesDescription(); + } + TemplateVariant more() const + { + return theTranslator->trMore(); + } + TemplateVariant detailedDesc() const + { + return theTranslator->trDetailedDescription(); + } + Private() + { + //%% string generatedBy + insert("generatedby", new PropertyFunc(this,&Private::generatedBy)); + //%% string generatedAt + insert("generatedAt", new PropertyFunc(this,&Private::generatedAt)); + //%% string search + insert("search", new PropertyFunc(this,&Private::search)); + //%% string mainPage + insert("mainPage", new PropertyFunc(this,&Private::mainPage)); + //%% string classes + insert("classes", new PropertyFunc(this,&Private::classes)); + //%% string classList + insert("classList", new PropertyFunc(this,&Private::classList)); + //%% string classIndex + insert("classIndex", new PropertyFunc(this,&Private::classIndex)); + //%% string classHierarchy + insert("classHierarchy", new PropertyFunc(this,&Private::classHierarchy)); + //%% string classMembers + insert("classMembers", new PropertyFunc(this,&Private::classMembers)); + //%% string modules + insert("modules", new PropertyFunc(this,&Private::modules)); + //%% string namespaces + insert("namespaces", new PropertyFunc(this,&Private::namespaces)); + //%% string files + insert("files", new PropertyFunc(this,&Private::files)); + //%% string pages + insert("pages", new PropertyFunc(this,&Private::pages)); + //%% string examples + insert("examples", new PropertyFunc(this,&Private::examples)); + //%% string namespaceList + insert("namespaceList", new PropertyFunc(this,&Private::namespaceList)); + //%% string namespaceMembers + insert("namespaceMembers",new PropertyFunc(this,&Private::namespaceMembers)); + //%% srting fileList + insert("fileList", new PropertyFunc(this,&Private::fileList)); + //%% string fileMembers + insert("fileMembers", new PropertyFunc(this,&Private::fileMembers)); + //%% string relatedPagesDescripiton + insert("relatedPagesDesc",new PropertyFunc(this,&Private::relatedPagesDesc)); + //%% string more + insert("more", new PropertyFunc(this,&Private::more)); + //%% string detailedDescription + insert("detailedDesc", new PropertyFunc(this,&Private::detailedDesc)); + + m_javaOpt = Config_getBool("OPTIMIZE_OUTPUT_JAVA"); + m_fortranOpt = Config_getBool("OPTIMIZE_FOR_FORTRAN"); + m_vhdlOpt = Config_getBool("OPTIMIZE_OUTPUT_VHDL"); + } + private: + bool m_javaOpt; + bool m_fortranOpt; + bool m_vhdlOpt; +}; +//%% } + +TranslateContext::TranslateContext() +{ + p = new Private; +} + +TranslateContext::~TranslateContext() +{ + delete p; +} + +TemplateVariant TranslateContext::get(const char *n) const +{ + return p->get(n); +} + +static TemplateVariant parseDoc(Definition *def,const QCString &file,int line, + const QCString &relPath,const QCString &docStr,bool isBrief) +{ + TemplateVariant result; + DocRoot *root = validatingParseDoc(file,line,def,0,docStr,TRUE,FALSE,0,isBrief,FALSE); + QGString docs; + { + FTextStream ts(&docs); + // TODO: support other generators + HtmlCodeGenerator codeGen(ts,relPath); + HtmlDocVisitor visitor(ts,codeGen,def); + root->accept(&visitor); + } + bool isEmpty = root->isEmpty(); + if (isEmpty) + result = ""; + else + result = TemplateVariant(docs); + result.setRaw(TRUE); + delete root; + return result; +} + +//------------------------------------------------------------------------ + +//%% struct Symbol: shared info for all symbols +//%% { +template +class DefinitionContext : public PropertyMapper +{ + public: + DefinitionContext(const T *super,Definition *d) : m_def(d), m_detailsCached(FALSE) + { + //%% string name: the name of the symbol + PropertyMapper::insert("name", new typename PropertyMapper::PropertyFunc(super,&DefinitionContext::name)); + //%% string relPath: the relative path to the root of the output (CREATE_SUBDIRS) + PropertyMapper::insert("relPath", new typename PropertyMapper::PropertyFunc(super,&DefinitionContext::relPath)); + //%% string fileName: the file name of the output file associated with the symbol (without extension) + PropertyMapper::insert("fileName", new typename PropertyMapper::PropertyFunc(super,&DefinitionContext::fileName)); + //%% string details: the detailed documentation for this symbol + PropertyMapper::insert("details", new typename PropertyMapper::PropertyFunc(super,&DefinitionContext::details)); + //%% string brief: the brief description for this symbol + PropertyMapper::insert("brief", new typename PropertyMapper::PropertyFunc(super,&DefinitionContext::brief)); + } + TemplateVariant fileName() const + { + return TemplateVariant(m_def->getOutputFileBase()); + } + TemplateVariant name() const + { + return m_def->displayName(); + } + TemplateVariant relPath() const + { + static bool createSubdirs = Config_getBool("CREATE_SUBDIRS"); + return createSubdirs ? TemplateVariant("../../") : TemplateVariant(""); + } + TemplateVariant details() const + { + if (!m_detailsCached) + { + m_details = parseDoc(m_def,m_def->docFile(),m_def->docLine(), + relPath().toString(),m_def->documentation(),FALSE); + m_detailsCached = TRUE; + } + return m_details; + } + TemplateVariant brief() const + { + if (!m_briefCached) + { + m_brief = parseDoc(m_def,m_def->briefFile(),m_def->briefLine(), + relPath().toString(),m_def->briefDescription(),TRUE); + m_briefCached = TRUE; + } + return m_brief; + } + + private: + Definition *m_def; + mutable bool m_detailsCached; + mutable TemplateVariant m_details; + mutable bool m_briefCached; + mutable TemplateVariant m_brief; +}; +//%% } + +//------------------------------------------------------------------------ + +//%% struct Class(Symbol): class information +//%% { +class ClassContext::Private : public DefinitionContext +{ + public: + Private(ClassDef *cd) : DefinitionContext(this,cd) , m_classDef(cd) + { + insert("title", new PropertyFunc(this,&Private::title)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight", new PropertyFunc(this,&Private::subHighlight)); + insert("hasBrief", new PropertyFunc(this,&Private::hasBrief)); + insert("hasDetails", new PropertyFunc(this,&Private::hasDetails)); + } + TemplateVariant title() const + { + return TemplateVariant(m_classDef->title()); + } + TemplateVariant highlight() const + { + return TemplateVariant("classes"); + } + TemplateVariant subHighlight() const + { + return TemplateVariant(""); + } + TemplateVariant hasBrief() const + { + return m_classDef->hasBriefDescription(); + } + TemplateVariant hasDetails() const + { + return m_classDef->hasDetailedDescription(); + } + private: + ClassDef *m_classDef; +}; +//%% } + +ClassContext::ClassContext(ClassDef *cd) +{ + p = new Private(cd); +} + +ClassContext::~ClassContext() +{ + delete p; +} + +TemplateVariant ClassContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% struct Namespace(Symbol): namespace information +//%% { +class NamespaceContext::Private : public DefinitionContext +{ + public: + Private(NamespaceDef *nd) : DefinitionContext(this,nd) , m_namespaceDef(nd) + { + insert("title", new PropertyFunc(this,&Private::title)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight", new PropertyFunc(this,&Private::subHighlight)); + } + TemplateVariant title() const + { + return TemplateVariant(m_namespaceDef->title()); + } + TemplateVariant highlight() const + { + return TemplateVariant("namespaces"); + } + TemplateVariant subHighlight() const + { + return TemplateVariant(""); + } + private: + NamespaceDef *m_namespaceDef; +}; +//%% } + +NamespaceContext::NamespaceContext(NamespaceDef *nd) +{ + p = new Private(nd); +} + +NamespaceContext::~NamespaceContext() +{ + delete p; +} + +TemplateVariant NamespaceContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% struct File(Symbol): file information +//%% { +class FileContext::Private : public DefinitionContext +{ + public: + Private(FileDef *fd) : DefinitionContext(this,fd) , m_fileDef(fd) + { + insert("title", new PropertyFunc(this,&Private::title)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight", new PropertyFunc(this,&Private::subHighlight)); + } + TemplateVariant title() const + { + return TemplateVariant(m_fileDef->title()); + } + TemplateVariant highlight() const + { + return TemplateVariant("files"); + } + TemplateVariant subHighlight() const + { + return TemplateVariant(""); + } + private: + FileDef *m_fileDef; +}; +//%% } + +FileContext::FileContext(FileDef *fd) +{ + p = new Private(fd); +} + +FileContext::~FileContext() +{ + delete p; +} + +TemplateVariant FileContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% struct Dir(Symbol): directory information +//%% { +class DirContext::Private : public DefinitionContext +{ + public: + Private(DirDef *dd) : DefinitionContext(this,dd) , m_dirDef(dd) + { + insert("title", new PropertyFunc(this,&Private::title)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight", new PropertyFunc(this,&Private::subHighlight)); + insert("dirName", new PropertyFunc(this,&Private::dirName)); + } + TemplateVariant title() const + { + return TemplateVariant(m_dirDef->shortTitle()); + } + TemplateVariant highlight() const + { + return TemplateVariant("files"); + } + TemplateVariant subHighlight() const + { + return TemplateVariant(""); + } + TemplateVariant dirName() const + { + return TemplateVariant(m_dirDef->shortName()); + } + private: + DirDef *m_dirDef; +}; +//%% } + +DirContext::DirContext(DirDef *fd) +{ + p = new Private(fd); +} + +DirContext::~DirContext() +{ + delete p; +} + +TemplateVariant DirContext::get(const char *n) const +{ + return p->get(n); +} + + +//------------------------------------------------------------------------ + +//%% struct Page(Symbol): page information +//%% { +class PageContext::Private : public DefinitionContext +{ + public: + Private(PageDef *pd) : DefinitionContext(this,pd) , m_pageDef(pd) + { + insert("title", new PropertyFunc(this,&Private::title)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight", new PropertyFunc(this,&Private::subHighlight)); + } + TemplateVariant title() const + { + return TemplateVariant(m_pageDef->title()); + } + TemplateVariant highlight() const + { + return TemplateVariant("pages"); + } + TemplateVariant subHighlight() const + { + return TemplateVariant(""); + } + private: + PageDef *m_pageDef; +}; +//%% } + +PageContext::PageContext(PageDef *pd) +{ + p = new Private(pd); +} + +PageContext::~PageContext() +{ + delete p; +} + +TemplateVariant PageContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% struct Module(Symbol): group information +//%% { +class ModuleContext::Private : public DefinitionContext +{ + public: + Private(GroupDef *gd) : DefinitionContext(this,gd) , m_groupDef(gd) + { + insert("title", new PropertyFunc(this,&Private::title)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight", new PropertyFunc(this,&Private::subHighlight)); + } + TemplateVariant title() const + { + return TemplateVariant(m_groupDef->groupTitle()); + } + TemplateVariant highlight() const + { + return TemplateVariant("modules"); + } + TemplateVariant subHighlight() const + { + return TemplateVariant(""); + } + private: + GroupDef *m_groupDef; +}; +//%% } + +ModuleContext::ModuleContext(GroupDef *gd) +{ + p = new Private(gd); +} + +ModuleContext::~ModuleContext() +{ + delete p; +} + +TemplateVariant ModuleContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% list ClassList[Class] : list of classes +class ClassListContext::Private : public GenericNodeListContext +{ + public: + void addClasses(const ClassSDict &classSDict) + { + ClassSDict::Iterator cli(classSDict); + ClassDef *cd; + for (cli.toFirst() ; (cd=cli.current()) ; ++cli ) + { + if (cd->getLanguage()==SrcLangExt_VHDL && + ((VhdlDocGen::VhdlClasses)cd->protection()==VhdlDocGen::PACKAGECLASS || + (VhdlDocGen::VhdlClasses)cd->protection()==VhdlDocGen::PACKBODYCLASS) + ) // no architecture + { + continue; + } + if (cd->isLinkableInProject() && cd->templateMaster()==0) + { + append(new ClassContext(cd)); + } + } + } +}; + +ClassListContext::ClassListContext() +{ + p = new Private; + p->addClasses(*Doxygen::classSDict); + p->addClasses(*Doxygen::hiddenClasses); +} + +ClassListContext::~ClassListContext() +{ + delete p; +} + +// TemplateListIntf +int ClassListContext::count() const +{ + return p->count(); +} + +TemplateVariant ClassListContext::at(int index) const +{ + return p->at(index); +} + +TemplateListIntf::ConstIterator *ClassListContext::createIterator() const +{ + return p->createIterator(); +} + +//------------------------------------------------------------------------ + +//%% struct ClassInheritanceNode: node in inheritance tree +//%% { +class ClassInheritanceNodeContext::Private : public PropertyMapper +{ + public: + Private(ClassDef *cd) : m_classContext(cd) + { + //%% bool is_leaf_node: true if this node does not have any children + insert("is_leaf_node", new PropertyFunc(this,&Private::isLeafNode)); + //%% ClassInheritance children: list of nested classes/namespaces + insert("children", new PropertyFunc(this,&Private::children)); + //%% Class class: class info + insert("class", new PropertyFunc(this,&Private::getClass)); + } + void addChildren(const BaseClassList *bcl,bool hideSuper) + { + if (bcl==0) return; + BaseClassListIterator bcli(*bcl); + BaseClassDef *bcd; + for (bcli.toFirst() ; (bcd=bcli.current()) ; ++bcli) + { + ClassDef *cd=bcd->classDef; + if (cd->getLanguage()==SrcLangExt_VHDL && (VhdlDocGen::VhdlClasses)cd->protection()!=VhdlDocGen::ENTITYCLASS) + { + continue; + } + + bool b; + if (cd->getLanguage()==SrcLangExt_VHDL) + { + b=hasVisibleRoot(cd->subClasses()); + } + else + { + b=hasVisibleRoot(cd->baseClasses()); + } + + if (cd->isVisibleInHierarchy() && b) // hasVisibleRoot(cd->baseClasses())) + { + bool hasChildren = !cd->visited && !hideSuper && classHasVisibleChildren(cd); + ClassInheritanceNodeContext *tnc = new ClassInheritanceNodeContext(cd); + m_children.append(tnc); + if (hasChildren) + { + //printf("Class %s at %p visited=%d\n",cd->name().data(),cd,cd->visited); + bool wasVisited=cd->visited; + cd->visited=TRUE; + if (cd->getLanguage()==SrcLangExt_VHDL) + { + tnc->addChildren(cd->baseClasses(),wasVisited); + } + else + { + tnc->addChildren(cd->subClasses(),wasVisited); + } + } + } + } + } + TemplateVariant isLeafNode() const + { + return m_children.isEmpty(); + } + TemplateVariant children() const + { + return TemplateVariant(&m_children); + } + TemplateVariant getClass() const + { + return TemplateVariant(&m_classContext); + } + private: + GenericNodeListContext m_children; + ClassContext m_classContext; +}; +//%% } + +ClassInheritanceNodeContext::ClassInheritanceNodeContext(ClassDef *cd) +{ + p = new Private(cd); +} + +ClassInheritanceNodeContext::~ClassInheritanceNodeContext() +{ + delete p; +} + +TemplateVariant ClassInheritanceNodeContext::get(const char *n) const +{ + return p->get(n); +} + +void ClassInheritanceNodeContext::addChildren(const BaseClassList *bcl,bool hideSuper) +{ + p->addChildren(bcl,hideSuper); +} + +//------------------------------------------------------------------------ + +//%% list ClassInheritance[ClassInheritanceNode]: list of classes +class ClassInheritanceContext::Private : public + GenericNodeListContext +{ + public: + void addClasses(const ClassSDict &classSDict) + { + ClassSDict::Iterator cli(classSDict); + ClassDef *cd; + for (cli.toFirst();(cd=cli.current());++cli) + { + bool b; + if (cd->getLanguage()==SrcLangExt_VHDL) + { + if (!(VhdlDocGen::VhdlClasses)cd->protection()==VhdlDocGen::ENTITYCLASS) + { + continue; + } + b=!hasVisibleRoot(cd->subClasses()); + } + else + { + b=!hasVisibleRoot(cd->baseClasses()); + } + if (b) + { + if (cd->isVisibleInHierarchy()) // should it be visible + { + // new root level class + ClassInheritanceNodeContext *tnc = new ClassInheritanceNodeContext(cd); + append(tnc); + bool hasChildren = !cd->visited && classHasVisibleChildren(cd); + if (cd->getLanguage()==SrcLangExt_VHDL && hasChildren) + { + tnc->addChildren(cd->baseClasses(),cd->visited); + cd->visited=TRUE; + } + else if (hasChildren) + { + tnc->addChildren(cd->subClasses(),cd->visited); + cd->visited=TRUE; + } + } + } + } + } +}; + +ClassInheritanceContext::ClassInheritanceContext() +{ + p = new Private; + initClassHierarchy(Doxygen::classSDict); + initClassHierarchy(Doxygen::hiddenClasses); + p->addClasses(*Doxygen::classSDict); + p->addClasses(*Doxygen::hiddenClasses); +} + +ClassInheritanceContext::~ClassInheritanceContext() +{ + delete p; +} + +// TemplateListIntf +int ClassInheritanceContext::count() const +{ + return (int)p->count(); +} + +TemplateVariant ClassInheritanceContext::at(int index) const +{ + TemplateVariant result; + if (index>=0 && indexat(index); + } + return result; +} + +TemplateListIntf::ConstIterator *ClassInheritanceContext::createIterator() const +{ + return p->createIterator(); +} + +//------------------------------------------------------------------------ + +//%% struct ClassHierarchy: inheritance tree +//%% { +class ClassHierarchyContext::Private : public PropertyMapper +{ + public: + TemplateVariant tree() const + { + return TemplateVariant(&m_classTree); + } + TemplateVariant fileName() const + { + return "hierarchy"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "classes"; + } + TemplateVariant subhighlight() const + { + return "classhierarchy"; + } + TemplateVariant title() const + { + static bool vhdlOpt = Config_getBool("OPTIMIZE_OUTPUT_VHDL"); + if (vhdlOpt) + { + return VhdlDocGen::trDesignUnitHierarchy(); + } + else + { + return theTranslator->trClassHierarchy(); + } + } + Private() + { + //%% ClassInheritance tree + insert("tree",new PropertyFunc(this,&Private::tree)); + insert("fileName",new PropertyFunc(this,&Private::fileName)); + insert("relPath",new PropertyFunc(this,&Private::relPath)); + insert("highlight",new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title",new PropertyFunc(this,&Private::title)); + } + private: + ClassInheritanceContext m_classTree; +}; +//%% } + +ClassHierarchyContext::ClassHierarchyContext() +{ + p = new Private; +} + +ClassHierarchyContext::~ClassHierarchyContext() +{ + delete p; +} + +TemplateVariant ClassHierarchyContext::get(const char *name) const +{ + return p->get(name); +} + +//------------------------------------------------------------------------ + +//%% struct NestingNode: node is a nesting relation tree +//%% { +class NestingNodeContext::Private : public PropertyMapper +{ + public: + Private(Definition *d,bool addCls) : m_def(d), + m_classContext(m_def->definitionType()==Definition::TypeClass?(ClassDef*)d:0), + m_namespaceContext(m_def->definitionType()==Definition::TypeNamespace?(NamespaceDef*)d:0) + { + //%% bool is_leaf_node: true if this node does not have any children + insert("is_leaf_node", new PropertyFunc(this,&Private::isLeafNode)); + //%% Nesting children: list of nested classes/namespaces + insert("children", new PropertyFunc(this,&Private::children)); + //%% [optional] Class class: class info (if this node represents a class) + insert("class", new PropertyFunc(this,&Private::getClass)); + //%% [optional] Namespace namespace: namespace info (if this node represents a namespace) + insert("namespace", new PropertyFunc(this,&Private::getNamespace)); + addNamespaces(addCls); + addClasses(); + } + TemplateVariant isLeafNode() const + { + return m_children.count()==0; + } + TemplateVariant children() const + { + return TemplateVariant(&m_children); + } + TemplateVariant getClass() const + { + if (m_def->definitionType()==Definition::TypeClass) + { + return TemplateVariant(&m_classContext); + } + else + { + return TemplateVariant(FALSE); + } + } + TemplateVariant getNamespace() const + { + if (m_def->definitionType()==Definition::TypeNamespace) + { + return TemplateVariant(&m_namespaceContext); + } + else + { + return TemplateVariant(FALSE); + } + } + void addClasses() + { + ClassDef *cd = m_def->definitionType()==Definition::TypeClass ? (ClassDef*)m_def : 0; + if (cd && cd->getClassSDict()) + { + m_children.addClasses(*cd->getClassSDict(),FALSE); + } + } + void addNamespaces(bool addClasses) + { + NamespaceDef *nd = m_def->definitionType()==Definition::TypeNamespace ? (NamespaceDef*)m_def : 0; + if (nd && nd->getNamespaceSDict()) + { + m_children.addNamespaces(*nd->getNamespaceSDict(),FALSE,addClasses); + } + if (addClasses && nd && nd->getClassSDict()) + { + m_children.addClasses(*nd->getClassSDict(),FALSE); + } + } + Definition *m_def; + private: + NestingContext m_children; + ClassContext m_classContext; + NamespaceContext m_namespaceContext; +}; +//%% } + +NestingNodeContext::NestingNodeContext(Definition *d,bool addClass) +{ + p = new Private(d,addClass); +} + +NestingNodeContext::~NestingNodeContext() +{ + delete p; +} + +TemplateVariant NestingNodeContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% list Nesting[NestingNode]: namespace and class nesting relations +class NestingContext::Private : public GenericNodeListContext +{ + public: + void addNamespaces(const NamespaceSDict &nsDict,bool rootOnly,bool addClasses) + { + NamespaceSDict::Iterator nli(nsDict); + NamespaceDef *nd; + for (nli.toFirst();(nd=nli.current());++nli) + { + if (nd->localName().find('@')==-1 && + (!rootOnly || nd->getOuterScope()==Doxygen::globalScope)) + { + bool hasChildren = namespaceHasVisibleChild(nd,addClasses); + bool isLinkable = nd->isLinkableInProject(); + if (isLinkable || hasChildren) + { + NestingNodeContext *nnc = new NestingNodeContext(nd,addClasses); + append(nnc); + } + } + } + } + void addClasses(const ClassSDict &clDict,bool rootOnly) + { + ClassSDict::Iterator cli(clDict); + ClassDef *cd; + for (;(cd=cli.current());++cli) + { + if (cd->getLanguage()==SrcLangExt_VHDL) + { + if ((VhdlDocGen::VhdlClasses)cd->protection()==VhdlDocGen::PACKAGECLASS || + (VhdlDocGen::VhdlClasses)cd->protection()==VhdlDocGen::PACKBODYCLASS + )// no architecture + { + continue; + } + } + if (!rootOnly || + cd->getOuterScope()==0 || + cd->getOuterScope()==Doxygen::globalScope + ) + { + if (classVisibleInIndex(cd) && cd->templateMaster()==0) + { + NestingNodeContext *nnc = new NestingNodeContext(cd,TRUE); + append(nnc); + } + } + } + } +}; + +NestingContext::NestingContext() +{ + p = new Private; +} + +NestingContext::~NestingContext() +{ + delete p; +} + +// TemplateListIntf +int NestingContext::count() const +{ + return p->count(); +} + +TemplateVariant NestingContext::at(int index) const +{ + return p->at(index); +} + +TemplateListIntf::ConstIterator *NestingContext::createIterator() const +{ + return p->createIterator(); +} + +void NestingContext::addClasses(const ClassSDict &clDict,bool rootOnly) +{ + p->addClasses(clDict,rootOnly); +} + +void NestingContext::addNamespaces(const NamespaceSDict &nsDict,bool rootOnly,bool addClasses) +{ + p->addNamespaces(nsDict,rootOnly,addClasses); +} + +//------------------------------------------------------------------------ + +//%% struct ClassTree: Class nesting relations +//%% { +class ClassTreeContext::Private : public PropertyMapper +{ + public: + TemplateVariant tree() const + { + return TemplateVariant(&m_classTree); + } + TemplateVariant fileName() const + { + return "annotated"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "classes"; + } + TemplateVariant subhighlight() const + { + return "classlist"; + } + TemplateVariant title() const + { + static bool fortranOpt = Config_getBool("OPTIMIZE_FOR_FORTRAN"); + static bool vhdlOpt = Config_getBool("OPTIMIZE_OUTPUT_VHDL"); + if (fortranOpt) + { + return theTranslator->trCompoundListFortran(); + } + else if (vhdlOpt) + { + return VhdlDocGen::trDesignUnitList(); + } + else + { + return theTranslator->trClasses(); + } + } + Private() + { + if (Doxygen::namespaceSDict) + { + m_classTree.addNamespaces(*Doxygen::namespaceSDict,TRUE,TRUE); + } + if (Doxygen::classSDict) + { + m_classTree.addClasses(*Doxygen::classSDict,TRUE); + } + //%% Nesting tree + insert("tree", new PropertyFunc(this,&Private::tree)); + insert("fileName", new PropertyFunc(this,&Private::fileName)); + insert("relPath", new PropertyFunc(this,&Private::relPath)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title", new PropertyFunc(this,&Private::title)); + } + private: + NestingContext m_classTree; +}; +//%% } + +ClassTreeContext::ClassTreeContext() +{ + p = new Private; +} + +ClassTreeContext::~ClassTreeContext() +{ + delete p; +} + +TemplateVariant ClassTreeContext::get(const char *name) const +{ + return p->get(name); +} + +//------------------------------------------------------------------------ + +//%% list NamespaceList[Namespace] : list of namespaces +class NamespaceListContext::Private : public GenericNodeListContext +{ + public: + void addNamespaces(const NamespaceSDict &nsDict) + { + NamespaceSDict::Iterator nli(nsDict); + NamespaceDef *nd; + for (nli.toFirst();(nd=nli.current());++nli) + { + if (nd->isLinkableInProject()) + { + append(new NamespaceContext(nd)); + } + } + } +}; + +NamespaceListContext::NamespaceListContext() +{ + p = new Private; + p->addNamespaces(*Doxygen::namespaceSDict); +} + +NamespaceListContext::~NamespaceListContext() +{ + delete p; +} + +// TemplateListIntf +int NamespaceListContext::count() const +{ + return p->count(); +} + +TemplateVariant NamespaceListContext::at(int index) const +{ + return p->at(index); +} + +TemplateListIntf::ConstIterator *NamespaceListContext::createIterator() const +{ + return p->createIterator(); +} + +//------------------------------------------------------------------------ + +//%% struct NamespaceTree: tree of nested namespace +//%% { +class NamespaceTreeContext::Private : public PropertyMapper +{ + public: + TemplateVariant tree() const + { + return TemplateVariant(&m_namespaceTree); + } + TemplateVariant fileName() const + { + return "namespaces"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "namespaces"; + } + TemplateVariant subhighlight() const + { + return "namespacelist"; + } + TemplateVariant title() const + { + static bool javaOpt = Config_getBool("OPTIMIZE_OUTPUT_JAVA"); + static bool fortranOpt = Config_getBool("OPTIMIZE_FOR_FORTRAN"); + static bool vhdlOpt = Config_getBool("OPTIMIZE_OUTPUT_VHDL"); + if (javaOpt || vhdlOpt) + { + return theTranslator->trPackages(); + } + else if (fortranOpt) + { + return theTranslator->trModulesList(); + } + else + { + return theTranslator->trNamespaceList(); + } + } + Private() + { + if (Doxygen::namespaceSDict) + { + m_namespaceTree.addNamespaces(*Doxygen::namespaceSDict,TRUE,FALSE); + } + //%% Nesting tree + insert("tree", new PropertyFunc(this,&Private::tree)); + insert("fileName", new PropertyFunc(this,&Private::fileName)); + insert("relPath", new PropertyFunc(this,&Private::relPath)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title", new PropertyFunc(this,&Private::title)); + } + private: + NestingContext m_namespaceTree; +}; +//%% } + +NamespaceTreeContext::NamespaceTreeContext() +{ + p = new Private; +} + +NamespaceTreeContext::~NamespaceTreeContext() +{ + delete p; +} + +TemplateVariant NamespaceTreeContext::get(const char *name) const +{ + return p->get(name); +} + +//------------------------------------------------------------------------ + +//%% list FileList[File] : list of files +class FileListContext::Private : public GenericNodeListContext +{ + public: + void addFiles(const FileNameList &fnList) + { + // TODO: if FULL_PATH_NAMES is enabled, the ordering should be dir+file + FileNameListIterator fnli(fnList); + 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(); + bool src = fd->generateSourceFile(); + bool nameOk = !fd->isDocumentationFile(); + if (nameOk && (doc || src) && !fd->isReference()) + { + append(new FileContext(fd)); + } + } + } + } +}; + +FileListContext::FileListContext() +{ + p = new Private; + if (Doxygen::inputNameList) p->addFiles(*Doxygen::inputNameList); +} + +FileListContext::~FileListContext() +{ + delete p; +} + +// TemplateListIntf +int FileListContext::count() const +{ + return p->count(); +} + +TemplateVariant FileListContext::at(int index) const +{ + return p->at(index); +} + +TemplateListIntf::ConstIterator *FileListContext::createIterator() const +{ + return p->createIterator(); +} + +//------------------------------------------------------------------------ + +//%% struct DirFileNode: node is a directory hierarchy +//%% { +class DirFileNodeContext::Private : public PropertyMapper +{ + public: + Private(Definition *d) : m_def(d), + m_dirContext (m_def->definitionType()==Definition::TypeDir ? (DirDef*)d : 0), + m_fileContext(m_def->definitionType()==Definition::TypeFile ? (FileDef*)d : 0) + { + //%% bool is_leaf_node: true if this node does not have any children + insert("is_leaf_node", new PropertyFunc(this,&Private::isLeafNode)); + //%% DirFile children: list of nested classes/namespaces + insert("children", new PropertyFunc(this,&Private::children)); + //%% [optional] Dir dir: directory info (if this node represents a directory) + insert("dir", new PropertyFunc(this,&Private::getDir)); + //%% [optional] File file: file info (if this node represents a file) + insert("file", new PropertyFunc(this,&Private::getFile)); + addDirFiles(); + } + TemplateVariant isLeafNode() const + { + return m_children.count()==0; + } + TemplateVariant children() const + { + return TemplateVariant(&m_children); + } + TemplateVariant getDir() const + { + if (m_def->definitionType()==Definition::TypeDir) + { + return TemplateVariant(&m_dirContext); + } + else + { + return TemplateVariant(FALSE); + } + } + TemplateVariant getFile() const + { + if (m_def->definitionType()==Definition::TypeFile) + { + return TemplateVariant(&m_fileContext); + } + else + { + return TemplateVariant(FALSE); + } + } + void addDirFiles() + { + DirDef *dd = m_def->definitionType()==Definition::TypeDir ? (DirDef*)m_def : 0; + if (dd) + { + m_children.addDirs(dd->subDirs()); + if (dd && dd->getFiles()) + { + m_children.addFiles(*dd->getFiles()); + } + } + } + private: + Definition *m_def; + DirFileContext m_children; + DirContext m_dirContext; + FileContext m_fileContext; +}; +//%% } + +DirFileNodeContext::DirFileNodeContext(Definition *d) +{ + p = new Private(d); +} + +DirFileNodeContext::~DirFileNodeContext() +{ + delete p; +} + +TemplateVariant DirFileNodeContext::get(const char *n) const +{ + return p->get(n); +} + + +//------------------------------------------------------------------------ + +//%% list DirFile[DirFileNode]: list of directories and/or files +class DirFileContext::Private : public GenericNodeListContext +{ + public: + void addDirs(const DirSDict &dirDict) + { + SDict::Iterator dli(dirDict); + DirDef *dd; + for (dli.toFirst();(dd=dli.current());++dli) + { + if (dd->getOuterScope()==Doxygen::globalScope) + { + append(new DirFileNodeContext(dd)); + } + } + } + void addDirs(const DirList &dirList) + { + QListIterator li(dirList); + DirDef *dd; + for (li.toFirst();(dd=li.current());++li) + { + append(new DirFileNodeContext(dd)); + } + } + void addFiles(const FileNameList &fnList) + { + FileNameListIterator fnli(fnList); + FileName *fn; + for (fnli.toFirst();(fn=fnli.current());++fnli) + { + FileNameIterator fni(*fn); + FileDef *fd; + for (;(fd=fni.current());++fni) + { + if (fd->getDirDef()==0) // top level file + { + append(new DirFileNodeContext(fd)); + } + } + } + } + void addFiles(const FileList &fList) + { + QListIterator li(fList); + FileDef *fd; + for (li.toFirst();(fd=li.current());++li) + { + append(new DirFileNodeContext(fd)); + } + } +}; + +DirFileContext::DirFileContext() +{ + p = new Private; +} + +DirFileContext::~DirFileContext() +{ + delete p; +} + +// TemplateListIntf +int DirFileContext::count() const +{ + return p->count(); +} + +TemplateVariant DirFileContext::at(int index) const +{ + return p->at(index); +} + +TemplateListIntf::ConstIterator *DirFileContext::createIterator() const +{ + return p->createIterator(); +} + +void DirFileContext::addDirs(const DirSDict &dirs) +{ + p->addDirs(dirs); +} + +void DirFileContext::addDirs(const DirList &dirs) +{ + p->addDirs(dirs); +} + +void DirFileContext::addFiles(const FileNameList &files) +{ + p->addFiles(files); +} + +void DirFileContext::addFiles(const FileList &files) +{ + p->addFiles(files); +} + + +//------------------------------------------------------------------------ + +//%% struct FileTree: tree of directories and files +//%% { +class FileTreeContext::Private : public PropertyMapper +{ + public: + TemplateVariant tree() const + { + return TemplateVariant(&m_dirFileTree); + } + TemplateVariant fileName() const + { + return "files"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "files"; + } + TemplateVariant subhighlight() const + { + return "filelist"; + } + TemplateVariant title() const + { + return theTranslator->trFileList(); + } + Private() + { + // Add dirs tree + if (Doxygen::directories) + { + m_dirFileTree.addDirs(*Doxygen::directories); + } + if (Doxygen::inputNameList) + { + m_dirFileTree.addFiles(*Doxygen::inputNameList); + } + //%% DirFile tree: + insert("tree", new PropertyFunc(this,&Private::tree)); + insert("fileName", new PropertyFunc(this,&Private::fileName)); + insert("relPath", new PropertyFunc(this,&Private::relPath)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title", new PropertyFunc(this,&Private::title)); + } + private: + DirFileContext m_dirFileTree; +}; +//%% } + +FileTreeContext::FileTreeContext() +{ + p = new Private; +} + +FileTreeContext::~FileTreeContext() +{ + delete p; +} + +TemplateVariant FileTreeContext::get(const char *name) const +{ + return p->get(name); +} + +//------------------------------------------------------------------------ + +//%% struct PageNode: node is a directory hierarchy +//%% { +class PageNodeContext::Private : public PropertyMapper +{ + public: + Private(PageDef *pd) : m_pageDef(pd), m_pageContext(pd) + { + //%% bool is_leaf_node: true if this node does not have any children + insert("is_leaf_node", new PropertyFunc(this,&Private::isLeafNode)); + //%% PageList children: list of nested classes/namespaces + insert("children", new PropertyFunc(this,&Private::children)); + //%% Page page: page info + insert("page", new PropertyFunc(this,&Private::getPage)); + addPages(); + } + TemplateVariant isLeafNode() const + { + return m_children.count()==0; + } + TemplateVariant children() const + { + return TemplateVariant(&m_children); + } + TemplateVariant getPage() const + { + return TemplateVariant(&m_pageContext); + } + void addPages() + { + if (m_pageDef->getSubPages()) + { + m_children.addPages(*m_pageDef->getSubPages(),FALSE); + } + } + private: + PageDef *m_pageDef; + PageNodeListContext m_children; + PageContext m_pageContext; +}; +//%% } + +PageNodeContext::PageNodeContext(PageDef *pd) +{ + p = new Private(pd); +} + +PageNodeContext::~PageNodeContext() +{ + delete p; +} + +TemplateVariant PageNodeContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% list PageList[PageNode]: list of directories and/or files +class PageNodeListContext::Private : public GenericNodeListContext +{ + public: + void addPages(const PageSDict &pages,bool rootOnly) + { + SDict::Iterator pli(pages); + PageDef *pd; + for (pli.toFirst();(pd=pli.current());++pli) + { + if (!rootOnly || + pd->getOuterScope()==0 || + pd->getOuterScope()->definitionType()!=Definition::TypePage) + { + append(new PageNodeContext(pd)); + } + } + } +}; + +PageNodeListContext::PageNodeListContext() +{ + p = new Private; +} + +PageNodeListContext::~PageNodeListContext() +{ + delete p; +} + +// TemplateListIntf +int PageNodeListContext::count() const +{ + return p->count(); +} + +TemplateVariant PageNodeListContext::at(int index) const +{ + return p->at(index); +} + +TemplateListIntf::ConstIterator *PageNodeListContext::createIterator() const +{ + return p->createIterator(); +} + +void PageNodeListContext::addPages(const PageSDict &pages,bool rootOnly) +{ + p->addPages(pages,rootOnly); +} + +//------------------------------------------------------------------------ + +//%% struct PageTree: tree of related pages +//%% { +class PageTreeContext::Private : public PropertyMapper +{ + public: + TemplateVariant tree() const + { + return TemplateVariant(&m_pageList); + } + TemplateVariant fileName() const + { + return "pages"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "pages"; + } + TemplateVariant subhighlight() const + { + return ""; + } + TemplateVariant title() const + { + return theTranslator->trRelatedPages(); + } + Private() + { + // Add pages + if (Doxygen::pageSDict) + { + m_pageList.addPages(*Doxygen::pageSDict,TRUE); + } + + //%% PageNodeList tree: + insert("tree", new PropertyFunc(this,&Private::tree)); + insert("fileName", new PropertyFunc(this,&Private::fileName)); + insert("relPath", new PropertyFunc(this,&Private::relPath)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title", new PropertyFunc(this,&Private::title)); + } + private: + PageNodeListContext m_pageList; +}; +//%% } + +PageTreeContext::PageTreeContext() +{ + p = new Private; +} + +PageTreeContext::~PageTreeContext() +{ + delete p; +} + +TemplateVariant PageTreeContext::get(const char *name) const +{ + return p->get(name); +} + +//------------------------------------------------------------------------ + +//%% struct PageList: list of related pages +//%% { +class PageListContext::Private : public PropertyMapper +{ + public: + TemplateVariant items() const + { + return TemplateVariant(&m_pageList); + } + TemplateVariant fileName() const + { + return "pages"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "pages"; + } + TemplateVariant subhighlight() const + { + return ""; + } + TemplateVariant title() const + { + return theTranslator->trRelatedPages(); + } + Private() + { + // Add pages + PageSDict::Iterator pdi(*Doxygen::pageSDict); + PageDef *pd=0; + for (pdi.toFirst();(pd=pdi.current());++pdi) + { + if (!pd->getGroupDef() && !pd->isReference()) + { + m_pageList.append(new PageContext(pd)); + } + } + + //%% list[Page] items: + insert("items", new PropertyFunc(this,&Private::items)); + insert("fileName", new PropertyFunc(this,&Private::fileName)); + insert("relPath", new PropertyFunc(this,&Private::relPath)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title", new PropertyFunc(this,&Private::title)); + } + private: + GenericNodeListContext m_pageList; +}; +//%% } + +PageListContext::PageListContext() +{ + p = new Private; +} + +PageListContext::~PageListContext() +{ + delete p; +} + +TemplateVariant PageListContext::get(const char *name) const +{ + return p->get(name); +} + + +//------------------------------------------------------------------------ + +//%% struct ModuleNode: node is a directory hierarchy +//%% { +class ModuleNodeContext::Private : public PropertyMapper +{ + public: + Private(GroupDef *gd) : m_groupDef(gd), m_moduleContext(gd) + { + //%% bool is_leaf_node: true if this node does not have any children + insert("is_leaf_node", new PropertyFunc(this,&Private::isLeafNode)); + //%% ModuleList children: list of submodules + insert("children", new PropertyFunc(this,&Private::children)); + //%% Module module: module info + insert("module", new PropertyFunc(this,&Private::getModule)); + addModules(); + } + TemplateVariant isLeafNode() const + { + return m_children.count()==0; + } + TemplateVariant children() const + { + return TemplateVariant(&m_children); + } + TemplateVariant getModule() const + { + return TemplateVariant(&m_moduleContext); + } + void addModules() + { + if (m_groupDef->getSubGroups()) + { + m_children.addModules(*m_groupDef->getSubGroups()); + } + } + private: + GroupDef *m_groupDef; + ModuleListContext m_children; + ModuleContext m_moduleContext; +}; +//%% } + +ModuleNodeContext::ModuleNodeContext(GroupDef *gd) +{ + p = new Private(gd); +} + +ModuleNodeContext::~ModuleNodeContext() +{ + delete p; +} + +TemplateVariant ModuleNodeContext::get(const char *n) const +{ + return p->get(n); +} + +//------------------------------------------------------------------------ + +//%% list ModuleList[ModuleNode]: list of directories and/or files +class ModuleListContext::Private : public GenericNodeListContext +{ + public: + void addModules(const GroupSDict &modules) + { + static bool externalGroups = Config_getBool("EXTERNAL_GROUPS"); + GroupSDict::Iterator gli(modules); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + if (!gd->isASubGroup() && gd->isVisible() && (!gd->isReference() || externalGroups)) + { + append(new ModuleNodeContext(gd)); + } + } + } + void addModules(const GroupList &list) + { + QListIterator gli(list); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + append(new ModuleNodeContext(gd)); + } + } +}; + +ModuleListContext::ModuleListContext() +{ + p = new Private; +} + +ModuleListContext::~ModuleListContext() +{ + delete p; +} + +// TemplateListIntf +int ModuleListContext::count() const +{ + return p->count(); +} + +TemplateVariant ModuleListContext::at(int index) const +{ + return p->at(index); +} + +TemplateListIntf::ConstIterator *ModuleListContext::createIterator() const +{ + return p->createIterator(); +} + +void ModuleListContext::addModules(const GroupSDict &modules) +{ + p->addModules(modules); +} + +void ModuleListContext::addModules(const GroupList &modules) +{ + p->addModules(modules); +} + + +//------------------------------------------------------------------------ + +//%% struct ModuleTree: tree of modules +//%% { +class ModuleTreeContext::Private : public PropertyMapper +{ + public: + TemplateVariant tree() const + { + return TemplateVariant(&m_moduleList); + } + TemplateVariant fileName() const + { + return "modules"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "modules"; + } + TemplateVariant subhighlight() const + { + return ""; + } + TemplateVariant title() const + { + return theTranslator->trModules(); + } + Private() + { + // Add modules + if (Doxygen::groupSDict) + { + m_moduleList.addModules(*Doxygen::groupSDict); + } + + //%% ModuleList tree: + insert("tree", new PropertyFunc(this,&Private::tree)); + insert("fileName", new PropertyFunc(this,&Private::fileName)); + insert("relPath", new PropertyFunc(this,&Private::relPath)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title", new PropertyFunc(this,&Private::title)); + } + private: + ModuleListContext m_moduleList; +}; +//%% } + +ModuleTreeContext::ModuleTreeContext() +{ + p = new Private; +} + +ModuleTreeContext::~ModuleTreeContext() +{ + delete p; +} + +TemplateVariant ModuleTreeContext::get(const char *name) const +{ + return p->get(name); +} + +//------------------------------------------------------------------------ + +//%% struct ExampleList: list of examples page +//%% { +class ExampleListContext::Private : public PropertyMapper +{ + public: + TemplateVariant items() const + { + return TemplateVariant(&m_pageList); + } + TemplateVariant fileName() const + { + return "examples"; + } + TemplateVariant relPath() const + { + return ""; + } + TemplateVariant highlight() const + { + return "examples"; + } + TemplateVariant subhighlight() const + { + return ""; + } + TemplateVariant title() const + { + return theTranslator->trExamples(); + } + Private() + { + // Add pages + if (Doxygen::exampleSDict) + { + m_pageList.addPages(*Doxygen::exampleSDict,FALSE); + } + + //%% PageNodeList items: + insert("items", new PropertyFunc(this,&Private::items)); + insert("fileName", new PropertyFunc(this,&Private::fileName)); + insert("relPath", new PropertyFunc(this,&Private::relPath)); + insert("highlight", new PropertyFunc(this,&Private::highlight)); + insert("subhighlight",new PropertyFunc(this,&Private::subhighlight)); + insert("title", new PropertyFunc(this,&Private::title)); + } + private: + PageNodeListContext m_pageList; +}; +//%% } + +ExampleListContext::ExampleListContext() +{ + p = new Private; +} + +ExampleListContext::~ExampleListContext() +{ + delete p; +} + +TemplateVariant ExampleListContext::get(const char *name) const +{ + return p->get(name); +} + +//------------------------------------------------------------------------ + +class HtmlEscaper : public TemplateEscapeIntf +{ + public: + QCString escape(const QCString &s) + { + return convertToHtml(s,TRUE); + } +}; + +//------------------------------------------------------------------------ + +void generateOutputViaTemplate() +{ + TemplateEngine e; + TemplateContext *ctx = e.createContext(); + HtmlEscaper esc; + ctx->setEscapeIntf(&esc); + if (ctx) + { + DoxygenContext doxygen; + ConfigContext config; + TranslateContext tr; + ClassListContext classList; + ClassTreeContext classTree; + ClassHierarchyContext classHierarchy; + NamespaceListContext namespaceList; + NamespaceTreeContext namespaceTree; + FileListContext fileList; + FileTreeContext fileTree; + PageTreeContext pageTree; + PageListContext pageList; + ModuleTreeContext moduleTree; + ExampleListContext exampleList; + + //%% Doxygen doxygen: + ctx->set("doxygen",&doxygen); + //%% Translator tr: + ctx->set("tr",&tr); + //%% Config config: + ctx->set("config",&config); + //%% ClassList classList: + ctx->set("classList",&classList); // not used for standard HTML + //%% ClassTree classTree: + ctx->set("classTree",&classTree); + // classIndex + //%% ClassHierarchy classHierarchy: + ctx->set("classHierarchy",&classHierarchy); + //%% NamespaceList namespaceList: + ctx->set("namespaceList",&namespaceList); + //%% NamespaceTree namespaceTree: + ctx->set("namespaceTree",&namespaceTree); + //%% FileList fileList: + ctx->set("fileList",&fileList); + //%% FileTree fileTree: + ctx->set("fileTree",&fileTree); + //%% PageList pageList + ctx->set("pageList",&pageList); + //%% PageTree pageTree + ctx->set("pageTree",&pageTree); + //%% ModuleTree moduleTree + ctx->set("moduleTree",&moduleTree); + //%% ExampleList exampleList + ctx->set("exampleList",&exampleList); + + // render HTML output + Template *tpl = e.loadByName("htmllayout.tpl"); + if (tpl) + { + ctx->setOutputDirectory(Config_getString("HTML_OUTPUT")); + FTextStream ts; + tpl->render(ts,ctx); + } + + // TODO: render other outputs + } +} + diff --git a/src/context.h b/src/context.h new file mode 100644 index 0000000..4940403 --- /dev/null +++ b/src/context.h @@ -0,0 +1,535 @@ +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "template.h" + +class Definition; +class ClassDef; +class ClassSDict; +class PageDef; +class GroupDef; +class NamespaceDef; +class BaseClassList; +class NamespaceSDict; +class FileDef; +class FileList; +class FileNameList; +class DirSDict; +class DirList; +class DirDef; +class PageSDict; +class GroupSDict; +class GroupDef; +class GroupList; + +//---------------------------------------------------- + +class ConfigContext : public TemplateStructIntf +{ + public: + ConfigContext(); + ~ConfigContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class DoxygenContext : public TemplateStructIntf +{ + public: + DoxygenContext(); + ~DoxygenContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class TranslateContext : public TemplateStructIntf +{ + public: + TranslateContext(); + ~TranslateContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ClassContext : public TemplateStructIntf +{ + public: + ClassContext(ClassDef *); + ~ClassContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class NamespaceContext : public TemplateStructIntf +{ + public: + NamespaceContext(NamespaceDef *); + ~NamespaceContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class FileContext : public TemplateStructIntf +{ + public: + FileContext(FileDef *); + ~FileContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; +//---------------------------------------------------- + +class DirContext : public TemplateStructIntf +{ + public: + DirContext(DirDef *); + ~DirContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + + +//---------------------------------------------------- + +class PageContext : public TemplateStructIntf +{ + public: + PageContext(PageDef *); + ~PageContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ModuleContext : public TemplateStructIntf +{ + public: + ModuleContext(GroupDef *); + ~ModuleContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ClassListContext : public TemplateListIntf +{ + public: + ClassListContext(); + ~ClassListContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ClassInheritanceNodeContext : public TemplateStructIntf +{ + public: + ClassInheritanceNodeContext(ClassDef *); + ~ClassInheritanceNodeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + void addChildren(const BaseClassList *bcl,bool hideSuper); + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ClassInheritanceContext : public TemplateListIntf +{ + public: + ClassInheritanceContext(); + ~ClassInheritanceContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ClassHierarchyContext : public TemplateStructIntf +{ + public: + ClassHierarchyContext(); + ~ClassHierarchyContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class NestingNodeContext : public TemplateStructIntf +{ + public: + NestingNodeContext(Definition *,bool addClasses); + ~NestingNodeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class NestingContext : public TemplateListIntf +{ + public: + NestingContext(); + ~NestingContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + void addNamespaces(const NamespaceSDict &nsDict,bool rootOnly,bool addClasses); + void addClasses(const ClassSDict &clDict,bool rootOnly); + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ClassTreeContext : public TemplateStructIntf +{ + public: + ClassTreeContext(); + ~ClassTreeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class NamespaceListContext : public TemplateListIntf +{ + public: + NamespaceListContext(); + ~NamespaceListContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class NamespaceTreeContext : public TemplateStructIntf +{ + public: + NamespaceTreeContext(); + ~NamespaceTreeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class DirFileNodeContext : public TemplateStructIntf +{ + public: + DirFileNodeContext(Definition *); + ~DirFileNodeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class DirFileContext : public TemplateListIntf +{ + public: + DirFileContext(); + ~DirFileContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + void addDirs(const DirSDict &); + void addDirs(const DirList &); + void addFiles(const FileNameList &); + void addFiles(const FileList &); + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class FileListContext : public TemplateListIntf +{ + public: + FileListContext(); + ~FileListContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class FileTreeContext : public TemplateStructIntf +{ + public: + FileTreeContext(); + ~FileTreeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class PageNodeContext : public TemplateStructIntf +{ + public: + PageNodeContext(PageDef *); + ~PageNodeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class PageNodeListContext : public TemplateListIntf +{ + public: + PageNodeListContext(); + ~PageNodeListContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + void addPages(const PageSDict &,bool rootOnly); + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class PageListContext : public TemplateStructIntf +{ + public: + PageListContext(); + ~PageListContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class PageTreeContext : public TemplateStructIntf +{ + public: + PageTreeContext(); + ~PageTreeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ModuleNodeContext : public TemplateStructIntf +{ + public: + ModuleNodeContext(GroupDef *); + ~ModuleNodeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ModuleListContext : public TemplateListIntf +{ + public: + ModuleListContext(); + ~ModuleListContext(); + + // TemplateListIntf + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + void addModules(const GroupSDict &); + void addModules(const GroupList &); + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ModuleTreeContext : public TemplateStructIntf +{ + public: + ModuleTreeContext(); + ~ModuleTreeContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +class ExampleListContext : public TemplateStructIntf +{ + public: + ExampleListContext(); + ~ExampleListContext(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + private: + class Private; + Private *p; +}; + +//---------------------------------------------------- + +void generateOutputViaTemplate(); + +#endif diff --git a/src/definition.cpp b/src/definition.cpp index b259088..2140a59 100644 --- a/src/definition.cpp +++ b/src/definition.cpp @@ -1815,7 +1815,7 @@ int Definition::getEndBodyLine() const return m_impl->body ? m_impl->body->endLine : -1; } -FileDef *Definition::getBodyDef() +FileDef *Definition::getBodyDef() const { return m_impl->body ? m_impl->body->fileDef : 0; } diff --git a/src/definition.h b/src/definition.h index 6b6e1d7..e3502a0 100644 --- a/src/definition.h +++ b/src/definition.h @@ -243,7 +243,7 @@ class Definition : public DefinitionIntf /*! Returns the file in which the body of this item is located or 0 if no * body is available. */ - FileDef *getBodyDef(); + FileDef *getBodyDef() const; /** Returns the programming language this definition was written in. */ SrcLangExt getLanguage() const; diff --git a/src/dirdef.cpp b/src/dirdef.cpp index fe6e47d..5df047a 100644 --- a/src/dirdef.cpp +++ b/src/dirdef.cpp @@ -329,12 +329,16 @@ void DirDef::endMemberDeclarations(OutputList &ol) ol.endMemberSections(); } +QCString DirDef::shortTitle() const +{ + return theTranslator->trDirReference(m_shortName); +} + void DirDef::writeDocumentation(OutputList &ol) { static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW"); ol.pushGeneratorState(); - QCString shortTitle=theTranslator->trDirReference(m_shortName); QCString title=theTranslator->trDirReference(m_dispName); startFile(ol,getOutputFileBase(),name(),title,HLI_None,!generateTreeView); @@ -348,7 +352,7 @@ void DirDef::writeDocumentation(OutputList &ol) startTitle(ol,getOutputFileBase()); ol.pushGeneratorState(); ol.disableAllBut(OutputGenerator::Html); - ol.parseText(shortTitle); + ol.parseText(shortTitle()); ol.enableAll(); ol.disable(OutputGenerator::Html); ol.parseText(title); diff --git a/src/dirdef.h b/src/dirdef.h index 99e2137..8b11b2e 100644 --- a/src/dirdef.h +++ b/src/dirdef.h @@ -66,6 +66,7 @@ class DirDef : public Definition const QDict *usedDirs() const { return m_usedDirs; } bool isParentOf(DirDef *dir) const; bool depGraphIsTrivial() const; + QCString shortTitle() const; // generate output void writeDocumentation(OutputList &ol); diff --git a/src/doxygen.cpp b/src/doxygen.cpp index 5b7ba1d..6f9a8bf 100644 --- a/src/doxygen.cpp +++ b/src/doxygen.cpp @@ -97,6 +97,7 @@ #include "docsets.h" #include "formula.h" #include "settings.h" +#include "context.h" #define RECURSE_ENTRYTREE(func,var) \ do { if (var->children()) { \ @@ -177,6 +178,7 @@ static QDict g_usingDeclarations(1009); // used classes static FileStorage *g_storage = 0; static bool g_successfulRun = FALSE; static bool g_dumpSymbolMap = FALSE; +static bool g_useOutputTemplate = FALSE; void clearAll() { @@ -10230,6 +10232,12 @@ void readConfiguration(int argc, char **argv) setvbuf(stdout,NULL,_IONBF,0); Doxygen::outputToWizard=TRUE; break; + case 'T': + msg("Warning: this option activates output generation via Django like template files. " + "This option is scheduled for doxygen 2.0, is currently incomplete and highly experimental! " + "Only use if you are a doxygen developer\n"); + g_useOutputTemplate=TRUE; + break; case 'h': case '?': usage(argv[0]); @@ -11309,6 +11317,17 @@ void generateOutput() Doxygen::formulaList->generateBitmaps(Config_getString("HTML_OUTPUT")); g_s.end(); } + + if (Config_getBool("SORT_GROUP_NAMES")) + { + Doxygen::groupSDict->sort(); + GroupSDict::Iterator gli(*Doxygen::groupSDict); + GroupDef *gd; + for (gli.toFirst();(gd=gli.current());++gli) + { + gd->sortSubGroups(); + } + } writeMainPageTagFileData(); @@ -11475,6 +11494,8 @@ void generateOutput() msg("finished...\n"); } + if (g_useOutputTemplate) generateOutputViaTemplate(); + /************************************************************************** * Start cleaning up * **************************************************************************/ diff --git a/src/filedef.cpp b/src/filedef.cpp index b12ecd3..e75c7b0 100644 --- a/src/filedef.cpp +++ b/src/filedef.cpp @@ -1794,3 +1794,9 @@ void FileDef::getAllIncludeFilesRecursively(QStrList &incFiles) const QDict includes(257); ::getAllIncludeFilesRecursively(&includes,this,incFiles); } + +QCString FileDef::title() const +{ + return theTranslator->trFileReference(name()); +} + diff --git a/src/filedef.h b/src/filedef.h index 9ccffc6..d1d7496 100644 --- a/src/filedef.h +++ b/src/filedef.h @@ -131,6 +131,8 @@ class FileDef : public Definition MemberGroupSDict *getMemberGroupSDict() const { return m_memberGroupSDict; } NamespaceSDict *getNamespaceSDict() const { return m_namespaceSDict; } ClassSDict *getClassSDict() const { return m_classSDict; } + + QCString title() const; //--------------------------------- diff --git a/src/fortranscanner.l b/src/fortranscanner.l old mode 100755 new mode 100644 diff --git a/src/ftextstream.h b/src/ftextstream.h index 9167ae9..d073f40 100644 --- a/src/ftextstream.h +++ b/src/ftextstream.h @@ -48,14 +48,14 @@ class FTextStream inline FTextStream &FTextStream::operator<<( char c) { - m_dev->putch(c); + if (m_dev) m_dev->putch(c); return *this; } inline FTextStream &FTextStream::operator<<( const char* s) { uint len = qstrlen( s ); - m_dev->writeBlock( s, len ); + if (m_dev) m_dev->writeBlock( s, len ); return *this; } diff --git a/src/index.cpp b/src/index.cpp index cfa3e02..ae96c80 100644 --- a/src/index.cpp +++ b/src/index.cpp @@ -414,38 +414,11 @@ void addMembersToIndex(T *def,LayoutDocManager::LayoutPart part, } } -//---------------------------------------------------------------------------- - -static bool classHasVisibleChildren(ClassDef *cd) -{ - BaseClassList *bcl; - - if (cd->getLanguage()==SrcLangExt_VHDL) // reverse baseClass/subClass relation - { - if (cd->baseClasses()==0) return FALSE; - bcl=cd->baseClasses(); - } - else - { - if (cd->subClasses()==0) return FALSE; - bcl=cd->subClasses(); - } - - BaseClassListIterator bcli(*bcl); - for ( ; bcli.current() ; ++bcli) - { - if (bcli.current()->classDef->isVisibleInHierarchy()) - { - return TRUE; - } - } - return FALSE; -} //---------------------------------------------------------------------------- /*! Generates HTML Help tree of classes */ -static void writeClassTree(OutputList &ol,BaseClassList *bcl,bool hideSuper,int level,FTVHelp* ftv,bool addToIndex) +static void writeClassTree(OutputList &ol,const BaseClassList *bcl,bool hideSuper,int level,FTVHelp* ftv,bool addToIndex) { if (bcl==0) return; BaseClassListIterator bcli(*bcl); @@ -562,14 +535,6 @@ static void writeClassTree(OutputList &ol,BaseClassList *bcl,bool hideSuper,int //---------------------------------------------------------------------------- -static bool classVisibleInIndex(ClassDef *cd) -{ - static bool allExternals = Config_getBool("ALLEXTERNALS"); - return (allExternals && cd->isLinkable()) || cd->isLinkableInProject(); -} - -//---------------------------------------------------------------------------- - static bool dirHasVisibleChildren(DirDef *dd) { if (dd->hasDocumentation()) return TRUE; @@ -1409,39 +1374,6 @@ void writeClassTree(ClassSDict *clDict,FTVHelp *ftv,bool addToIndex,bool globalO } } -static bool containsVisibleChild(NamespaceDef *nd,bool includeClasses) -{ - if (nd->getNamespaceSDict()) - { - NamespaceSDict::Iterator cnli(*nd->getNamespaceSDict()); - NamespaceDef *cnd; - for (cnli.toFirst();(cnd=cnli.current());++cnli) - { - if (cnd->isLinkable() && cnd->localName().find('@')==-1) - { - return TRUE; - } - else if (containsVisibleChild(cnd,includeClasses)) - { - return TRUE; - } - } - } - if (includeClasses && nd->getClassSDict()) - { - ClassSDict::Iterator cli(*nd->getClassSDict()); - ClassDef *cd; - for (;(cd=cli.current());++cli) - { - if (cd->isLinkableInProject() && cd->templateMaster()==0) - { - return TRUE; - } - } - } - return FALSE; -} - static void writeNamespaceTree(NamespaceSDict *nsDict,FTVHelp *ftv, bool rootOnly,bool showClasses,bool addToIndex) { @@ -1455,7 +1387,7 @@ static void writeNamespaceTree(NamespaceSDict *nsDict,FTVHelp *ftv, (!rootOnly || nd->getOuterScope()==Doxygen::globalScope)) { - bool hasChildren = containsVisibleChild(nd,showClasses); + bool hasChildren = namespaceHasVisibleChild(nd,showClasses); bool isLinkable = nd->isLinkableInProject(); QCString ref; @@ -3562,7 +3494,6 @@ static void writeGroupTreeNode(OutputList &ol, GroupDef *gd, int level, FTVHelp* if (gd->getSubGroups()->count()>0) { startIndexHierarchy(ol,level+1); - if (Config_getBool("SORT_GROUP_NAMES")) gd->sortSubGroups(); QListIterator gli(*gd->getSubGroups()); GroupDef *subgd = 0; for (gli.toFirst();(subgd=gli.current());++gli) @@ -3596,10 +3527,6 @@ static void writeGroupHierarchy(OutputList &ol, FTVHelp* ftv,bool addToIndex) ol.disable(OutputGenerator::Html); } startIndexHierarchy(ol,0); - if (Config_getBool("SORT_GROUP_NAMES")) - { - Doxygen::groupSDict->sort(); - } GroupSDict::Iterator gli(*Doxygen::groupSDict); GroupDef *gd; for (gli.toFirst();(gd=gli.current());++gli) diff --git a/src/libdoxygen.pro.in b/src/libdoxygen.pro.in index 96b7b9e..b230b2f 100644 --- a/src/libdoxygen.pro.in +++ b/src/libdoxygen.pro.in @@ -30,6 +30,7 @@ HEADERS = arguments.h \ compound.xsd.h \ condparser.h \ config.h \ + context.h \ constexp.h \ cppvalue.h \ debug.h \ @@ -129,6 +130,7 @@ HEADERS = arguments.h \ store.h \ tagreader.h \ tclscanner.h \ + template.h \ textdocvisitor.h \ tooltip.h \ translator.h \ @@ -155,6 +157,7 @@ SOURCES = arguments.cpp \ commentcnv.cpp \ commentscan.cpp \ condparser.cpp \ + context.cpp \ cppvalue.cpp \ dbusxmlscanner.cpp \ debug.cpp \ @@ -224,6 +227,7 @@ SOURCES = arguments.cpp \ store.cpp \ tagreader.cpp \ tclscanner.cpp \ + template.cpp \ textdocvisitor.cpp \ tooltip.cpp \ util.cpp \ @@ -239,8 +243,8 @@ SOURCES = arguments.cpp \ win32:TMAKE_CXXFLAGS += -DQT_NODLL win32-msvc:TMAKE_CXXFLAGS += -Zm200 -win32-g++:TMAKE_CXXFLAGS += -fno-exceptions -fno-rtti -linux-g++:TMAKE_CXXFLAGS += -fno-exceptions -fno-rtti +win32-g++:TMAKE_CXXFLAGS += -fno-exceptions +linux-g++:TMAKE_CXXFLAGS += -fno-exceptions INCLUDEPATH += ../qtools #INCLUDEPATH += ../libpng INCLUDEPATH += ../libmd5 diff --git a/src/namespacedef.cpp b/src/namespacedef.cpp index ee1568e..b0ba6b4 100644 --- a/src/namespacedef.cpp +++ b/src/namespacedef.cpp @@ -460,27 +460,8 @@ void NamespaceDef::writeDocumentation(OutputList &ol) static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW"); //static bool outputJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA"); //static bool fortranOpt = Config_getBool("OPTIMIZE_FOR_FORTRAN"); - SrcLangExt lang = getLanguage(); - QCString pageTitle; - if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp) - { - pageTitle = theTranslator->trPackage(displayName()); - } - else if (lang==SrcLangExt_Fortran) - { - pageTitle = theTranslator->trModuleReference(displayName()); - } - else if (lang==SrcLangExt_IDL) - { - pageTitle = isConstantGroup() - ? theTranslator->trConstantGroupReference(displayName()) - : theTranslator->trModuleReference(displayName()); - } - else - { - pageTitle = theTranslator->trNamespaceReference(displayName()); - } + QCString pageTitle = title(); startFile(ol,getOutputFileBase(),name(),pageTitle,HLI_NamespaceVisible,!generateTreeView); if (!generateTreeView) @@ -522,6 +503,7 @@ void NamespaceDef::writeDocumentation(OutputList &ol) //---------------------------------------- start flexible part ------------------------------- + SrcLangExt lang = getLanguage(); QListIterator eli( LayoutDocManager::instance().docEntries(LayoutDocManager::Namespace)); LayoutDocEntry *lde; @@ -1096,3 +1078,27 @@ MemberDef * NamespaceDef::getMemberByName(const QCString &n) const return md; } +QCString NamespaceDef::title() const +{ + SrcLangExt lang = getLanguage(); + QCString pageTitle; + if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp) + { + pageTitle = theTranslator->trPackage(displayName()); + } + else if (lang==SrcLangExt_Fortran) + { + pageTitle = theTranslator->trModuleReference(displayName()); + } + else if (lang==SrcLangExt_IDL) + { + pageTitle = isConstantGroup() + ? theTranslator->trConstantGroupReference(displayName()) + : theTranslator->trModuleReference(displayName()); + } + else + { + pageTitle = theTranslator->trNamespaceReference(displayName()); + } + return pageTitle; +} diff --git a/src/namespacedef.h b/src/namespacedef.h index ff64107..06d9d1d 100644 --- a/src/namespacedef.h +++ b/src/namespacedef.h @@ -92,6 +92,8 @@ class NamespaceDef : public Definition /*! Returns the namespaces contained in this namespace */ NamespaceSDict *getNamespaceSDict() const { return namespaceSDict; } + QCString title() const; + bool visited; private: diff --git a/src/template.cpp b/src/template.cpp new file mode 100644 index 0000000..3c60304 --- /dev/null +++ b/src/template.cpp @@ -0,0 +1,2968 @@ +#include "template.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sortdict.h" +#include "ftextstream.h" +#include "message.h" +#include "util.h" + +#define ENABLE_TRACING 0 + +#if ENABLE_TRACING +#define TRACE(x) printf x +#else +#define TRACE(x) +#endif + +class TemplateToken; + +//------------------------------------------------------------------- + +static QValueList split(const QCString &str,const QCString &sep,bool allowEmptyEntries=FALSE,bool cleanup=TRUE) +{ + QValueList lst; + + int j = 0; + int i = str.find( sep, j ); + + while (i!=-1) + { + if ( str.mid(j,i-j).length() > 0 ) + { + if (cleanup) + { + lst.append(str.mid(j,i-j).stripWhiteSpace()); + } + else + { + lst.append(str.mid(j,i-j)); + } + } + else if (allowEmptyEntries) + { + lst.append(""); + } + j = i + sep.length(); + i = str.find(sep,j); + } + + int l = str.length() - 1; + if (str.mid(j,l-j+1).length()>0) + { + if (cleanup) + { + lst.append(str.mid(j,l-j+1).stripWhiteSpace()); + } + else + { + lst.append(str.mid(j,l-j+1)); + } + } + else if (allowEmptyEntries) + { + lst.append(""); + } + + return lst; +} + +//---------------------------------------------------------------------------- + +#if ENABLE_TRACING +static QCString replace(const char *s,char csrc,char cdst) +{ + QCString result = s; + for (char *p=result.data();*p;p++) + { + if (*p==csrc) *p=cdst; + } + return result; +} +#endif + +//- TemplateVariant implementation ------------------------------------------- + +/** @brief Private data of a template variant object */ +class TemplateVariant::Private +{ + public: + Private() : raw(FALSE) {} + Type type; + int intVal; + QCString strVal; + bool boolVal; + const TemplateStructIntf *strukt; + const TemplateListIntf *list; + FuncType func; + const void *obj; + bool raw; +}; + +TemplateVariant::TemplateVariant() +{ + p = new Private; + p->type=None; +} + +TemplateVariant::TemplateVariant(bool b) +{ + p = new Private; + p->type = Bool; + p->boolVal = b; +} + +TemplateVariant::TemplateVariant(int v) +{ + p = new Private; + p->type = Integer; + p->intVal = v; +} + +TemplateVariant::TemplateVariant(const char *s) +{ + p = new Private; + p->type = String; + p->strVal = s; +} + +TemplateVariant::TemplateVariant(const QCString &s) +{ + p = new Private; + p->type = String; + p->strVal = s; +} + +TemplateVariant::TemplateVariant(const TemplateStructIntf *s) +{ + p = new Private; + p->type = Struct; + p->strukt = s; } + +TemplateVariant::TemplateVariant(const TemplateListIntf *l) +{ + p = new Private; + p->type = List; + p->list = l; +} + +TemplateVariant::TemplateVariant(const void *obj,FuncType f) +{ + p = new Private; + p->type = Function; + p->func = f; + p->obj = obj; +} + +TemplateVariant::~TemplateVariant() +{ + delete p; +} + +TemplateVariant::TemplateVariant(const TemplateVariant &v) +{ + p = new Private; + p->type = v.p->type; + p->raw = v.p->raw; + switch (p->type) + { + case None: break; + case Bool: p->boolVal = v.p->boolVal; break; + case Integer: p->intVal = v.p->intVal; break; + case String: p->strVal = v.p->strVal; break; + case Struct: p->strukt = v.p->strukt; break; + case List: p->list = v.p->list; break; + case Function: p->func = v.p->func; + p->obj = v.p->obj; break; + } +} + +TemplateVariant &TemplateVariant::operator=(const TemplateVariant &v) +{ + p->type = v.p->type; + p->raw = v.p->raw; + switch (p->type) + { + case None: break; + case Bool: p->boolVal = v.p->boolVal; break; + case Integer: p->intVal = v.p->intVal; break; + case String: p->strVal = v.p->strVal; break; + case Struct: p->strukt = v.p->strukt; break; + case List: p->list = v.p->list; break; + case Function: p->func = v.p->func; + p->obj = v.p->obj; break; + } + return *this; +} + +QCString TemplateVariant::toString() const +{ + QCString result; + switch (p->type) + { + case None: + break; + case Bool: + result=p->boolVal ? "true" : "false"; + break; + case Integer: + result=QCString().setNum(p->intVal); + break; + case String: + result=p->strVal; + break; + case Struct: + result="[struct]"; + break; + case List: + result="[list]"; + break; + case Function: + result="[function]"; + break; + } + return result; +} + +bool TemplateVariant::toBool() const +{ + bool result=FALSE; + switch (p->type) + { + case None: + break; + case Bool: + result = p->boolVal; + break; + case Integer: + result = p->intVal!=0; + break; + case String: + result = !p->strVal.isEmpty() && p->strVal!="false" && p->strVal!="0"; + break; + case Struct: + result = TRUE; + break; + case List: + result = p->list->count()!=0; + break; + case Function: + result = FALSE; + break; + } + return result; +} + +int TemplateVariant::toInt() const +{ + int result=0; + switch (p->type) + { + case None: + break; + case Bool: + result = p->boolVal ? 1 : 0; + break; + case Integer: + result = p->intVal; + break; + case String: + result = p->strVal.toInt(); + break; + case Struct: + break; + case List: + result = p->list->count(); + break; + case Function: + result = 0; + break; + } + return result; +} + +const TemplateStructIntf *TemplateVariant::toStruct() const +{ + return p->type==Struct ? p->strukt : 0; +} + +const TemplateListIntf *TemplateVariant::toList() const +{ + return p->type==List ? p->list : 0; +} + +QCString TemplateVariant::call(const QValueList &args) +{ + if (p->type==Function) return p->func(p->obj,args); + return QCString(); +} + +bool TemplateVariant::operator==(TemplateVariant &other) +{ + if (p->type==None) + { + return FALSE; + } + if (p->type==TemplateVariant::List && other.p->type==TemplateVariant::List) + { + return p->list==other.p->list; // TODO: improve me + } + else if (p->type==TemplateVariant::Struct && other.p->type==TemplateVariant::Struct) + { + return p->strukt==other.p->strukt; // TODO: improve me + } + else + { + return toString()==other.toString(); + } +} + +TemplateVariant::Type TemplateVariant::type() const +{ + return p->type; +} + +bool TemplateVariant::isValid() const +{ + return p->type!=None; +} + +void TemplateVariant::setRaw(bool b) +{ + p->raw = b; +} + +bool TemplateVariant::raw() const +{ + return p->raw; +} + +//- Template struct implementation -------------------------------------------- + + +/** @brief Private data of a template struct object */ +class TemplateStruct::Private +{ + public: + Private() : fields(17) + { fields.setAutoDelete(TRUE); } + QDict fields; +}; + +TemplateStruct::TemplateStruct() +{ + p = new Private; +} + +TemplateStruct::~TemplateStruct() +{ + delete p; +} + +void TemplateStruct::set(const char *name,const TemplateVariant &v) +{ + TemplateVariant *pv = p->fields.find(name); + if (pv) // change existing field + { + *pv = v; + } + else // insert new field + { + p->fields.insert(name,new TemplateVariant(v)); + } +} + +TemplateVariant TemplateStruct::get(const char *name) const +{ + TemplateVariant *v = p->fields.find(name); + return v ? *v : TemplateVariant(); +} + +//- Template list implementation ---------------------------------------------- + + +/** @brief Private data of a template list object */ +class TemplateList::Private +{ + public: + Private() : index(-1) {} + QValueList elems; + int index; +}; + + +TemplateList::TemplateList() +{ + p = new Private; +} + +TemplateList::~TemplateList() +{ + delete p; +} + +int TemplateList::count() const +{ + return p->elems.count(); +} + +void TemplateList::append(const TemplateVariant &v) +{ + p->elems.append(v); +} + +// iterator support +class TemplateListConstIterator : public TemplateListIntf::ConstIterator +{ + public: + TemplateListConstIterator(const TemplateList &l) : m_list(l) { m_index=-1; } + virtual ~TemplateListConstIterator() {} + virtual void toFirst() + { + m_it = m_list.p->elems.begin(); + m_index=0; + } + virtual void toLast() + { + m_it = m_list.p->elems.fromLast(); + m_index=m_list.count()-1; + } + virtual void toNext() + { + if (m_it!=m_list.p->elems.end()) + { + ++m_it; + ++m_index; + } + } + virtual void toPrev() + { + if (m_index>0) + { + --m_it; + --m_index; + } + else + { + m_index=-1; + } + } + virtual bool current(TemplateVariant &v) const + { + if (m_index<0 || m_it==m_list.p->elems.end()) + { + v = TemplateVariant(); + return FALSE; + } + else + { + v = *m_it; + return TRUE; + } + } + private: + const TemplateList &m_list; + QValueList::ConstIterator m_it; + int m_index; +}; + +TemplateListIntf::ConstIterator *TemplateList::createIterator() const +{ + return new TemplateListConstIterator(*this); +} + +TemplateVariant TemplateList::at(int index) const +{ + if (index>=0 && index<(int)p->elems.count()) + { + return p->elems[index]; + } + else + { + return TemplateVariant(); + } +} + +//- Operator types ------------------------------------------------------------ + +/** @brief Class representing operators that can appear in template expressions */ +class Operator +{ + public: + /* Operator precedence (low to high) + or + and + not + in + ==, !=, <, >, <=, >= + | + : + */ + enum Type + { + Or, And, Not, In, Equal, NotEqual, Less, Greater, LessEqual, + GreaterEqual, Filter, Colon, Last + }; + + static const char *toString(Type op) + { + switch(op) + { + case Or: return "or"; + case And: return "and"; + case Not: return "not"; + case In: return "in"; + case Equal: return "=="; + case NotEqual: return "!="; + case Less: return "<"; + case Greater: return ">"; + case LessEqual: return "<="; + case GreaterEqual: return ">="; + case Filter: return "|"; + case Colon: return ":"; + case Last: return "?"; + } + return "?"; + } +}; + +//----------------------------------------------------------------------------- + +class TemplateNodeBlock; + +/** @brief Class holding stacks of blocks available in the context */ +class TemplateBlockContext +{ + public: + TemplateBlockContext(); + TemplateNodeBlock *get(const QCString &name) const; + TemplateNodeBlock *pop(const QCString &name) const; + void add(TemplateNodeBlock *block); + void add(TemplateBlockContext *ctx); + void push(TemplateNodeBlock *block); + void clear(); + private: + QDict< QList > m_blocks; +}; + + +/** @brief Internal class representing the implementation of a template + * context */ +class TemplateContextImpl : public TemplateContext +{ + public: + TemplateContextImpl(); + virtual ~TemplateContextImpl(); + + // TemplateContext methods + void push(); + void pop(); + void set(const char *name,const TemplateVariant &v); + TemplateVariant get(const QCString &name) const; + const TemplateVariant *getRef(const QCString &name) const; + void setOutputDirectory(const QCString &dir) + { m_outputDir = dir; } + void setEscapeIntf(TemplateEscapeIntf *intf) + { m_escapeIntf = intf; } + + // internal methods + TemplateBlockContext *blockContext(); + TemplateVariant getPrimary(const QCString &name) const; + void setLocation(const QCString &templateName,int line) + { m_templateName=templateName; m_line=line; } + QCString templateName() const { return m_templateName; } + int line() const { return m_line; } + QCString outputDirectory() const { return m_outputDir; } + TemplateEscapeIntf *escapeIntf() const { return m_escapeIntf; } + + private: + QCString m_templateName; + int m_line; + QCString m_outputDir; + QList< QDict > m_contextStack; + TemplateBlockContext m_blockContext; + TemplateEscapeIntf *m_escapeIntf; +}; + +//----------------------------------------------------------------------------- + +/** @brief The implementation of the "add" filter */ +class FilterAdd +{ + public: + static int variantIntValue(const TemplateVariant &v,bool &isInt) + { + isInt = v.type()==TemplateVariant::Integer; + if (!isInt && v.type()==TemplateVariant::String) + { + return v.toString().toInt(&isInt); + } + return isInt ? v.toInt() : 0; + } + static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg) + { + if (!v.isValid()) + { + return arg; + } + bool lhsIsInt; + int lhsValue = variantIntValue(v,lhsIsInt); + bool rhsIsInt; + int rhsValue = variantIntValue(arg,rhsIsInt); + if (lhsIsInt && rhsIsInt) + { + return lhsValue+rhsValue; + } + else if (v.type()==TemplateVariant::String && arg.type()==TemplateVariant::String) + { + return TemplateVariant(v.toString() + arg.toString()); + } + else + { + return v; + } + } +}; + +//----------------------------------------------------------------------------- + +/** @brief The implementation of the "prepend" filter */ +class FilterPrepend +{ + public: + static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg) + { + if (v.type()==TemplateVariant::String && arg.type()==TemplateVariant::String) + { + return TemplateVariant(arg.toString() + v.toString()); + } + else + { + return v; + } + } +}; + +//-------------------------------------------------------------------- + +/** @brief The implementation of the "length" filter */ +class FilterLength +{ + public: + static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) + { + if (!v.isValid()) + { + return TemplateVariant(); + } + if (v.type()==TemplateVariant::List) + { + return TemplateVariant(v.toList()->count()); + } + else if (v.type()==TemplateVariant::String) + { + return TemplateVariant((int)v.toString().length()); + } + else + { + return TemplateVariant(); + } + } +}; + +//-------------------------------------------------------------------- + +/** @brief The implementation of the "default" filter */ +class FilterDefault +{ + public: + static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg) + { + if (!v.isValid()) + { + return arg; + } + else if (v.type()==TemplateVariant::String && v.toString().isEmpty()) + { + return arg; + } + else + { + return v; + } + } +}; + +//-------------------------------------------------------------------- + +/** @brief The implementation of the "default" filter */ +class FilterStripPath +{ + public: + static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) + { + if (!v.isValid() || v.type()!=TemplateVariant::String) + { + return v; + } + QCString result = v.toString(); + int i=result.findRev('/'); + if (i!=-1) + { + result=result.mid(i+1); + } + i=result.findRev('\\'); + if (i!=-1) + { + result=result.mid(i+1); + } + return result; + } +}; + +//-------------------------------------------------------------------- + +/** @brief The implementation of the "default" filter */ +class FilterNoWrap +{ + public: + static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) + { + if (!v.isValid() || v.type()!=TemplateVariant::String) + { + return v; + } + QCString s = v.toString(); + return substitute(s," "," "); + } +}; + + +//-------------------------------------------------------------------- + +/** @brief Factory singleton for registering and creating filters */ +class TemplateFilterFactory +{ + public: + typedef TemplateVariant (FilterFunction)(const TemplateVariant &v,const TemplateVariant &arg); + + static TemplateFilterFactory *instance() + { + static TemplateFilterFactory *instance = 0; + if (instance==0) instance = new TemplateFilterFactory; + return instance; + } + + TemplateVariant apply(const QCString &name,const TemplateVariant &v,const TemplateVariant &arg, bool &ok) + { + FilterFunction *func = (FilterFunction*)m_registry.find(name); + if (func) + { + ok=TRUE; + return (*func)(v,arg); + } + else + { + ok=FALSE; + return v; + } + } + + void registerFilter(const QCString &name,FilterFunction *func) + { + m_registry.insert(name,(void*)func); + } + + /** @brief Helper class for registering a filter function */ + template class AutoRegister + { + public: + AutoRegister(const QCString &key) + { + TemplateFilterFactory::instance()->registerFilter(key,&T::apply); + } + }; + + private: + QDict m_registry; +}; + +// register a handlers for each filter we support +static TemplateFilterFactory::AutoRegister fAdd("add"); +static TemplateFilterFactory::AutoRegister fPrepend("prepend"); +static TemplateFilterFactory::AutoRegister fLength("length"); +static TemplateFilterFactory::AutoRegister fDefault("default"); +static TemplateFilterFactory::AutoRegister fStripPath("strippath"); +static TemplateFilterFactory::AutoRegister fNoWrap("nowrap"); + +//-------------------------------------------------------------------- + +/** @brief Base class for all nodes in the abstract syntax tree of an + * expression. + */ +class ExprAst +{ + public: + virtual ~ExprAst() {} + virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(); } +}; + +/** @brief Class representing a number in the AST */ +class ExprAstNumber : public ExprAst +{ + public: + ExprAstNumber(int num) : m_number(num) + { TRACE(("ExprAstNumber(%d)\n",num)); } + int number() const { return m_number; } + virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_number); } + private: + int m_number; +}; + +/** @brief Class representing a variable in the AST */ +class ExprAstVariable : public ExprAst +{ + public: + ExprAstVariable(const char *name) : m_name(name) + { TRACE(("ExprAstVariable(%s)\n",name)); } + const QCString &name() const { return m_name; } + virtual TemplateVariant resolve(TemplateContext *c) + { + TemplateContextImpl *ci = dynamic_cast(c); + TemplateVariant v = c->get(m_name); + if (!v.isValid()) + { + warn(ci->templateName(),ci->line(),"undefined variable '%s' in expression",m_name.data()); + } + return v; + } + private: + QCString m_name; +}; + +/** @brief Class representing a filter in the AST */ +class ExprAstFilter : public ExprAst +{ + public: + ExprAstFilter(const char *name,ExprAst *arg) : m_name(name), m_arg(arg) + { TRACE(("ExprAstFilter(%s)\n",name)); } + ~ExprAstFilter() { delete m_arg; } + const QCString &name() const { return m_name; } + TemplateVariant apply(const TemplateVariant &v,TemplateContext *c) + { + TemplateContextImpl *ci = dynamic_cast(c); + TRACE(("Applying filter '%s' to '%s' (type=%d)\n",m_name.data(),v.toString().data(),v.type())); + TemplateVariant arg; + if (m_arg) arg = m_arg->resolve(c); + bool ok; + TemplateVariant result = TemplateFilterFactory::instance()->apply(m_name,v,arg,ok); + if (!ok) + { + warn(ci->templateName(),ci->line(),"unknown filter '%s'",m_name.data()); + } + return result; + } + private: + QCString m_name; + ExprAst *m_arg; +}; + +/** @brief Class representing a filter applied to an expression in the AST */ +class ExprAstFilterAppl : public ExprAst +{ + public: + ExprAstFilterAppl(ExprAst *expr,ExprAstFilter *filter) + : m_expr(expr), m_filter(filter) + { TRACE(("ExprAstFilterAppl\n")); } + ~ExprAstFilterAppl() { delete m_expr; delete m_filter; } + virtual TemplateVariant resolve(TemplateContext *c) + { + return m_filter->apply(m_expr->resolve(c),c); + } + private: + ExprAst *m_expr; + ExprAstFilter *m_filter; +}; + +/** @brief Class representing a string literal in the AST */ +class ExprAstLiteral : public ExprAst +{ + public: + ExprAstLiteral(const char *lit) : m_literal(lit) + { TRACE(("ExprAstLiteral(%s)\n",lit)); } + const QCString &literal() const { return m_literal; } + virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_literal); } + private: + QCString m_literal; +}; + +/** @brief Class representing a negation (not) operator in the AST */ +class ExprAstNegate : public ExprAst +{ + public: + ExprAstNegate(ExprAst *expr) : m_expr(expr) + { TRACE(("ExprAstNegate\n")); } + ~ExprAstNegate() { delete m_expr; } + virtual TemplateVariant resolve(TemplateContext *c) + { return TemplateVariant(!m_expr->resolve(c).toBool()); } + private: + ExprAst *m_expr; +}; + +/** @brief Class representing a binary operator in the AST */ +class ExprAstBinary : public ExprAst +{ + public: + ExprAstBinary(Operator::Type op,ExprAst *lhs,ExprAst *rhs) + : m_operator(op), m_lhs(lhs), m_rhs(rhs) + { TRACE(("ExprAstBinary %s\n",Operator::toString(op))); } + ~ExprAstBinary() { delete m_lhs; delete m_rhs; } + virtual TemplateVariant resolve(TemplateContext *c) + { + TemplateVariant lhs = m_lhs->resolve(c); + TemplateVariant rhs = m_rhs ? m_rhs->resolve(c) : TemplateVariant(); + switch(m_operator) + { + case Operator::Or: + return TemplateVariant(lhs.toBool() || rhs.toBool()); + case Operator::And: + return TemplateVariant(lhs.toBool() && rhs.toBool()); + case Operator::Equal: + return TemplateVariant(lhs == rhs); + case Operator::NotEqual: + return TemplateVariant(!(lhs == rhs)); + case Operator::Less: + if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String) + { + return lhs.toString()rhs.toInt(); + } + case Operator::LessEqual: + if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String) + { + return lhs.toString()==rhs.toString() || lhs.toString()=rhs.toInt(); + } + default: + return TemplateVariant(); + } + } + private: + Operator::Type m_operator; + ExprAst *m_lhs; + ExprAst *m_rhs; +}; + +//-------------------------------------------------------------------- + +/** @brief Recursive decent parser for Django style template expressions. + */ +class ExpressionParser +{ + public: + ExpressionParser(const QCString &templateName,int line) + : m_templateName(templateName), m_line(line), m_tokenStream(0) + { + } + virtual ~ExpressionParser() + { + } + + ExprAst *parse(const char *expr) + { + if (expr==0) return 0; + m_tokenStream = expr; + getNextToken(); + return parseOrExpression(); + } + + ExprAst *parsePrimary(const char *expr) + { + if (expr==0) return 0; + m_tokenStream = expr; + getNextToken(); + return parsePrimaryExpression(); + } + + ExprAst *parseVariable(const char *varExpr) + { + if (varExpr==0) return 0; + m_tokenStream = varExpr; + getNextToken(); + return parseFilteredVariable(); + } + + private: + + /** @brief Class representing a token within an expression. */ + class ExprToken + { + public: + ExprToken() : type(Unknown), num(-1), op(Operator::Or) + { + } + enum Type + { + Unknown, Operator, Number, Identifier, Literal + }; + + Type type; + int num; + QCString id; + Operator::Type op; + }; + + ExprAst *parseOrExpression() + { + TRACE(("{parseOrExpression(%s)\n",m_tokenStream)); + ExprAst *lhs = parseAndExpression(); + if (lhs) + { + while (m_curToken.type==ExprToken::Operator && + m_curToken.op==Operator::Or) + { + getNextToken(); + ExprAst *rhs = parseAndExpression(); + lhs = new ExprAstBinary(Operator::Or,lhs,rhs); + } + } + TRACE(("}parseOrExpression(%s)\n",m_tokenStream)); + return lhs; + } + + ExprAst *parseAndExpression() + { + TRACE(("{parseAndExpression(%s)\n",m_tokenStream)); + ExprAst *lhs = parseNotExpression(); + if (lhs) + { + while (m_curToken.type==ExprToken::Operator && + m_curToken.op==Operator::And) + { + getNextToken(); + ExprAst *rhs = parseNotExpression(); + lhs = new ExprAstBinary(Operator::And,lhs,rhs); + } + } + TRACE(("}parseAndExpression(%s)\n",m_tokenStream)); + return lhs; + } + + ExprAst *parseNotExpression() + { + TRACE(("{parseNotExpression(%s)\n",m_tokenStream)); + ExprAst *result=0; + if (m_curToken.type==ExprToken::Operator && + m_curToken.op==Operator::Not) + { + getNextToken(); + ExprAst *expr = parseCompareExpression(); + if (expr==0) + { + warn(m_templateName,m_line,"argument missing for not operator"); + return 0; + } + result = new ExprAstNegate(expr); + } + else + { + result = parseCompareExpression(); + } + TRACE(("}parseNotExpression(%s)\n",m_tokenStream)); + return result; + } + + ExprAst *parseCompareExpression() + { + TRACE(("{parseCompareExpression(%s)\n",m_tokenStream)); + ExprAst *lhs = parsePrimaryExpression(); + if (lhs) + { + Operator::Type op = m_curToken.op; + if (m_curToken.type==ExprToken::Operator && + (op==Operator::Less || + op==Operator::Greater || + op==Operator::Equal || + op==Operator::NotEqual || + op==Operator::LessEqual || + op==Operator::GreaterEqual + ) + ) + { + getNextToken(); + ExprAst *rhs = parseNotExpression(); + lhs = new ExprAstBinary(op,lhs,rhs); + } + } + TRACE(("}parseCompareExpression(%s)\n",m_tokenStream)); + return lhs; + } + + ExprAst *parsePrimaryExpression() + { + TRACE(("{parsePrimary(%s)\n",m_tokenStream)); + ExprAst *result=0; + switch (m_curToken.type) + { + case ExprToken::Number: + result = parseNumber(); + break; + case ExprToken::Identifier: + result = parseFilteredVariable(); + break; + case ExprToken::Literal: + result = parseLiteral(); + break; + default: + if (m_curToken.type==ExprToken::Operator) + { + warn(m_templateName,m_line,"unexpected operator '%s' in expression", + Operator::toString(m_curToken.op)); + } + else + { + warn(m_templateName,m_line,"unexpected token in expression"); + } + } + TRACE(("}parsePrimary(%s)\n",m_tokenStream)); + return result; + } + + ExprAst *parseNumber() + { + TRACE(("{parseNumber(%d)\n",m_curToken.num)); + ExprAst *num = new ExprAstNumber(m_curToken.num); + getNextToken(); + TRACE(("}parseNumber()\n")); + return num; + } + + ExprAst *parseIdentifier() + { + TRACE(("{parseIdentifier(%s)\n",m_curToken.id.data())); + ExprAst *id = new ExprAstVariable(m_curToken.id); + getNextToken(); + TRACE(("}parseIdentifier()\n")); + return id; + } + + ExprAst *parseLiteral() + { + TRACE(("{parseLiteral(%s)\n",m_curToken.id.data())); + ExprAst *lit = new ExprAstLiteral(m_curToken.id); + getNextToken(); + TRACE(("}parseLiteral()\n")); + return lit; + } + + ExprAst *parseFilteredVariable() + { + TRACE(("{parseFilteredVariable()\n")); + ExprAst *expr = parseIdentifier(); + if (expr) + { + while (m_curToken.type==ExprToken::Operator && + m_curToken.op==Operator::Filter) + { + getNextToken(); + ExprAstFilter *filter = parseFilter(); + if (!filter) break; + expr = new ExprAstFilterAppl(expr,filter); + } + } + TRACE(("}parseFilteredVariable()\n")); + return expr; + } + + ExprAstFilter *parseFilter() + { + TRACE(("{parseFilter(%s)\n",m_curToken.id.data())); + QCString filterName = m_curToken.id; + getNextToken(); + ExprAst *argExpr=0; + if (m_curToken.type==ExprToken::Operator && + m_curToken.op==Operator::Colon) + { + getNextToken(); + argExpr = parsePrimaryExpression(); + } + ExprAstFilter *filter = new ExprAstFilter(filterName,argExpr); + TRACE(("}parseFilter()\n")); + return filter; + } + + + bool getNextToken() + { + const char *p = m_tokenStream; + char s[2]; + s[1]=0; + if (p==0 || *p=='\0') return FALSE; + while (*p==' ') p++; // skip over spaces + char c=*p; + if (strncmp(p,"not ",4)==0) + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::Not; + p+=4; + } + else if (strncmp(p,"and ",4)==0) + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::And; + p+=4; + } + else if (strncmp(p,"or ",3)==0) + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::Or; + p+=3; + } + else if (c=='=' && *(p+1)=='=') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::Equal; + p+=2; + } + else if (c=='!' && *(p+1)=='=') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::NotEqual; + p+=2; + } + else if (c=='<' && *(p+1)=='=') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::LessEqual; + p+=2; + } + else if (c=='>' && *(p+1)=='=') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::GreaterEqual; + p+=2; + } + else if (c=='<') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::Less; + p++; + } + else if (c=='>') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::Greater; + p++; + } + else if (c=='|') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::Filter; + p++; + } + else if (c==':') + { + m_curToken.type = ExprToken::Operator; + m_curToken.op = Operator::Colon; + p++; + } + else if ((c=='-' && *(p+1)>='0' && *(p+1)<='9') || (c>='0' && c<='9')) + { + m_curToken.type = ExprToken::Number; + const char *np = p; + if (c=='-') np++; + m_curToken.num = 0; + while (*np>='0' && *np<='9') + { + m_curToken.num*=10; + m_curToken.num+=*np-'0'; + np++; + } + if (c=='-') m_curToken.num=-m_curToken.num; + p=np; + } + else if (c=='_' || (c>='a' && c<='z') || (c>='A' && c<='Z')) + { + m_curToken.type = ExprToken::Identifier; + s[0]=c; + m_curToken.id = s; + p++; + while ((c=*p) && + (c=='_' || c=='.' || + (c>='a' && c<='z') || + (c>='A' && c<='Z') || + (c>='0' && c<='9')) + ) + { + s[0]=c; + m_curToken.id+=s; + p++; + } + } + else if (c=='"' || c=='\'') + { + m_curToken.type = ExprToken::Literal; + m_curToken.id.resize(0); + p++; + char tokenChar = c; + char cp=0; + while ((c=*p) && (c!=tokenChar || (c==tokenChar && cp=='\\'))) + { + s[0]=c; + if (c!='\\' || cp=='\\') // don't add escapes + { + m_curToken.id+=s; + } + cp=c; + p++; + } + if (*p==tokenChar) p++; + } + else + { + m_curToken.type = ExprToken::Unknown; + char s[2]; + s[0]=c; + s[1]=0; + warn(m_templateName,m_line,"Found unknown token %s while parsing %s",s,m_tokenStream); + m_curToken.id = s; + p++; + } + //TRACE(("token type=%d op=%d num=%d id=%s\n", + // m_curToken.type,m_curToken.op,m_curToken.num,m_curToken.id.data())); + + m_tokenStream = p; + return TRUE; + } + + ExprToken m_curToken; + QCString m_templateName; + int m_line; + const char *m_tokenStream; +}; + +//---------------------------------------------------------- + +/** @brief Base class of all nodes in a template's AST */ +class TemplateNode +{ + public: + TemplateNode(TemplateNode *parent) : m_parent(parent) {} + virtual ~TemplateNode() {} + + virtual void render(FTextStream &ts, TemplateContext *c) = 0; + + TemplateNode *parent() { return m_parent; } + + private: + TemplateNode *m_parent; +}; + +//---------------------------------------------------------- + +/** @brief Parser for templates */ +class TemplateParser +{ + public: + TemplateParser(const QCString &templateName,QList &tokens); + void parse(TemplateNode *parent,int line,const QStrList &stopAt, + QList &nodes); + bool hasNextToken() const; + TemplateToken *takeNextToken(); + void removeNextToken(); + void prependToken(const TemplateToken *token); + const TemplateToken *currentToken() const; + QCString templateName() const { return m_templateName; } + private: + QCString m_templateName; + QList &m_tokens; +}; + +//---------------------------------------------------------- + +/** @brief Class representing a lexical token in a template */ +class TemplateToken +{ + public: + enum Type { Text, Variable, Block }; + TemplateToken(Type t,const char *d,int l) : type(t), data(d), line(l) {} + Type type; + QCString data; + int line; +}; + +//---------------------------------------------------------- + +/** @brief Class representing a list of AST nodes in a template */ +class TemplateNodeList : public QList +{ + public: + TemplateNodeList() + { + setAutoDelete(TRUE); + } + void render(FTextStream &ts,TemplateContext *c) + { + TRACE(("{TemplateNodeList::render\n")); + QListIterator it(*this); + TemplateNode *tn=0; + for (it.toFirst();(tn=it.current());++it) + { + tn->render(ts,c); + } + TRACE(("}TemplateNodeList::render\n")); + } +}; + +//---------------------------------------------------------- + +/** @brief Internal class representing the implementation of a template */ +class TemplateImpl : public TemplateNode, public Template +{ + public: + TemplateImpl(TemplateEngine *e,const QCString &name,const QCString &data); + void render(FTextStream &ts, TemplateContext *c); + + TemplateEngine *engine() const { return m_engine; } + TemplateBlockContext *blockContext() { return &m_blockContext; } + + private: + QCString m_name; + TemplateNodeList m_nodes; + TemplateEngine *m_engine; + TemplateBlockContext m_blockContext; +}; + +//---------------------------------------------------------- + + +TemplateContextImpl::TemplateContextImpl() + : m_templateName(""), m_line(1), m_escapeIntf(0) +{ + m_contextStack.setAutoDelete(TRUE); + push(); +} + +TemplateContextImpl::~TemplateContextImpl() +{ + pop(); +} + +void TemplateContextImpl::set(const char *name,const TemplateVariant &v) +{ + TemplateVariant *pv = m_contextStack.first()->find(name); + if (pv) + { + m_contextStack.first()->remove(name); + } + m_contextStack.first()->insert(name,new TemplateVariant(v)); +} + +TemplateVariant TemplateContextImpl::get(const QCString &name) const +{ + int i=name.find('.'); + if (i==-1) // simple name + { + return getPrimary(name); + } + else // obj.prop + { + TemplateVariant v; + QCString objName = name.left(i); + v = getPrimary(objName); + QCString propName = name.mid(i+1); + while (!propName.isEmpty()) + { + //printf("getPrimary(%s) type=%d:%s\n",objName.data(),v.type(),v.toString().data()); + if (v.type()==TemplateVariant::Struct) + { + i = propName.find("."); + int l = i==-1 ? propName.length() : i; + v = v.toStruct()->get(propName.left(l)); + if (!v.isValid()) + { + warn(m_templateName,m_line,"requesting non-existing property '%s' for object '%s'",propName.left(l).data(),objName.data()); + } + if (i!=-1) + { + objName = propName.left(i); + propName = propName.mid(i+1); + } + else + { + propName.resize(0); + } + } + else if (v.type()==TemplateVariant::List) + { + i = propName.find("."); + int l = i==-1 ? propName.length() : i; + bool b; + int index = propName.left(l).toInt(&b); + if (b) + { + v = v.toList()->at(index); + } + else + { + warn(m_templateName,m_line,"list index '%s' is not valid",propName.data()); + break; + } + if (i!=-1) + { + propName = propName.mid(i+1); + } + else + { + propName.resize(0); + } + } + else + { + warn(m_templateName,m_line,"using . on an object '%s' is not an struct or list",objName.data()); + return TemplateVariant(); + } + } while (i!=-1); + return v; + } +} + +const TemplateVariant *TemplateContextImpl::getRef(const QCString &name) const +{ + QListIterator< QDict > it(m_contextStack); + QDict *dict; + for (it.toFirst();(dict=it.current());++it) + { + TemplateVariant *v = dict->find(name); + if (v) return v; + } + return 0; // not found +} + +TemplateVariant TemplateContextImpl::getPrimary(const QCString &name) const +{ + const TemplateVariant *v = getRef(name); + return v ? *v : TemplateVariant(); +} + +void TemplateContextImpl::push() +{ + QDict *dict = new QDict; + dict->setAutoDelete(TRUE); + m_contextStack.prepend(dict); +} + +void TemplateContextImpl::pop() +{ + if (!m_contextStack.removeFirst()) + { + warn(m_templateName,m_line,"pop() called on empty context stack!\n"); + } +} + +TemplateBlockContext *TemplateContextImpl::blockContext() +{ + return &m_blockContext; +} + +//---------------------------------------------------------- + +/** @brief Class representing a piece of plain text in a template */ +class TemplateNodeText : public TemplateNode +{ + public: + TemplateNodeText(TemplateParser *,TemplateNode *parent,int,const QCString &data) + : TemplateNode(parent), m_data(data) + { + TRACE(("TemplateNodeText('%s')\n",replace(data,'\n',' ').data())); + } + + void render(FTextStream &ts, TemplateContext *) + { + //printf("TemplateNodeText::render(%s)\n",m_data.data()); + ts << m_data; + } + private: + QCString m_data; +}; + +//---------------------------------------------------------- + +/** @brief Class representing a variable in a template */ +class TemplateNodeVariable : public TemplateNode +{ + public: + TemplateNodeVariable(TemplateParser *parser,TemplateNode *parent,int line,const QCString &var) + : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line) + { + TRACE(("TemplateNodeVariable(%s)\n",var.data())); + ExpressionParser expParser(m_templateName,line); + int i=var.find(':'); + int j=var.find('|'); + if (i==-1 || (j!=-1 && j args = split(var.mid(i+1),","); + for (uint j=0;j(c); + ci->setLocation(m_templateName,m_line); + QValueList args; + for (uint i=0;iresolve(c); + args.append(v); + } + TemplateVariant v = m_var->resolve(c); + QCString value; + if (v.type()==TemplateVariant::Function) + { + value = v.call(args); + } + else + { + value = v.toString(); + } + //printf("TemplateNodeVariable::render(%s) raw=%d\n",value.data(),v.raw()); + if (ci->escapeIntf() && !v.raw()) + { + ts << ci->escapeIntf()->escape(value); + } + else + { + ts << value; + } + } + + private: + QCString m_templateName; + int m_line; + ExprAst *m_var; + QList m_args; +}; + +//---------------------------------------------------------- + +/** @brief Helper class for creating template AST tag nodes and returning + * the template for a given node. + */ +template class TemplateNodeCreator : public TemplateNode +{ + public: + TemplateNodeCreator(TemplateParser *parser,TemplateNode *parent,int line) + : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line) {} + static TemplateNode *createInstance(TemplateParser *parser, + TemplateNode *parent, + int line, + const QCString &data) + { + return new T(parser,parent,line,data); + } + TemplateImpl *getTemplate() + { + TemplateNode *root = this; + while (root && root->parent()) + { + root = root->parent(); + } + return dynamic_cast(root); + } + protected: + QCString m_templateName; + int m_line; +}; + +//---------------------------------------------------------- + +/** @brief Class representing an 'if' tag in a template */ +class TemplateNodeIf : public TemplateNodeCreator +{ + public: + TemplateNodeIf(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) : + TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeIf(%s)\n",data.data())); + if (data.isEmpty()) + { + warn(m_templateName,line,"missing argument for if tag"); + } + QStrList stopAt; + stopAt.append("endif"); + stopAt.append("else"); + parser->parse(this,line,stopAt,m_trueNodes); + TemplateToken *tok = parser->takeNextToken(); + ExpressionParser ex(parser->templateName(),line); + m_guardAst = ex.parse(data); + + if (tok && tok->data=="else") + { + stopAt.removeLast(); + parser->parse(this,line,stopAt,m_falseNodes); + parser->removeNextToken(); // skip over endif + } + delete tok; + TRACE(("}TemplateNodeIf(%s)\n",data.data())); + } + ~TemplateNodeIf() + { + delete m_guardAst; + } + + void render(FTextStream &ts, TemplateContext *c) + { + dynamic_cast(c)->setLocation(m_templateName,m_line); + //printf("TemplateNodeIf::render #trueNodes=%d #falseNodes=%d\n",m_trueNodes.count(),m_falseNodes.count()); + if (m_guardAst) + { + TemplateVariant guardValue = m_guardAst->resolve(c); + if (guardValue.toBool()) // guard is true, render corresponding nodes + { + m_trueNodes.render(ts,c); + } + else // guard is false, render corresponding nodes + { + m_falseNodes.render(ts,c); + } + } + } + private: + ExprAst *m_guardAst; + TemplateNodeList m_trueNodes; + TemplateNodeList m_falseNodes; +}; + +//---------------------------------------------------------- + +/** @brief Class representing a 'for' tag in a template */ +class TemplateNodeFor : public TemplateNodeCreator +{ + public: + TemplateNodeFor(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeFor(%s)\n",data.data())); + QCString exprStr; + int i = data.find(" in "); + if (i==-1) + { + if (data.right(3)==" in") + { + warn(m_templateName,line,"for is missing container after 'in' keyword"); + } + else if (data=="in") + { + warn(m_templateName,line,"for needs at least one iterator variable"); + } + else + { + warn(m_templateName,line,"for is missing 'in' keyword"); + } + } + else + { + m_vars = split(data.left(i),","); + if (m_vars.count()==0) + { + warn(m_templateName,line,"for needs at least one iterator variable"); + } + + int j = data.find(" reversed",i); + m_reversed = (j!=-1); + + if (j==-1) j=data.length(); + if (j>i+4) + { + exprStr = data.mid(i+4,j-i-4); // skip over " in " part + } + if (exprStr.isEmpty()) + { + warn(m_templateName,line,"for is missing container after 'in' keyword"); + } + } + ExpressionParser expParser(parser->templateName(),line); + m_expr = expParser.parseVariable(exprStr); + + QStrList stopAt; + stopAt.append("endfor"); + stopAt.append("empty"); + parser->parse(this,line,stopAt,m_loopNodes); + TemplateToken *tok = parser->takeNextToken(); + if (tok && tok->data=="empty") + { + stopAt.removeLast(); + parser->parse(this,line,stopAt,m_emptyNodes); + parser->removeNextToken(); // skip over endfor + } + delete tok; + TRACE(("}TemplateNodeFor(%s)\n",data.data())); + } + + ~TemplateNodeFor() + { + delete m_expr; + } + + void render(FTextStream &ts, TemplateContext *c) + { + dynamic_cast(c)->setLocation(m_templateName,m_line); + //printf("TemplateNodeFor::render #loopNodes=%d #emptyNodes=%d\n", + // m_loopNodes.count(),m_emptyNodes.count()); + if (m_expr) + { + TemplateVariant v = m_expr->resolve(c); + const TemplateListIntf *list = v.toList(); + if (list) + { + uint listSize = list->count(); + if (listSize==0) // empty for loop + { + m_emptyNodes.render(ts,c); + return; + } + c->push(); + //int index = m_reversed ? list.count() : 0; + TemplateVariant v; + const TemplateVariant *parentLoop = c->getRef("forloop"); + uint index = m_reversed ? listSize-1 : 0; + TemplateListIntf::ConstIterator *it = list->createIterator(); + for (m_reversed ? it->toLast() : it->toFirst(); + (it->current(v)); + m_reversed ? it->toPrev() : it->toNext()) + { + TemplateStruct s; + s.set("counter0", (int)index); + s.set("counter", (int)(index+1)); + s.set("revcounter", (int)(listSize-index)); + s.set("revcounter0", (int)(listSize-index-1)); + s.set("first",index==0); + s.set("last", index==listSize-1); + s.set("parentloop",parentLoop ? *parentLoop : TemplateVariant()); + c->set("forloop",&s); + + // add variables for this loop to the context + //obj->addVariableToContext(index,m_vars,c); + uint vi=0; + if (m_vars.count()==1) // loop variable represents an item + { + c->set(m_vars[vi++],v); + } + else if (m_vars.count()>1 && v.type()==TemplateVariant::Struct) + // loop variables represent elements in a list item + { + for (uint i=0;iset(m_vars[vi],v.toStruct()->get(m_vars[vi])); + } + } + for (;viset(m_vars[vi],TemplateVariant()); + } + + // render all items for this iteration of the loop + m_loopNodes.render(ts,c); + + if (m_reversed) index--; else index++; + } + c->pop(); + delete it; + } + else // simple type... + { + warn(m_templateName,m_line,"for requires a variable of list type!"); + } + } + } + + private: + bool m_reversed; + ExprAst *m_expr; + QValueList m_vars; + TemplateNodeList m_loopNodes; + TemplateNodeList m_emptyNodes; +}; + +//---------------------------------------------------------- + +/** @brief Class representing a 'block' tag in a template */ +class TemplateNodeBlock : public TemplateNodeCreator +{ + public: + TemplateNodeBlock(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeBlock(%s)\n",data.data())); + m_blockName = data; + if (m_blockName.isEmpty()) + { + warn(parser->templateName(),line,"block tag without name"); + } + QStrList stopAt; + stopAt.append("endblock"); + parser->parse(this,line,stopAt,m_nodes); + parser->removeNextToken(); // skip over endblock + TRACE(("}TemplateNodeBlock(%s)\n",data.data())); + } + + void render(FTextStream &ts, TemplateContext *c) + { + TemplateContextImpl *ci = dynamic_cast(c); + ci->setLocation(m_templateName,m_line); + TemplateImpl *t = getTemplate(); + if (t) + { + // remove block from the context, so block.super can work + TemplateNodeBlock *nb = ci->blockContext()->pop(m_blockName); + if (nb) // block is overruled + { + ci->push(); + QGString super; + FTextStream ss(&super); + // get super block of block nb + TemplateNodeBlock *sb = ci->blockContext()->get(m_blockName); + if (sb && sb!=nb && sb!=this) // nb and sb both overrule this block + { + sb->render(ss,c); // render parent of nb to string + } + else if (nb!=this) // only nb overrules this block + { + m_nodes.render(ss,c); // render parent of nb to string + } + // add 'block.super' variable to allow access to parent block content + TemplateStruct superBlock; + superBlock.set("super",super.data()); + ci->set("block",&superBlock); + // render the overruled block contents + nb->m_nodes.render(ts,c); + ci->pop(); + // re-add block to the context + ci->blockContext()->push(nb); + } + else // block has no overrule + { + m_nodes.render(ts,c); + } + } + } + + QCString name() const + { + return m_blockName; + } + + private: + QCString m_blockName; + TemplateNodeList m_nodes; +}; + +//---------------------------------------------------------- + +/** @brief Class representing a 'extend' tag in a template */ +class TemplateNodeExtend : public TemplateNodeCreator +{ + public: + TemplateNodeExtend(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeExtend(%s)\n",data.data())); + ExpressionParser ep(m_templateName,line); + if (data.isEmpty()) + { + warn(m_templateName,line,"extend tag is missing template file argument"); + } + m_extendExpr = ep.parsePrimary(data); + QStrList stopAt; + parser->parse(this,line,stopAt,m_nodes); + TRACE(("}TemplateNodeExtend(%s)\n",data.data())); + } + ~TemplateNodeExtend() + { + delete m_extendExpr; + } + + void render(FTextStream &ts, TemplateContext *c) + { + dynamic_cast(c)->setLocation(m_templateName,m_line); + if (m_extendExpr==0) return; + + QCString extendFile = m_extendExpr->resolve(c).toString(); + if (extendFile.isEmpty()) + { + warn(m_templateName,m_line,"invalid parameter for extend command"); + } + + // goto root of tree (template node) + TemplateImpl *t = getTemplate(); + if (t) + { + TemplateImpl *baseTemplate = dynamic_cast(t->engine()->loadByName(extendFile)); + if (baseTemplate) + { + // fill block context + TemplateContextImpl *ci = dynamic_cast(c); + TemplateBlockContext *bc = ci->blockContext(); + + // add overruling blocks to the context + QListIterator li(m_nodes); + TemplateNode *n; + for (li.toFirst();(n=li.current());++li) + { + TemplateNodeBlock *nb = dynamic_cast(n); + if (nb) + { + bc->add(nb); + } + } + + // render the base template with the given context + baseTemplate->render(ts,c); + + // clean up + bc->clear(); + delete baseTemplate; + } + else + { + warn(m_templateName,m_line,"failed to load template %s for extend",extendFile.data()); + } + } + } + + private: + ExprAst *m_extendExpr; + TemplateNodeList m_nodes; +}; + +/** @brief Class representing an 'include' tag in a template */ +class TemplateNodeInclude : public TemplateNodeCreator +{ + public: + TemplateNodeInclude(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("TemplateNodeInclude(%s)\n",data.data())); + ExpressionParser ep(m_templateName,line); + if (data.isEmpty()) + { + warn(m_templateName,line,"include tag is missing template file argument"); + } + m_includeExpr = ep.parsePrimary(data); + } + ~TemplateNodeInclude() + { + delete m_includeExpr; + } + void render(FTextStream &ts, TemplateContext *c) + { + dynamic_cast(c)->setLocation(m_templateName,m_line); + if (m_includeExpr) + { + QCString includeFile = m_includeExpr->resolve(c).toString(); + if (includeFile.isEmpty()) + { + warn(m_templateName,m_line,"invalid parameter for include command\n"); + } + else + { + TemplateImpl *t = getTemplate(); + if (t) + { + TemplateImpl *incTemplate = dynamic_cast(t->engine()->loadByName(includeFile)); + if (incTemplate) + { + incTemplate->render(ts,c); + } + else + { + warn(m_templateName,m_line,"failed to load template '%s' for include",includeFile.data()?includeFile.data():""); + } + } + } + } + } + + private: + ExprAst *m_includeExpr; +}; + +//---------------------------------------------------------- + +/** @brief Class representing an 'instantiate' tag in a template */ +class TemplateNodeCreate : public TemplateNodeCreator +{ + public: + TemplateNodeCreate(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("TemplateNodeCreate(%s)\n",data.data())); + ExpressionParser ep(m_templateName,line); + if (data.isEmpty()) + { + warn(m_templateName,line,"create tag is missing arguments"); + } + int i = data.find(" from "); + if (i==-1) + { + if (data.right(3)==" from") + { + warn(m_templateName,line,"create is missing template name after 'from' keyword"); + } + else if (data=="from") + { + warn(m_templateName,line,"create needs a file name and a template name"); + } + else + { + warn(m_templateName,line,"create is missing 'from' keyword"); + } + } + else + { + ExpressionParser ep(m_templateName,line); + m_fileExpr = ep.parsePrimary(data.left(i).stripWhiteSpace()); + m_templateExpr = ep.parsePrimary(data.mid(i+6).stripWhiteSpace()); + } + } + ~TemplateNodeCreate() + { + delete m_templateExpr; + delete m_fileExpr; + } + void render(FTextStream &, TemplateContext *c) + { + TemplateContextImpl* ci = dynamic_cast(c); + ci->setLocation(m_templateName,m_line); + if (m_templateExpr && m_fileExpr) + { + QCString templateFile = m_templateExpr->resolve(c).toString(); + QCString outputFile = m_fileExpr->resolve(c).toString(); + if (templateFile.isEmpty()) + { + warn(m_templateName,m_line,"empty template name parameter for create command\n"); + } + else if (outputFile.isEmpty()) + { + warn(m_templateName,m_line,"empty file name parameter for create command\n"); + } + else + { + TemplateImpl *t = getTemplate(); + if (t) + { + TemplateImpl *createTemplate = dynamic_cast(t->engine()->loadByName(templateFile)); + if (createTemplate) + { + if (!ci->outputDirectory().isEmpty()) + { + outputFile.prepend(ci->outputDirectory()+"/"); + } + QFile f(outputFile); + if (f.open(IO_WriteOnly)) + { + FTextStream ts(&f); + createTemplate->render(ts,c); + delete createTemplate; + } + else + { + warn(m_templateName,m_line,"failed to open output file '%s' for create command",outputFile.data()); + } + } + else + { + warn(m_templateName,m_line,"failed to load template '%s' for include",templateFile.data()); + } + } + } + } + } + + private: + ExprAst *m_templateExpr; + ExprAst *m_fileExpr; +}; + +//---------------------------------------------------------- + +/** @brief Class representing an 'instantiate' tag in a template */ +class TemplateNodeTree : public TemplateNodeCreator +{ + struct TreeContext + { + TreeContext(TemplateNodeTree *o,const TemplateListIntf *l,TemplateContext *c) + : object(o), list(l), templateCtx(c) {} + TemplateNodeTree *object; + const TemplateListIntf *list; + TemplateContext *templateCtx; + }; + public: + TemplateNodeTree(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeTree(%s)\n",data.data())); + ExpressionParser ep(m_templateName,line); + if (data.isEmpty()) + { + warn(m_templateName,line,"recursetree tag is missing data argument"); + } + m_treeExpr = ep.parsePrimary(data); + QStrList stopAt; + stopAt.append("endrecursetree"); + parser->parse(this,line,stopAt,m_treeNodes); + parser->removeNextToken(); // skip over endrecursetree + TRACE(("}TemplateNodeTree(%s)\n",data.data())); + } + ~TemplateNodeTree() + { + delete m_treeExpr; + } + static QCString renderChildrenStub(const void *ctx, const QValueList &) + { + return ((TreeContext*)ctx)->object->renderChildren((const TreeContext*)ctx); + } + QCString renderChildren(const TreeContext *ctx) + { + //printf("TemplateNodeTree::renderChildren(%d)\n",ctx->list->count()); + // render all children of node to a string and return it + QGString result; + FTextStream ss(&result); + TemplateContext *c = ctx->templateCtx; + c->push(); + TemplateVariant node; + TemplateListIntf::ConstIterator *it = ctx->list->createIterator(); + for (it->toFirst();(it->current(node));it->toNext()) + { + c->set("node",node); + bool hasChildren=FALSE; + const TemplateStructIntf *ns = node.toStruct(); + if (ns) // node is a struct + { + TemplateVariant v = ns->get("children"); + if (v.isValid()) // with a field 'children' + { + const TemplateListIntf *list = v.toList(); + if (list && list->count()>0) // non-empty list + { + TreeContext childCtx(this,list,ctx->templateCtx); + TemplateVariant children(&childCtx,renderChildrenStub); + children.setRaw(TRUE); + c->set("children",children); + m_treeNodes.render(ss,c); + hasChildren=TRUE; + } + } + } + if (!hasChildren) + { + c->set("children",TemplateVariant("")); // provide default + m_treeNodes.render(ss,c); + } + } + c->pop(); + return result.data(); + } + void render(FTextStream &ts, TemplateContext *c) + { + //printf("TemplateNodeTree::render()\n"); + TemplateContextImpl* ci = dynamic_cast(c); + ci->setLocation(m_templateName,m_line); + TemplateVariant v = m_treeExpr->resolve(c); + const TemplateListIntf *list = v.toList(); + if (list) + { + TreeContext ctx(this,list,c); + ts << renderChildren(&ctx); + } + else + { + warn(m_templateName,m_line,"recursetree's argument should be a list type"); + } + } + + private: + ExprAst *m_treeExpr; + TemplateNodeList m_treeNodes; +}; + +//---------------------------------------------------------- + +/** @brief Class representing an 'instantiate' tag in a template */ +class TemplateNodeWith : public TemplateNodeCreator +{ + struct Mapping + { + Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {} + ~Mapping() { delete value; } + QCString name; + ExprAst *value; + }; + public: + TemplateNodeWith(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + m_args.setAutoDelete(TRUE); + ExpressionParser expParser(parser->templateName(),line); + QValueList args = split(data," "); + QValueListIterator it = args.begin(); + while (it!=args.end()) + { + QCString arg = *it; + int j=arg.find('='); + if (j>0) + { + ExprAst *expr = expParser.parsePrimary(arg.mid(j+1)); + if (expr) + { + m_args.append(new Mapping(arg.left(j),expr)); + } + } + else + { + warn(parser->templateName(),line,"invalid argument '%s' for with tag",arg.data()); + } + ++it; + } + QStrList stopAt; + stopAt.append("endwith"); + parser->parse(this,line,stopAt,m_nodes); + parser->removeNextToken(); // skip over endwith + } + ~TemplateNodeWith() + { + } + void render(FTextStream &ts, TemplateContext *c) + { + TemplateContextImpl *ci = dynamic_cast(c); + c->push(); + QListIterator it(m_args); + Mapping *mapping; + for (it.toFirst();(mapping=it.current());++it) + { + TemplateVariant value = mapping->value->resolve(c); + ci->set(mapping->name,value); + } + m_nodes.render(ts,c); + c->pop(); + } + private: + TemplateNodeList m_nodes; + QList m_args; +}; + +//---------------------------------------------------------- + +/** @brief Factory class for creating tag AST nodes found in a template */ +class TemplateNodeFactory +{ + public: + typedef TemplateNode *(*CreateFunc)(TemplateParser *parser, + TemplateNode *parent, + int line, + const QCString &data); + + static TemplateNodeFactory *instance() + { + static TemplateNodeFactory *instance = 0; + if (instance==0) instance = new TemplateNodeFactory; + return instance; + } + + TemplateNode *create(const QCString &name, + TemplateParser *parser, + TemplateNode *parent, + int line, + const QCString &data) + { + if (m_registry.find(name)==0) return 0; + return ((CreateFunc)m_registry[name])(parser,parent,line,data); + } + + void registerTemplateNode(const QCString &name,CreateFunc func) + { + m_registry.insert(name,(void*)func); + } + + /** @brief Helper class for registering a template AST node */ + template class AutoRegister + { + public: + AutoRegister(const QCString &key) + { + TemplateNodeFactory::instance()->registerTemplateNode(key,T::createInstance); + } + }; + + private: + QDict m_registry; +}; + +// register a handler for each start tag we support +static TemplateNodeFactory::AutoRegister autoRefIf("if"); +static TemplateNodeFactory::AutoRegister autoRefFor("for"); +static TemplateNodeFactory::AutoRegister autoRefTree("recursetree"); +static TemplateNodeFactory::AutoRegister autoRefWith("with"); +static TemplateNodeFactory::AutoRegister autoRefBlock("block"); +static TemplateNodeFactory::AutoRegister autoRefExtend("extend"); +static TemplateNodeFactory::AutoRegister autoRefCreate("create"); +static TemplateNodeFactory::AutoRegister autoRefInclude("include"); + +//---------------------------------------------------------- + +TemplateBlockContext::TemplateBlockContext() : m_blocks(257) +{ + m_blocks.setAutoDelete(TRUE); +} + +TemplateNodeBlock *TemplateBlockContext::get(const QCString &name) const +{ + QList *list = m_blocks.find(name); + if (list==0 || list->count()==0) + { + return 0; + } + else + { + return list->getLast(); + } +} + +TemplateNodeBlock *TemplateBlockContext::pop(const QCString &name) const +{ + QList *list = m_blocks.find(name); + if (list==0 || list->count()==0) + { + return 0; + } + else + { + return list->take(list->count()-1); + } +} + +void TemplateBlockContext::add(TemplateNodeBlock *block) +{ + QList *list = m_blocks.find(block->name()); + if (list==0) + { + list = new QList; + m_blocks.insert(block->name(),list); + } + list->prepend(block); +} + +void TemplateBlockContext::add(TemplateBlockContext *ctx) +{ + QDictIterator< QList > di(ctx->m_blocks); + QList *list; + for (di.toFirst();(list=di.current());++di) + { + QListIterator li(*list); + TemplateNodeBlock *nb; + for (li.toFirst();(nb=li.current());++li) + { + add(nb); + } + } +} + +void TemplateBlockContext::clear() +{ + m_blocks.clear(); +} + +void TemplateBlockContext::push(TemplateNodeBlock *block) +{ + QList *list = m_blocks.find(block->name()); + if (list==0) + { + list = new QList; + m_blocks.insert(block->name(),list); + } + list->append(block); +} + + +//---------------------------------------------------------- + +/** @brief Lexer class for turning a template into a list of tokens */ +class TemplateLexer +{ + public: + TemplateLexer(const QCString &fileName,const QCString &data); + void tokenize(QList &tokens); + private: + void addToken(QList &tokens, + const char *data,int line,int startPos,int endPos, + TemplateToken::Type type); + void reset(); + QCString m_fileName; + QCString m_data; +}; + +TemplateLexer::TemplateLexer(const QCString &fileName,const QCString &data) : + m_fileName(fileName), m_data(data) +{ +} + +void TemplateLexer::tokenize(QList &tokens) +{ + enum LexerStates + { + StateText, + StateBeginTemplate, + StateTag, + StateEndTag, + StateComment, + StateEndComment, + StateMaybeVar, + StateVariable, + StateEndVariable + }; + + const char *p=m_data.data(); + int state=StateText; + int pos=0; + int lastTokenPos=0; + int startLinePos=0; + bool emptyOutputLine=TRUE; + int line=1; + char c; + int markStartPos=-1; + for (;(c=*p);p++,pos++) + { + switch (state) + { + case StateText: + if (c=='{') // {{ or {% or {# or something else + { + state=StateBeginTemplate; + } + else if (c!=' ' && c!='\t' && c!='\n') // non-whitepace text + { + emptyOutputLine=FALSE; + } + break; + case StateBeginTemplate: + switch (c) + { + case '%': // {% + state=StateTag; + markStartPos=pos-1; + break; + case '#': // {# + state=StateComment; + markStartPos=pos-1; + break; + case '{': // {{ + state=StateMaybeVar; + markStartPos=pos-1; + break; + default: + state=StateText; + emptyOutputLine=FALSE; + break; + } + break; + case StateTag: + if (c=='\n') + { + warn(m_fileName,line,"unexpected new line inside {%%...%%} block"); + } + else if (c=='%') // %} or something else + { + state=StateEndTag; + } + break; + case StateEndTag: + if (c=='}') // %} + { + // found tag! + state=StateText; + addToken(tokens,m_data.data(),line,lastTokenPos, + emptyOutputLine ? startLinePos : markStartPos, + TemplateToken::Text); + addToken(tokens,m_data.data(),line,markStartPos+2, + pos-1,TemplateToken::Block); + lastTokenPos = pos+1; + } + else // something else + { + if (c=='\n') + { + warn(m_fileName,line,"unexpected new line inside {%%...%%} block"); + } + state=StateTag; + } + break; + case StateComment: + if (c=='\n') + { + warn(m_fileName,line,"unexpected new line inside {#...#} block"); + } + else if (c=='#') // #} or something else + { + state=StateEndComment; + } + break; + case StateEndComment: + if (c=='}') // #} + { + // found comment tag! + state=StateText; + addToken(tokens,m_data.data(),line,lastTokenPos, + emptyOutputLine ? startLinePos : markStartPos, + TemplateToken::Text); + lastTokenPos = pos+1; + } + else // something else + { + if (c=='\n') + { + warn(m_fileName,line,"unexpected new line inside {#...#} block"); + } + state=StateComment; + } + break; + case StateMaybeVar: + switch (c) + { + case '#': // {{# + state=StateComment; + markStartPos=pos-1; + break; + case '%': // {{% + state=StateTag; + markStartPos=pos-1; + break; + default: // {{ + state=StateVariable; + break; + } + break; + case StateVariable: + if (c=='\n') + { + warn(m_fileName,line,"unexpected new line inside {{...}} block"); + } + else if (c=='}') // }} or something else + { + state=StateEndVariable; + } + break; + case StateEndVariable: + if (c=='}') // }} + { + // found variable tag! + state=StateText; + addToken(tokens,m_data.data(),line,lastTokenPos, + emptyOutputLine ? startLinePos : markStartPos, + TemplateToken::Text); + addToken(tokens,m_data.data(),line,markStartPos+2, + pos-1,TemplateToken::Variable); + lastTokenPos = pos+1; + } + else // something else + { + if (c=='\n') + { + warn(m_fileName,line,"unexpected new line inside {{...}} block"); + } + state=StateVariable; + } + break; + } + if (c=='\n') // new line + { + state=StateText; + startLinePos=pos+1; + // if the current line only contain commands and whitespace, + // then skip it in the output by moving lastTokenPos + if (markStartPos!=-1 && emptyOutputLine) lastTokenPos = startLinePos; + // reset markers + markStartPos=-1; + line++; + emptyOutputLine=TRUE; + } + } + if (lastTokenPos &tokens, + const char *data,int line, + int startPos,int endPos, + TemplateToken::Type type) +{ + if (startPos &tokens) : + m_templateName(templateName), m_tokens(tokens) +{ +} + +void TemplateParser::parse( + TemplateNode *parent,int line,const QStrList &stopAt, + QList &nodes) +{ + TRACE(("{TemplateParser::parse\n")); + // process the tokens. Build node list + while (hasNextToken()) + { + TemplateToken *tok = takeNextToken(); + //printf("%p:Token type=%d data='%s' line=%d\n", + // parent,tok->type,tok->data.data(),tok->line); + switch(tok->type) + { + case TemplateToken::Text: + nodes.append(new TemplateNodeText(this,parent,tok->line,tok->data)); + break; + case TemplateToken::Variable: + nodes.append(new TemplateNodeVariable(this,parent,tok->line,tok->data)); + break; + case TemplateToken::Block: + { + QCString command = tok->data; + int sep = command.find(' '); + if (sep!=-1) + { + command=command.left(sep); + } + if (stopAt.contains(command)) + { + prependToken(tok); + TRACE(("}TemplateParser::parse: stop\n")); + return; + } + QCString arg; + if (sep!=-1) + { + arg = tok->data.mid(sep+1); + } + TemplateNode *node = TemplateNodeFactory::instance()-> + create(command,this,parent,tok->line,arg); + if (node) + { + nodes.append(node); + } + else if (command=="empty" || command=="else" || + command=="endif" || command=="endfor" || + command=="endblock" || command=="endwith" || + command=="endrecursetree") + { + warn(m_templateName,tok->line,"Found tag '%s' without matching start tag",command.data()); + } + else + { + warn(m_templateName,tok->line,"Unknown tag '%s'",command.data()); + } + } + break; + } + delete tok; + } + if (!stopAt.isEmpty()) + { + QStrListIterator it(stopAt); + const char *s; + QCString options; + for (it.toFirst();(s=it.current());++it) + { + if (!options.isEmpty()) options+=", "; + options+=s; + } + warn(m_templateName,line,"Unclosed tag in template, expected one of: %s", + options.data()); + } + TRACE(("}TemplateParser::parse: last token\n")); +} + +bool TemplateParser::hasNextToken() const +{ + return !m_tokens.isEmpty(); +} + +TemplateToken *TemplateParser::takeNextToken() +{ + return m_tokens.take(0); +} + +const TemplateToken *TemplateParser::currentToken() const +{ + return m_tokens.first(); +}; + +void TemplateParser::removeNextToken() +{ + m_tokens.removeFirst(); +} + +void TemplateParser::prependToken(const TemplateToken *token) +{ + m_tokens.prepend(token); +} + + + +//---------------------------------------------------------- + + +TemplateImpl::TemplateImpl(TemplateEngine *engine,const QCString &name,const QCString &data) + : TemplateNode(0) +{ + m_name = name; + m_engine = engine; + TemplateLexer lexer(name,data); + QList tokens; + tokens.setAutoDelete(TRUE); + lexer.tokenize(tokens); + TemplateParser parser(name,tokens); + parser.parse(this,1,QStrList(),m_nodes); +} + +void TemplateImpl::render(FTextStream &ts, TemplateContext *c) +{ + if (!m_nodes.isEmpty()) + { + TemplateNodeExtend *ne = dynamic_cast(m_nodes.getFirst()); + if (ne==0) // normal template, add blocks to block context + { + TemplateContextImpl *ci = dynamic_cast(c); + TemplateBlockContext *bc = ci->blockContext(); + QListIterator li(m_nodes); + TemplateNode *n; + for (li.toFirst();(n=li.current());++li) + { + TemplateNodeBlock *nb = dynamic_cast(n); + if (nb) + { + bc->add(nb); + } + } + } + m_nodes.render(ts,c); + } +} + +//---------------------------------------------------------- + +/** @brief Private data of the template engine */ +class TemplateEngine::Private +{ + public: + Private() { templates.setAutoDelete(TRUE); } + QList