diff options
author | Dimitri van Heesch <dimitri@stack.nl> | 2013-08-26 08:18:59 (GMT) |
---|---|---|
committer | Dimitri van Heesch <dimitri@stack.nl> | 2013-10-21 18:21:33 (GMT) |
commit | 784a67d23ff81275c95af4199179da094953be2e (patch) | |
tree | 90a165d2f3327f3ad94775f616c3440d37813702 /src | |
parent | 74815268dd88f2cfb4473462cef3c33eebd5516a (diff) | |
download | Doxygen-784a67d23ff81275c95af4199179da094953be2e.zip Doxygen-784a67d23ff81275c95af4199179da094953be2e.tar.gz Doxygen-784a67d23ff81275c95af4199179da094953be2e.tar.bz2 |
Added rudimentary support for django like template system for output creation.
Diffstat (limited to 'src')
-rw-r--r-- | src/classdef.cpp | 49 | ||||
-rw-r--r-- | src/classdef.h | 8 | ||||
-rw-r--r-- | src/context.cpp | 2442 | ||||
-rw-r--r-- | src/context.h | 535 | ||||
-rw-r--r-- | src/definition.cpp | 2 | ||||
-rw-r--r-- | src/definition.h | 2 | ||||
-rw-r--r-- | src/dirdef.cpp | 8 | ||||
-rw-r--r-- | src/dirdef.h | 1 | ||||
-rw-r--r-- | src/doxygen.cpp | 21 | ||||
-rw-r--r-- | src/filedef.cpp | 6 | ||||
-rw-r--r-- | src/filedef.h | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | src/fortranscanner.l | 0 | ||||
-rw-r--r-- | src/ftextstream.h | 4 | ||||
-rw-r--r-- | src/index.cpp | 77 | ||||
-rw-r--r-- | src/libdoxygen.pro.in | 8 | ||||
-rw-r--r-- | src/namespacedef.cpp | 46 | ||||
-rw-r--r-- | src/namespacedef.h | 2 | ||||
-rw-r--r-- | src/template.cpp | 2968 | ||||
-rw-r--r-- | src/template.h | 395 | ||||
-rw-r--r-- | src/util.cpp | 69 | ||||
-rw-r--r-- | src/util.h | 5 |
21 files changed, 6532 insertions, 118 deletions
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 T> +class GenericConstIterator : public TemplateListIntf::ConstIterator +{ + public: + GenericConstIterator(const QList<T> &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<T> m_it; +}; + +//------------------------------------------------------------------------ + +// standard list implementation +template<class T> +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<count()) + { + result = m_children.at(index); + } + return result; + } + TemplateListIntf::ConstIterator *createIterator() const + { + return new GenericConstIterator<T>(m_children); + } + + void append(T *ctn) + { + m_children.append(ctn); + } + bool isEmpty() const + { + return m_children.isEmpty(); + } + private: + mutable QList<T> m_children; +}; + +//------------------------------------------------------------------------ + +/** @brief Helper class to map a property name to a handler member function */ +template<typename T> +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<PropertyFunc> 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<TemplateList> 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<DoxygenContext::Private> +{ + 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<TranslateContext::Private> +{ + public: + static QCString generatedAtFunc(const void *obj,const QValueList<TemplateVariant> &args) + { + return ((TranslateContext::Private*)obj)->generatedAt(args); + } + QCString generatedAt(const QValueList<TemplateVariant> &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<typename T> +class DefinitionContext : public PropertyMapper<T> +{ + public: + DefinitionContext(const T *super,Definition *d) : m_def(d), m_detailsCached(FALSE) + { + //%% string name: the name of the symbol + PropertyMapper<T>::insert("name", new typename PropertyMapper<T>::PropertyFunc(super,&DefinitionContext::name)); + //%% string relPath: the relative path to the root of the output (CREATE_SUBDIRS) + PropertyMapper<T>::insert("relPath", new typename PropertyMapper<T>::PropertyFunc(super,&DefinitionContext::relPath)); + //%% string fileName: the file name of the output file associated with the symbol (without extension) + PropertyMapper<T>::insert("fileName", new typename PropertyMapper<T>::PropertyFunc(super,&DefinitionContext::fileName)); + //%% string details: the detailed documentation for this symbol + PropertyMapper<T>::insert("details", new typename PropertyMapper<T>::PropertyFunc(super,&DefinitionContext::details)); + //%% string brief: the brief description for this symbol + PropertyMapper<T>::insert("brief", new typename PropertyMapper<T>::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<ClassContext::Private> +{ + 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<NamespaceContext::Private> +{ + 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<FileContext::Private> +{ + 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<DirContext::Private> +{ + 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<PageContext::Private> +{ + 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<ModuleContext::Private> +{ + 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<ClassContext> +{ + 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<ClassInheritanceNodeContext::Private> +{ + 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<ClassInheritanceNodeContext> 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<ClassInheritanceNodeContext> +{ + 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 && index<count()) + { + result = p->at(index); + } + return result; +} + +TemplateListIntf::ConstIterator *ClassInheritanceContext::createIterator() const +{ + return p->createIterator(); +} + +//------------------------------------------------------------------------ + +//%% struct ClassHierarchy: inheritance tree +//%% { +class ClassHierarchyContext::Private : public PropertyMapper<ClassHierarchyContext::Private> +{ + 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<NestingNodeContext::Private> +{ + 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<NestingNodeContext> +{ + 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<ClassTreeContext::Private> +{ + 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<NamespaceContext> +{ + 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<NamespaceTreeContext::Private> +{ + 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<FileContext> +{ + 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<DirFileNodeContext::Private> +{ + 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<DirFileNodeContext> +{ + public: + void addDirs(const DirSDict &dirDict) + { + SDict<DirDef>::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<DirDef> 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<FileDef> 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<FileTreeContext::Private> +{ + 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<PageNodeContext::Private> +{ + 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<PageNodeContext> +{ + public: + void addPages(const PageSDict &pages,bool rootOnly) + { + SDict<PageDef>::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<PageTreeContext::Private> +{ + 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<PageListContext::Private> +{ + 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<PageContext> 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<ModuleNodeContext::Private> +{ + 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<ModuleNodeContext> +{ + 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<GroupDef> 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<ModuleTreeContext::Private> +{ + 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<ExampleListContext::Private> +{ + 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<UsedDir> *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<FileDef> 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<void> 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 index 2102a20..2102a20 100755..100644 --- a/src/fortranscanner.l +++ b/src/fortranscanner.l 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<GroupDef> 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<LayoutDocEntry> 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 <stdio.h> +#include <stdarg.h> + +#include <qlist.h> +#include <qarray.h> +#include <qdict.h> +#include <qstrlist.h> +#include <qvaluelist.h> +#include <qstack.h> +#include <qfile.h> +#include <qregexp.h> + +#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<QCString> split(const QCString &str,const QCString &sep,bool allowEmptyEntries=FALSE,bool cleanup=TRUE) +{ + QValueList<QCString> 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<TemplateVariant> &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<TemplateVariant> 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<TemplateVariant> 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<TemplateVariant>::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<TemplateNodeBlock> > 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<TemplateVariant> > 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 T> class AutoRegister + { + public: + AutoRegister<T>(const QCString &key) + { + TemplateFilterFactory::instance()->registerFilter(key,&T::apply); + } + }; + + private: + QDict<void> m_registry; +}; + +// register a handlers for each filter we support +static TemplateFilterFactory::AutoRegister<FilterAdd> fAdd("add"); +static TemplateFilterFactory::AutoRegister<FilterPrepend> fPrepend("prepend"); +static TemplateFilterFactory::AutoRegister<FilterLength> fLength("length"); +static TemplateFilterFactory::AutoRegister<FilterDefault> fDefault("default"); +static TemplateFilterFactory::AutoRegister<FilterStripPath> fStripPath("strippath"); +static TemplateFilterFactory::AutoRegister<FilterNoWrap> 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<TemplateContextImpl*>(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<TemplateContextImpl*>(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.toString(); + } + else + { + return lhs.toInt()<rhs.toInt(); + } + case Operator::Greater: + if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String) + { + return !(lhs.toString()<rhs.toString()); + } + else + { + return lhs.toInt()>rhs.toInt(); + } + case Operator::LessEqual: + if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String) + { + return lhs.toString()==rhs.toString() || lhs.toString()<rhs.toString(); + } + else + { + return lhs.toInt()<=rhs.toInt(); + } + case Operator::GreaterEqual: + if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String) + { + return lhs.toString()==rhs.toString() || !(lhs.toString()<rhs.toString()); + } + else + { + return lhs.toInt()>=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<TemplateToken> &tokens); + void parse(TemplateNode *parent,int line,const QStrList &stopAt, + QList<TemplateNode> &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<TemplateToken> &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<TemplateNode> +{ + public: + TemplateNodeList() + { + setAutoDelete(TRUE); + } + void render(FTextStream &ts,TemplateContext *c) + { + TRACE(("{TemplateNodeList::render\n")); + QListIterator<TemplateNode> 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("<unknown>"), 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<TemplateVariant> > it(m_contextStack); + QDict<TemplateVariant> *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<TemplateVariant> *dict = new QDict<TemplateVariant>; + 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<i)) // no arguments or arg belongs to filter + { + m_var = expParser.parseVariable(var); + } + else + { + QValueList<QCString> args = split(var.mid(i+1),","); + for (uint j=0;j<args.count();j++) + { + ExprAst *expr = expParser.parsePrimary(args[j]); + if (expr) + { + m_args.append(expr); + } + } + m_var = expParser.parseVariable(var.left(i)); + } + } + ~TemplateNodeVariable() + { + delete m_var; + } + + void render(FTextStream &ts, TemplateContext *c) + { + TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); + ci->setLocation(m_templateName,m_line); + QValueList<TemplateVariant> args; + for (uint i=0;i<m_args.count();i++) + { + TemplateVariant v = m_args.at(i)->resolve(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<ExprAst> m_args; +}; + +//---------------------------------------------------------- + +/** @brief Helper class for creating template AST tag nodes and returning + * the template for a given node. + */ +template<class T> 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<TemplateImpl*>(root); + } + protected: + QCString m_templateName; + int m_line; +}; + +//---------------------------------------------------------- + +/** @brief Class representing an 'if' tag in a template */ +class TemplateNodeIf : public TemplateNodeCreator<TemplateNodeIf> +{ + public: + TemplateNodeIf(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) : + TemplateNodeCreator<TemplateNodeIf>(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<TemplateContextImpl*>(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<TemplateNodeFor> +{ + public: + TemplateNodeFor(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator<TemplateNodeFor>(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<TemplateContextImpl*>(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;i<m_vars.count();i++,vi++) + { + c->set(m_vars[vi],v.toStruct()->get(m_vars[vi])); + } + } + for (;vi<m_vars.count();vi++) + { + c->set(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<QCString> m_vars; + TemplateNodeList m_loopNodes; + TemplateNodeList m_emptyNodes; +}; + +//---------------------------------------------------------- + +/** @brief Class representing a 'block' tag in a template */ +class TemplateNodeBlock : public TemplateNodeCreator<TemplateNodeBlock> +{ + public: + TemplateNodeBlock(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator<TemplateNodeBlock>(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<TemplateContextImpl*>(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<TemplateNodeExtend> +{ + public: + TemplateNodeExtend(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator<TemplateNodeExtend>(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<TemplateContextImpl*>(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<TemplateImpl*>(t->engine()->loadByName(extendFile)); + if (baseTemplate) + { + // fill block context + TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); + TemplateBlockContext *bc = ci->blockContext(); + + // add overruling blocks to the context + QListIterator<TemplateNode> li(m_nodes); + TemplateNode *n; + for (li.toFirst();(n=li.current());++li) + { + TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(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<TemplateNodeInclude> +{ + public: + TemplateNodeInclude(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator<TemplateNodeInclude>(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<TemplateContextImpl*>(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<TemplateImpl*>(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<TemplateNodeCreate> +{ + public: + TemplateNodeCreate(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator<TemplateNodeCreate>(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<TemplateContextImpl*>(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<TemplateImpl*>(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<TemplateNodeTree> +{ + 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<TemplateNodeTree>(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<TemplateVariant> &) + { + 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<TemplateContextImpl*>(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<TemplateNodeWith> +{ + 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<TemplateNodeWith>(parser,parent,line) + { + m_args.setAutoDelete(TRUE); + ExpressionParser expParser(parser->templateName(),line); + QValueList<QCString> args = split(data," "); + QValueListIterator<QCString> 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<TemplateContextImpl*>(c); + c->push(); + QListIterator<Mapping> 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<Mapping> 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 T> class AutoRegister + { + public: + AutoRegister<T>(const QCString &key) + { + TemplateNodeFactory::instance()->registerTemplateNode(key,T::createInstance); + } + }; + + private: + QDict<void> m_registry; +}; + +// register a handler for each start tag we support +static TemplateNodeFactory::AutoRegister<TemplateNodeIf> autoRefIf("if"); +static TemplateNodeFactory::AutoRegister<TemplateNodeFor> autoRefFor("for"); +static TemplateNodeFactory::AutoRegister<TemplateNodeTree> autoRefTree("recursetree"); +static TemplateNodeFactory::AutoRegister<TemplateNodeWith> autoRefWith("with"); +static TemplateNodeFactory::AutoRegister<TemplateNodeBlock> autoRefBlock("block"); +static TemplateNodeFactory::AutoRegister<TemplateNodeExtend> autoRefExtend("extend"); +static TemplateNodeFactory::AutoRegister<TemplateNodeCreate> autoRefCreate("create"); +static TemplateNodeFactory::AutoRegister<TemplateNodeInclude> autoRefInclude("include"); + +//---------------------------------------------------------- + +TemplateBlockContext::TemplateBlockContext() : m_blocks(257) +{ + m_blocks.setAutoDelete(TRUE); +} + +TemplateNodeBlock *TemplateBlockContext::get(const QCString &name) const +{ + QList<TemplateNodeBlock> *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<TemplateNodeBlock> *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<TemplateNodeBlock> *list = m_blocks.find(block->name()); + if (list==0) + { + list = new QList<TemplateNodeBlock>; + m_blocks.insert(block->name(),list); + } + list->prepend(block); +} + +void TemplateBlockContext::add(TemplateBlockContext *ctx) +{ + QDictIterator< QList<TemplateNodeBlock> > di(ctx->m_blocks); + QList<TemplateNodeBlock> *list; + for (di.toFirst();(list=di.current());++di) + { + QListIterator<TemplateNodeBlock> 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<TemplateNodeBlock> *list = m_blocks.find(block->name()); + if (list==0) + { + list = new QList<TemplateNodeBlock>; + 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<TemplateToken> &tokens); + private: + void addToken(QList<TemplateToken> &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<TemplateToken> &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<pos) + { + addToken(tokens,m_data.data(),line, + lastTokenPos,pos, + TemplateToken::Text); + } +} + +void TemplateLexer::addToken(QList<TemplateToken> &tokens, + const char *data,int line, + int startPos,int endPos, + TemplateToken::Type type) +{ + if (startPos<endPos) + { + int len = endPos-startPos+1; + QCString text(len+1); + qstrncpy(text.data(),data+startPos,len); + text[len]='\0'; + if (type!=TemplateToken::Text) text = text.stripWhiteSpace(); + tokens.append(new TemplateToken(type,text,line)); + } +} + +//---------------------------------------------------------- + +TemplateParser::TemplateParser(const QCString &templateName, + QList<TemplateToken> &tokens) : + m_templateName(templateName), m_tokens(tokens) +{ +} + +void TemplateParser::parse( + TemplateNode *parent,int line,const QStrList &stopAt, + QList<TemplateNode> &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<TemplateToken> 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<TemplateNodeExtend*>(m_nodes.getFirst()); + if (ne==0) // normal template, add blocks to block context + { + TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); + TemplateBlockContext *bc = ci->blockContext(); + QListIterator<TemplateNode> li(m_nodes); + TemplateNode *n; + for (li.toFirst();(n=li.current());++li) + { + TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(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<Template> templates; +}; + +TemplateEngine::TemplateEngine() +{ + p = new Private; +} + +TemplateEngine::~TemplateEngine() +{ + delete p; +} + +TemplateContext *TemplateEngine::createContext() const +{ + return new TemplateContextImpl; +} + +Template *TemplateEngine::newTemplate(const QCString &name,const QCString &data) +{ + Template *t = new TemplateImpl(this,name,data); + p->templates.append(t); + return t; +} + + +Template *TemplateEngine::loadByName(const QCString &fileName) +{ + Template *t=0; + QFile f(fileName); + if (f.open(IO_ReadOnly)) + { + uint size=f.size(); + char *data = new char[size+1]; + if (data) + { + data[size]=0; + if (f.readBlock(data,f.size())) + { + t = new TemplateImpl(this,fileName,data); + } + delete[] data; + } + } + return t; +} + diff --git a/src/template.h b/src/template.h new file mode 100644 index 0000000..454a70b --- /dev/null +++ b/src/template.h @@ -0,0 +1,395 @@ +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include <qcstring.h> +#include <qvaluelist.h> + +class FTextStream; + +class TemplateListIntf; +class TemplateStructIntf; +class TemplateEngine; + +/** @defgroup template_api Template API + * + * This is the API for a + * <a href="https://docs.djangoproject.com/en/1.6/topics/templates/">Django</a> + * compatible template system written in C++. + * It is somewhat inspired by Stephen Kelly's + * <a href="http://www.gitorious.org/grantlee/pages/Home">Grantlee</a>. + * + * A template is simply a text file. + * A template contains \b variables, which get replaced with values when the + * template is evaluated, and \b tags, which control the logic of the template. + * + * Variables look like this: `{{ variable }}` + * When the template engine encounters a variable, it evaluates that variable and + * replaces it with the result. Variable names consist of any combination of + * alphanumeric characters and the underscore ("_"). + * Use a dot (.) to access attributes of a variable. + * + * One can modify variables for display by using \b filters, for example: + * `{{ value|default:"nothing" }}` + * + * Tags look like this: `{% tag %}`. Tags are more complex than variables: + * Some create text in the output, some control flow by performing loops or logic, + * and some load external information into the template to be used by later variables. + * + * To comment-out part of a line in a template, use the comment syntax: + * `{# comment text #}`. + * + * Supported Django tags: + * - `for ... empty ... endfor` + * - `if ... else ... endif` + * - `block ... endblock` + * - `extends` + * - `include` + * + * Supported Django filters: + * - `default` + * - `length` + * - `add` + * + * Extension tags: + * - `create` which instantiates a template and writes the result to a file. + * The syntax is `{% create 'filename' from 'template' %}`. + * + * @{ + */ + +/** @brief Variant type which can hold one value of a fixed set of types. */ +class TemplateVariant +{ + public: + /** Signature of the callback function, used for function type variants */ + typedef QCString (*FuncType)(const void *obj, const QValueList<TemplateVariant> &args); + + /** Types of data that can be stored in a TemplateVariant */ + enum Type { None, Bool, Integer, String, Struct, List, Function }; + + /** Returns the type of the value stored in the variant */ + Type type() const; + + /** Returns TRUE if the variant holds a valid value, or FALSE otherwise */ + bool isValid() const; + + /** Constructs an invalid variant. */ + TemplateVariant(); + + /** Constructs a new variant with a boolean value \a b. */ + explicit TemplateVariant(bool b); + + /** Constructs a new variant with a integer value \a v. */ + TemplateVariant(int v); + + /** Constructs a new variant with a string value \a s. */ + TemplateVariant(const char *s); + + /** Constructs a new variant with a string value \a s. */ + TemplateVariant(const QCString &s); + + /** Constructs a new variant with a struct value \a s. + * @note. Only a pointer to the struct is stored. The caller + * is responsible to manage the memory for the struct object. + */ + TemplateVariant(const TemplateStructIntf *s); + + /** Constructs a new variant with a list value \a l. + * @note. Only a pointer to the struct is stored. The caller + * is responsible to manage the memory for the list object. + */ + TemplateVariant(const TemplateListIntf *l); + + /** Constructs a new variant which represents a function + * @param[in] obj Opaque user defined pointer, which + * is passed back when call() is invoked. + * @param[in] func Callback function to invoke when + * calling call() on this variant. + */ + TemplateVariant(const void *obj,FuncType func); + + /** Destroys the Variant object */ + ~TemplateVariant(); + + /** Constructs a copy of the variant, \a v, + * passed as the argument to this constructor. + */ + TemplateVariant(const TemplateVariant &v); + + /** Assigns the value of the variant \a v to this variant. */ + TemplateVariant &operator=(const TemplateVariant &v); + + /** Compares this QVariant with v and returns true if they are equal; + * otherwise returns false. + */ + bool operator==(TemplateVariant &other); + + /** Returns the variant as a string. */ + QCString toString() const; + + /** Returns the variant as a boolean. */ + bool toBool() const; + + /** Returns the variant as an integer. */ + int toInt() const; + + /** Returns the pointer to list referenced by this variant + * or 0 if this variant does not have list type. + */ + const TemplateListIntf *toList() const; + + /** Returns the pointer to struct referenced by this variant + * or 0 if this variant does not have struct type. + */ + const TemplateStructIntf *toStruct() const; + + /** Return the result of apply this function with \a args. + * Returns an empty string if the variant type is not a function. + */ + QCString call(const QValueList<TemplateVariant> &args); + + /** Sets whether or not the value of the Variant should be + * escaped or written as-is (raw). + * @param[in] b TRUE means write as-is, FALSE means apply escaping. + */ + void setRaw(bool b); + + /** Returns whether or not the value of the Value is raw. + * @see setRaw() + */ + bool raw() const; + + private: + class Private; + Private *p; +}; + +//------------------------------------------------------------------------ + +/** @brief Abstract read-only interface for a context value of type list. + * @note The values of the list are TemplateVariants. + */ +class TemplateListIntf +{ + public: + /** @brief Abstract interface for a iterator of a list. */ + class ConstIterator + { + public: + /** Destructor for the iterator */ + virtual ~ConstIterator() {} + /** Moves iterator to the first element in the list */ + virtual void toFirst() = 0; + /** Moves iterator to the last element in the list */ + virtual void toLast() = 0; + /** Moves iterator to the next element in the list */ + virtual void toNext() = 0; + /** Moves iterator to the previous element in the list */ + virtual void toPrev() = 0; + /* Returns TRUE if the iterator points to a valid element + * in the list, or FALSE otherwise. + * If TRUE is returned, the value pointed to be the + * iterator is assigned to \a v. + */ + virtual bool current(TemplateVariant &v) const = 0; + }; + + /** Destroys the list */ + virtual ~TemplateListIntf() {} + + /** Returns the number of elements in the list */ + virtual int count() const = 0; + + /** Returns the element at index position \a index. */ + virtual TemplateVariant at(int index) const = 0; + + /** Creates a new iterator for this list. + * @note the user should call delete on the returned pointer. + */ + virtual TemplateListIntf::ConstIterator *createIterator() const = 0; +}; + +/** @brief Default implementation of a context value of type list. */ +class TemplateList : public TemplateListIntf +{ + public: + /** Creates a list */ + TemplateList(); + /** Destroys the list */ + ~TemplateList(); + + // TemplateListIntf methods + virtual int count() const; + virtual TemplateVariant at(int index) const; + virtual TemplateListIntf::ConstIterator *createIterator() const; + + /** Appends element \a v to the end of the list */ + virtual void append(const TemplateVariant &v); + + private: + friend class TemplateListConstIterator; + class Private; + Private *p; +}; + +//------------------------------------------------------------------------ + +/** @brief Abstract interface for a context value of type struct. */ +class TemplateStructIntf +{ + public: + /** Destroys the struct */ + virtual ~TemplateStructIntf() {} + + /** Gets the value for a field name. + * @param[in] name The name of the field. + */ + virtual TemplateVariant get(const char *name) const = 0; +}; + + +/** @brief Default implementation of a context value of type struct. */ +class TemplateStruct : public TemplateStructIntf +{ + public: + /** Creates a struct */ + TemplateStruct(); + /** Destroys the struct */ + virtual ~TemplateStruct(); + + // TemplateStructIntf methods + virtual TemplateVariant get(const char *name) const; + + /** Sets the value the field of a struct + * @param[in] name The name of the field. + * @param[in] v The value to set. + */ + virtual void set(const char *name,const TemplateVariant &v); + + private: + class Private; + Private *p; +}; + +//------------------------------------------------------------------------ + +/** @brief Interface used to escape characters in a string */ +class TemplateEscapeIntf +{ + public: + /** Returns the \a input after escaping certain characters */ + virtual QCString escape(const QCString &input) = 0; +}; + +//------------------------------------------------------------------------ + +/** @brief Abstract interface for a template context. + * + * A Context consists of a stack of dictionaries. + * A dictionary consists of a mapping of string keys onto TemplateVariant values. + * A key is searched starting with the dictionary at the top of the stack + * and searching downwards until it is found. The stack is used to create + * local scopes. + * @note This object must be created by TemplateEngine + */ +class TemplateContext +{ + public: + virtual ~TemplateContext() {} + + /** Push a new scope on the stack. */ + virtual void push() = 0; + + /** Pop the current scope from the stack. */ + virtual void pop() = 0; + + /** Sets a value in the current scope. + * @param[in] name The name of the value; the key in the dictionary. + * @param[in] v The value associated with the key. + * @note When a given key is already present, + * its value will be replaced by \a v + */ + virtual void set(const char *name,const TemplateVariant &v) = 0; + + /** Gets the value for a given key + * @param[in] name The name of key. + * @returns The value, which can be an invalid variant in case the + * key was not found. + */ + virtual TemplateVariant get(const QCString &name) const = 0; + + /** Returns a pointer to the value corresponding to a given key. + * @param[in] name The name of key. + * @returns A pointer to the value, or 0 in case the key was not found. + */ + virtual const TemplateVariant *getRef(const QCString &name) const = 0; + + /** When files are create (i.e. by {% create ... %}) they written + * to the directory \a dir. + */ + virtual void setOutputDirectory(const QCString &dir) = 0; + + /** Sets the interface that will be used for escaping the result + * of variable expansion before writing it to the output. + */ + virtual void setEscapeIntf(TemplateEscapeIntf *intf) = 0; +}; + +//------------------------------------------------------------------------ + +/** @brief Abstract interface for a template. + * @note Must be created by TemplateEngine + */ +class Template +{ + public: + /** Destructor */ + virtual ~Template() {} + + /** Renders a template instance to a stream. + * @param[in] ts The text stream to write the results to. + * @param[in] c The context containing data that can be used + * when instantiating the template. + */ + virtual void render(FTextStream &ts,TemplateContext *c) = 0; +}; + +//------------------------------------------------------------------------ + +/** @brief Engine to create templates and template contexts. */ +class TemplateEngine +{ + public: + /** Create a template engine. */ + TemplateEngine(); + + /** Destroys the template engine. */ + ~TemplateEngine(); + + /** Creates a new context that can be using to render a template. + * @see Template::render() + */ + TemplateContext *createContext() const; + + /** Creates a new template whose contents are given by a string. + * @param[in] name The name of the template. + * @param[in] data The contents of the template. + * @return the new template, the caller will be the owner. + */ + Template *newTemplate(const QCString &name,const QCString &data); + + /** Creates a new template whole contents are in a file. + * @param[in] fileName The name of the file containing the + * template data + * @return the new template, the caller will be the owner. + */ + Template *loadByName(const QCString &fileName); + + private: + class Private; + Private *p; +}; + +/** @} */ + +#endif diff --git a/src/util.cpp b/src/util.cpp index 380ae94..c127362 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5076,6 +5076,34 @@ static void initBaseClassHierarchy(BaseClassList *bcl) cd->visited=FALSE; } } +//---------------------------------------------------------------------------- + +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; +} + //---------------------------------------------------------------------------- @@ -7988,3 +8016,44 @@ uint getUtf8CodeToUpper( const QCString& s, int idx ) //-------------------------------------------------------------------------------------- +bool namespaceHasVisibleChild(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 (namespaceHasVisibleChild(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; +} + +//---------------------------------------------------------------------------- + +bool classVisibleInIndex(ClassDef *cd) +{ + static bool allExternals = Config_getBool("ALLEXTERNALS"); + return (allExternals && cd->isLinkable()) || cd->isLinkableInProject(); +} + @@ -257,6 +257,9 @@ QCString replaceAnonymousScopes(const QCString &s,const char *replacement=0); void initClassHierarchy(ClassSDict *cl); bool hasVisibleRoot(BaseClassList *bcl); +bool classHasVisibleChildren(ClassDef *cd); +bool namespaceHasVisibleChild(NamespaceDef *nd,bool includeClasses); +bool classVisibleInIndex(ClassDef *cd); int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level=0); Protection classInheritedProtectionLevel(ClassDef *cd,ClassDef *bcd,Protection prot=Public,int level=0); @@ -443,5 +446,7 @@ uint getUtf8Code( const QCString& s, int idx ); uint getUtf8CodeToLower( const QCString& s, int idx ); uint getUtf8CodeToUpper( const QCString& s, int idx ); + + #endif |