diff options
Diffstat (limited to 'src/template.cpp')
-rw-r--r-- | src/template.cpp | 5465 |
1 files changed, 0 insertions, 5465 deletions
diff --git a/src/template.cpp b/src/template.cpp deleted file mode 100644 index f1c10ec..0000000 --- a/src/template.cpp +++ /dev/null @@ -1,5465 +0,0 @@ -/****************************************************************************** - * - * Copyright (C) 1997-2015 by Dimitri van Heesch. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation under the terms of the GNU General Public License is hereby - * granted. No representations are made about the suitability of this software - * for any purpose. It is provided "as is" without express or implied warranty. - * See the GNU General Public License for more details. - * - * Documents produced by Doxygen are derivative works derived from the - * input used in their production; they are not affected by this license. - * - */ - -#include "template.h" - -#include <vector> -#include <algorithm> -#include <unordered_map> -#include <deque> -#include <cstdio> -#include <fstream> -#include <sstream> - -#include "message.h" -#include "util.h" -#include "resourcemgr.h" -#include "portable.h" -#include "regex.h" -#include "fileinfo.h" -#include "dir.h" -#include "utf8.h" - -#define ENABLE_TRACING 0 - -#if ENABLE_TRACING -#define TRACE(x) printf x -#else -#define TRACE(x) -#endif - -class TemplateToken; - -//------------------------------------------------------------------- - -static std::vector<QCString> split(const QCString &str,const QCString &sep, - bool allowEmptyEntries=FALSE,bool cleanup=TRUE) -{ - std::vector<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.push_back(str.mid(j,i-j).stripWhiteSpace()); - } - else - { - lst.push_back(str.mid(j,i-j)); - } - } - else if (allowEmptyEntries) - { - lst.push_back(""); - } - 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.push_back(str.mid(j,l-j+1).stripWhiteSpace()); - } - else - { - lst.push_back(str.mid(j,l-j+1)); - } - } - else if (allowEmptyEntries) - { - lst.push_back(""); - } - - return lst; -} - -//---------------------------------------------------------------------------- - -/** Strips spaces surrounding `=` from string \a in, so - * `foo = 10 bar=5 baz= 'hello'` will become `foo=10 bar=5 baz='hello'` - */ -static void removeSpacesAroundEquals(QCString &s) -{ - //printf(">removeSpacesAroundEquals(%s)\n",qPrint(s)); - uint i=0, dstIdx=0, l=s.length(); - while (i<l) - { - char c = s[i++]; - if (c==' ') - { - bool found=false; - // look ahead for space or '=' - uint j=i; - while (j<l && (s[j]==' '|| s[j]=='=')) - { - if (s[j]=='=') found=true; - j++; - } - if (found) // found a '=', write it without spaces - { - c = '='; - i=j; - } - } - s[dstIdx++]=c; - } - s.resize(dstIdx+1); - //printf("<removeSpacesAroundEquals(%s)\n",qPrint(s)); -} - -//---------------------------------------------------------------------------- - -#if ENABLE_TRACING -static QCString replace(const QCString &s,char csrc,char cdst) -{ - QCString result = s; - for (uint i=0;i<result.length();i++) - { - if (result[i]==csrc) result[i]=cdst; - } - return result; -} -#endif - -//- Template struct & list forward declarations ------------------------------ - -class TemplateStruct; -using TemplateStructPtr = std::shared_ptr<TemplateStruct>; - -/** @brief Default implementation of a context value of type struct. */ -class TemplateStruct : public TemplateStructIntf -{ - public: - // TemplateStructIntf methods - virtual TemplateVariant get(const QCString &name) const; - virtual StringVector fields() const; - - /** Creates an instance and returns a shared pointer to it */ - static TemplateStructPtr alloc(); - - /** 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 QCString &name,const TemplateVariant &v); - - /** Removes the field from the struct */ - virtual void remove(const QCString &name); - - /** Creates a struct */ - TemplateStruct() = default; //{ printf("%p:TemplateStruct::TemplateStruct()\n",(void*)this); } - /** Destroys the struct */ - virtual ~TemplateStruct() = default; //{ printf("%p:TemplateStruct::~TemplateStruct()\n",(void*)this); } - - private: - - std::unordered_map<std::string,TemplateVariant> m_fields; -}; - -void TemplateStruct::set(const QCString &name,const TemplateVariant &v) -{ - auto it = m_fields.find(name.str()); - if (it!=m_fields.end()) // change existing field - { - it->second = v; - } - else // insert new field - { - m_fields.insert(std::make_pair(name.str(),v)); - } -} - -void TemplateStruct::remove(const QCString &name) -{ - auto it = m_fields.find(name.str()); - if (it!=m_fields.end()) - { - m_fields.erase(it); - } -} - -TemplateVariant TemplateStruct::get(const QCString &name) const -{ - auto it = m_fields.find(name.str()); - return it!=m_fields.end() ? it->second : TemplateVariant(); -} - -StringVector TemplateStruct::fields() const -{ - StringVector result; - for (const auto &kv : m_fields) - { - result.push_back(kv.first); - } - std::sort(result.begin(),result.end()); - return result; -} - -TemplateStructPtr TemplateStruct::alloc() -{ - return std::make_shared<TemplateStruct>(); -} - -class TemplateList; -using TemplateListPtr = std::shared_ptr<TemplateList>; - -//- Template list implementation ---------------------------------------------- - -// iterator support -template<class List> -class TemplateListGenericConstIterator : public TemplateListIntf::ConstIterator -{ - public: - TemplateListGenericConstIterator(const List &l) : m_list(l) { m_index=0; } - virtual void toFirst() - { - m_index=0; - } - virtual void toLast() - { - int count = static_cast<int>(m_list.count()); - m_index = count>0 ? count-1 : 0; - } - virtual void toNext() - { - if (m_index<static_cast<int>(m_list.count())) { m_index++; } - } - virtual void toPrev() - { - if (m_index>=0) { --m_index; } - } - virtual bool current(TemplateVariant &v) const - { - if (m_index>=0 && m_index<static_cast<int>(m_list.count())) - { - v = m_list.at(m_index); - return TRUE; - } - else - { - v = TemplateVariant(); - return FALSE; - } - } - private: - const List &m_list; - int m_index = 0; -}; - -//------------------------------------------------------------------------------- -// -/** @brief Default implementation of a context value of type list. */ -class TemplateList : public TemplateListIntf -{ - public: - // TemplateListIntf methods - virtual size_t count() const - { - return m_elems.size(); - } - virtual TemplateVariant at(size_t index) const - { - return index < m_elems.size() ? m_elems[static_cast<int>(index)] : TemplateVariant(); - } - virtual TemplateListIntf::ConstIteratorPtr createIterator() const - { - return std::make_unique< TemplateListGenericConstIterator<TemplateList> >(*this); - } - - /** Creates an instance and returns a shared pointer to it */ - static TemplateListPtr alloc() - { - return std::make_shared<TemplateList>(); - } - - /** Appends element \a v to the end of the list */ - virtual void append(const TemplateVariant &v) - { - m_elems.push_back(v); - } - - void removeAt(size_t index) - { - if (index<m_elems.size()) - { - m_elems.erase(m_elems.begin()+index); - } - } - - void insertAt(size_t index,TemplateListPtr list) - { - auto it = m_elems.begin()+index; - m_elems.insert(it,list->m_elems.begin(),list->m_elems.end()); - } - - /** Creates a list */ - TemplateList() = default; //{ printf("%p:TemplateList::TemplateList()\n",(void*)this); } - /** Destroys the list */ - virtual ~TemplateList() = default; //{ printf("%p:TemplateList::~TemplateList()\n",(void*)this); } - - private: - TemplateVariantList m_elems; -}; - -//- TemplateVariant implementation ------------------------------------------- - -TemplateVariant::TemplateVariant(TemplateVariant &&v) -{ - m_raw = std::move(v.m_raw); - m_variant = std::move(v.m_variant); - v.m_variant = VariantT(); -} - -TemplateVariant &TemplateVariant::operator=(TemplateVariant &&v) -{ - m_raw = std::move(v.m_raw); - m_variant = std::move(v.m_variant); - v.m_variant = VariantT(); - return *this; -} - -bool TemplateVariant::operator==(TemplateVariant &other) const -{ - if (!isValid()) - { - return FALSE; - } - if (isBool() && other.isBool()) - { - return std::get<bool>(m_variant) == std::get<bool>(other.m_variant); - } - else if (isInt() && other.isInt()) - { - return std::get<int>(m_variant) == std::get<int>(other.m_variant); - } - else if (isList() && other.isList()) - { - return toList() == other.toList(); - } - else if ((isStruct() || isWeakStruct()) && (other.isStruct() || other.isWeakStruct())) - { - return toStruct() == other.toStruct(); - } - return toString()==other.toString(); -} - -bool TemplateVariant::toBool() const -{ - switch (type()) - { - case Type::None: return false; - case Type::Bool: return std::get<bool>(m_variant); - case Type::Int: return std::get<int>(m_variant)!=0; - case Type::String: return !std::get<QCString>(m_variant).isEmpty(); - case Type::Struct: return true; - case Type::List: return std::get<TemplateListIntfPtr>(m_variant)->count()!=0; - case Type::Function: return false; - case Type::WeakStruct: return true; - } - return FALSE; -} - -int TemplateVariant::toInt() const -{ - switch (type()) - { - case Type::None: return 0; - case Type::Bool: return std::get<bool>(m_variant) ? 1 : 0; - case Type::Int: return std::get<int>(m_variant); - case Type::String: return std::get<QCString>(m_variant).toInt(); - case Type::Struct: return 0; - case Type::List: return static_cast<int>(std::get<TemplateListIntfPtr>(m_variant)->count()); - case Type::Function: return 0; - case Type::WeakStruct: return 0; - } - return 0; -} - -QCString TemplateVariant::toString() const -{ - switch (type()) - { - case Type::None: return QCString(); - case Type::Bool: return std::get<bool>(m_variant) ? "true" : "false"; - case Type::Int: return QCString().setNum(std::get<int>(m_variant)); - case Type::String: return std::get<QCString>(m_variant); - case Type::Struct: return structToString(); - case Type::List: return listToString(); - case Type::Function: return "[function]"; - case Type::WeakStruct: return structToString(); - } - return QCString(); -} - -/** Return a string representation of the type of the value stored in the variant */ -const char *TemplateVariant::typeAsString() const -{ - switch (type()) - { - case Type::None: return "invalid"; - case Type::Bool: return "bool"; - case Type::Int: return "integer"; - case Type::String: return "string"; - case Type::Struct: return "struct"; - case Type::List: return "list"; - case Type::Function: return "function"; - case Type::WeakStruct: return "struct"; - } - return "invalid"; -} - -TemplateListIntfPtr TemplateVariant::toList() -{ - return isList() ? std::get<TemplateListIntfPtr>(m_variant) : nullptr; -} -const TemplateListIntfPtr TemplateVariant::toList() const -{ - return isList() ? std::get<TemplateListIntfPtr>(m_variant) : nullptr; -} - -TemplateStructIntfPtr TemplateVariant::toStruct() -{ - return isStruct() ? std::get<TemplateStructIntfPtr>(m_variant) : - isWeakStruct() ? std::get<TemplateStructIntfWeakPtr>(m_variant).lock() : - nullptr; -} -const TemplateStructIntfPtr TemplateVariant::toStruct() const -{ - return isStruct() ? std::get<TemplateStructIntfPtr>(m_variant) : - isWeakStruct() ? std::get<TemplateStructIntfWeakPtr>(m_variant).lock() : - nullptr; -} - -TemplateVariant TemplateVariant::call(const std::vector<TemplateVariant> &args) -{ - return isFunction() ? std::get<FunctionDelegate>(m_variant)(args) : TemplateVariant(); -} - -//- Template struct implementation -------------------------------------------- - - -/** @brief Private data of a template struct object */ -class TemplateImmutableStruct::Private -{ - public: - Private(std::initializer_list<StructField> fs) : fields(fs) {} - std::unordered_map<std::string,TemplateVariant> fields; -}; - -TemplateImmutableStruct::TemplateImmutableStruct( - std::initializer_list<StructField> fields) - : p(std::make_unique<Private>(fields)) -{ -} - -TemplateImmutableStruct::~TemplateImmutableStruct() -{ -} - -TemplateVariant TemplateImmutableStruct::get(const QCString &name) const -{ - auto it = p->fields.find(name.str()); - return it!=p->fields.end() ? it->second : TemplateVariant(); -} - -StringVector TemplateImmutableStruct::fields() const -{ - StringVector result; - for (const auto &kv : p->fields) - { - result.push_back(kv.first); - } - std::sort(result.begin(),result.end()); - return result; -} - -TemplateStructIntfPtr TemplateImmutableStruct::alloc(std::initializer_list<StructField> fields) -{ - return std::make_shared<TemplateImmutableStruct>(fields); -} - -//- Template immutable list implementation ------------------------------------ - -/** @brief Private data of a template immutable list object */ -class TemplateImmutableList::Private -{ - public: - Private(std::initializer_list<TemplateVariant> e) : elems(e) {} - Private(const TemplateVariantList &e) : elems(e) {} - TemplateVariantList elems; - int index = -1; -}; - -TemplateImmutableList::TemplateImmutableList(std::initializer_list<TemplateVariant> elements) - : p(std::make_unique<Private>(elements)) -{ -} - -TemplateImmutableList::TemplateImmutableList(const TemplateVariantList &elements) - : p(std::make_unique<Private>(elements)) -{ -} - -TemplateImmutableList::~TemplateImmutableList() -{ -} - -size_t TemplateImmutableList::count() const -{ - return p->elems.size(); -} - -TemplateListIntf::ConstIteratorPtr TemplateImmutableList::createIterator() const -{ - return std::make_unique< TemplateListGenericConstIterator<TemplateImmutableList> >(*this); -} - -TemplateVariant TemplateImmutableList::at(size_t index) const -{ - return index<p->elems.size() ? p->elems[static_cast<int>(index)] : TemplateVariant(); -} - -TemplateListIntfPtr TemplateImmutableList::alloc(std::initializer_list<TemplateVariant> elements) -{ - return std::make_shared<TemplateImmutableList>(elements); -} - -TemplateListIntfPtr TemplateImmutableList::alloc(const TemplateVariantList &elements) -{ - return std::make_shared<TemplateImmutableList>(elements); -} - -//- 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, Plus, Minus, Multiply, Divide, Modulo, Filter, Colon, Comma, - LeftParen, RightParen, - 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 Plus: return "+"; - case Minus: return "-"; - case Multiply: return "*"; - case Divide: return "/"; - case Modulo: return "%"; - case Filter: return "|"; - case Colon: return ":"; - case Comma: return ","; - case LeftParen: return "("; - case RightParen: 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); - void add(TemplateNodeBlock *block); - void add(TemplateBlockContext *ctx); - void push(TemplateNodeBlock *block); - void clear(); - using NodeBlockList = std::deque<TemplateNodeBlock*>; - private: - std::map< std::string, NodeBlockList > m_blocks; -}; - -/** @brief A container to store a key-value pair */ -struct TemplateKeyValue -{ - TemplateKeyValue() {} - TemplateKeyValue(const QCString &k,const TemplateVariant &v) : key(k), value(v) {} - QCString key; - TemplateVariant value; -}; - -/** @brief Internal class representing the implementation of a template - * context */ -class TemplateContextImpl : public TemplateContext -{ - public: - TemplateContextImpl(const TemplateEngine *e); - virtual ~TemplateContextImpl(); - - using EscapeIntfMap = std::unordered_map<std::string, std::unique_ptr<TemplateEscapeIntf>>; - void copyEscapeIntfMap(const EscapeIntfMap &map) - { - for (const auto &kv : map) - { - m_escapeIntfMap.insert(std::make_pair(kv.first,kv.second->clone())); - } - } - - // TemplateContext methods - void push(); - void pop(); - void set(const QCString &name,const TemplateVariant &v); - //void update(const QCString &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(const QCString &ext,std::unique_ptr<TemplateEscapeIntf> intf) - { - int i=(!ext.isEmpty() && ext.at(0)=='.') ? 1 : 0; - m_escapeIntfMap.insert(std::make_pair(ext.mid(i).str(),std::move(intf))); - } - void selectEscapeIntf(const QCString &ext) - { - auto it = m_escapeIntfMap.find(ext.str()); - m_activeEscapeIntf = it!=m_escapeIntfMap.end() ? it->second.get() : 0; - } - void setActiveEscapeIntf(TemplateEscapeIntf *intf) { m_activeEscapeIntf = intf; } - TemplateEscapeIntf *escapeIntf() { return m_activeEscapeIntf; } - const TemplateEscapeIntf *escapeIntf() const { return m_activeEscapeIntf; } - void setSpacelessIntf(std::unique_ptr<TemplateSpacelessIntf> intf) { m_spacelessIntf = std::move(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; } - std::unique_ptr<TemplateSpacelessIntf> &spacelessIntf() { return m_spacelessIntf; } - const std::unique_ptr<TemplateSpacelessIntf> &spacelessInfo() const { return m_spacelessIntf; } - void enableSpaceless(bool b) { if (b && !m_spacelessEnabled) m_spacelessIntf->reset(); - m_spacelessEnabled=b; - } - bool spacelessEnabled() const { return m_spacelessEnabled && m_spacelessIntf; } - void enableTabbing(bool b) { m_tabbingEnabled=b; - if (m_activeEscapeIntf) m_activeEscapeIntf->enableTabbing(b); - } - bool tabbingEnabled() const { return m_tabbingEnabled; } - bool needsRecoding() const { return !m_encoding.isEmpty(); } - QCString encoding() const { return m_encoding; } - void setEncoding(const QCString &file,int line,const QCString &enc); - QCString recode(const QCString &s); - void warn(const QCString &fileName,int line,const char *fmt,...) const; - - // index related functions - void openSubIndex(const QCString &indexName); - void closeSubIndex(const QCString &indexName); - void addIndexEntry(const QCString &indexName,const std::vector<TemplateKeyValue> &arguments); - const TemplateStructPtr indices() const { return m_indices; } - - private: - const TemplateEngine *m_engine = 0; - QCString m_templateName = "<unknown>"; - int m_line = 1; - QCString m_outputDir; - std::deque< std::unordered_map<std::string,TemplateVariant> > m_contextStack; - TemplateBlockContext m_blockContext; - EscapeIntfMap m_escapeIntfMap; - TemplateEscapeIntf *m_activeEscapeIntf = 0; - std::unique_ptr<TemplateSpacelessIntf> m_spacelessIntf; - bool m_spacelessEnabled = false; - bool m_tabbingEnabled = false; - TemplateStructPtr m_indices; - std::unordered_map< std::string, std::stack<TemplateVariant> > m_indexStacks; - QCString m_encoding; - void *m_fromUtf8 = 0; -}; - -//----------------------------------------------------------------------------- - -/** @brief The implementation of the "add" filter */ -class FilterAdd -{ - public: - static int variantIntValue(const TemplateVariant &v,bool &isInt) - { - if (!v.isInt() && v.isString()) - { - return v.toString().toInt(&isInt); - } - return v.isInt() ? v.toInt() : 0; - } - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg) - { - if (!v.isValid()) - { - return arg; - } - bool lhsIsInt = false; - int lhsValue = variantIntValue(v,lhsIsInt); - bool rhsIsInt = false; - int rhsValue = variantIntValue(arg,rhsIsInt); - if (lhsIsInt && rhsIsInt) - { - return lhsValue+rhsValue; - } - else if (v.isString() && arg.isString()) - { - return TemplateVariant(v.toString() + arg.toString()); - } - else - { - return v; - } - } -}; - -//----------------------------------------------------------------------------- - -/** @brief The implementation of the "get" filter */ -class FilterGet -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg) - { - if (v.isValid() && (v.isStruct() || v.isWeakStruct()) && arg.isString()) - { - TemplateStructIntfPtr s = v.toStruct(); - if (s) - { - TemplateVariant result = v.toStruct()->get(arg.toString()); - //printf("\nok[%s]=%d\n",qPrint(arg.toString()),result.type()); - return result; - } - else - { - return false; - } - } - else - { - //printf("\nnok[%s]\n",qPrint(arg.toString())); - return false; - } - } -}; - -//----------------------------------------------------------------------------- - -/** @brief The implementation of the "raw" filter */ -class FilterRaw -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && (v.isString() || v.isInt())) - { - return TemplateVariant(v.toString(),TRUE); - } - else - { - return v; - } - } -}; - -//----------------------------------------------------------------------------- - -/** @brief The implementation of the "keep" filter */ -class FilterKeep -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args) - { - if (v.isValid() && (v.isList()) && args.isString()) - { - TemplateListIntfPtr list = v.toList(); - if (list) - { - //printf("FilterKeep::apply: v=%s args=%s\n",qPrint(v.toString()),qPrint(args.toString())); - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - - TemplateListPtr result = TemplateList::alloc(); - TemplateVariant item; - for (it->toFirst();(it->current(item));it->toNext()) - { - //printf("item type=%s\n",item.typeAsString()); - TemplateStructIntfPtr s = item.toStruct(); - if (s) - { - TemplateVariant value = s->get(args.toString()); - //printf("value type=%s\n",value.typeAsString()); - if (value.toBool()) - { - //printf("keeping it\n"); - result->append(item); - } - else - { - //printf("Dropping it\n"); - } - } - } - return TemplateVariant(std::static_pointer_cast<TemplateListIntf>(result)); - } - } - return v; - } -}; - -//----------------------------------------------------------------------------- - -/** @brief The implementation of the "list" filter */ -class FilterList -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid()) - { - if (v.isList()) // input is already a list - { - return v; - } - // create a list with v as the only element - TemplateListPtr list = TemplateList::alloc(); - list->append(v); - return TemplateVariant(std::static_pointer_cast<TemplateListIntf>(list)); - } - else - { - return v; - } - } -}; - -//----------------------------------------------------------------------------- -/** @brief The implementation of the "texlabel" filter */ -class FilterTexLabel -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && (v.isString())) - { - return TemplateVariant(latexEscapeLabelName(v.toString()),TRUE); - } - else - { - return v; - } - } -}; - -//----------------------------------------------------------------------------- - -/** @brief The implementation of the "texindex" filter */ -class FilterTexIndex -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && (v.isString())) - { - return TemplateVariant(latexEscapeIndexChars(v.toString()),TRUE); - } - else - { - return v; - } - } -}; - -//----------------------------------------------------------------------------- - -/** @brief The implementation of the "append" filter */ -class FilterAppend -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg) - { - if ((v.isString() || v.isInt()) && - (arg.isString() || arg.isInt())) - { - 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.isString() || v.isInt()) && - arg.isString()) - { - 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.isList()) - { - return TemplateVariant(v.toList()->count()); - } - else if (v.isString()) - { - return TemplateVariant(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.isString() && v.toString().isEmpty()) - { - return arg; - } - else - { - return v; - } - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "flatten" filter */ -class FilterFlatten -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (!v.isValid() || !v.isList()) - { - return v; - } - else - { - TemplateListPtr list = TemplateList::alloc(); - TemplateListIntfPtr tree = v.toList(); - flatten(tree,list); - return TemplateVariant(std::static_pointer_cast<TemplateListIntf>(list)); - } - } - - private: - static void flatten(const TemplateListIntfPtr tree,TemplateListPtr list) - { - if (tree) - { - TemplateListIntf::ConstIteratorPtr it = tree->createIterator(); - TemplateVariant item; - for (it->toFirst();(it->current(item));it->toNext()) - { - TemplateStructIntfPtr s = item.toStruct(); - if (s) - { - list->append(item); - // if s has "children" then recurse into the children - TemplateVariant children = s->get("children"); - if (children.isValid() && children.isList()) - { - flatten(children.toList(),list); - } - } - else - { - list->append(item); - } - } - } - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "listsort" filter */ -class FilterListSort -{ - struct ListElem - { - ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {} - QCString key; - TemplateVariant value; - }; - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args) - { - if (v.isList() && args.isString()) - { - //printf("FilterListSort::apply: v=%s args=%s\n",qPrint(v.toString()),qPrint(args.toString())); - TemplateListIntfPtr list = v.toList(); - if (list) - { - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - - TemplateVariant item; - TemplateListPtr result = TemplateList::alloc(); - - // create list of items based on v using the data in args as a sort key - using SortList = std::vector<ListElem>; - SortList sortList; - sortList.reserve(v.toList()->count()); - for (it->toFirst();(it->current(item));it->toNext()) - { - TemplateStructIntfPtr s = item.toStruct(); - if (s) - { - QCString sortKey = determineSortKey(s,args.toString()); - sortList.emplace_back(sortKey,item); - //printf("sortKey=%s\n",qPrint(sortKey)); - } - } - - // sort the list - std::sort(sortList.begin(), - sortList.end(), - [](const auto &lhs,const auto &rhs) { return lhs.key < rhs.key; }); - - // add sorted items to the result list - for (const auto &elem : sortList) - { - result->append(elem.value); - } - return TemplateVariant(std::static_pointer_cast<TemplateListIntf>(result)); - } - } - return v; - } - - private: - static QCString determineSortKey(const TemplateStructIntfPtr s,const QCString &arg) - { - int i,p=0; - QCString result; - while ((i=arg.find("{{",p))!=-1) - { - result+=arg.mid(p,i-p); - int j=arg.find("}}",i+2); - if (j!=-1) - { - QCString var = arg.mid(i+2,j-i-2); - TemplateVariant val=s->get(var); - //printf("found argument %s value=%s\n",qPrint(var),qPrint(val.toString())); - result+=val.toString(); - p=j+2; - } - else - { - p=i+1; - } - } - result+=arg.right(arg.length()-p); - return result; - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "groupBy" filter */ -class FilterGroupBy -{ - struct ListElem - { - ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {} - QCString key; - TemplateVariant value; - }; - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args) - { - if (v.isList() && args.isString()) - { - TemplateListIntfPtr list = v.toList(); - if (list) - { - //printf("FilterListSort::apply: v=%s args=%s\n",qPrint(v.toString()),qPrint(args.toString())); - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - - TemplateVariant item; - TemplateListPtr result = TemplateList::alloc(); - - // create list of items based on v using the data in args as a sort key - using SortList = std::vector<ListElem>; - SortList sortList; - sortList.reserve(v.toList()->count()); - for (it->toFirst();(it->current(item));it->toNext()) - { - TemplateStructIntfPtr s = item.toStruct(); - if (s) - { - QCString sortKey = determineSortKey(s,args.toString()); - sortList.emplace_back(sortKey,item); - //printf("sortKey=%s\n",qPrint(sortKey)); - } - } - - // sort the list - std::sort(sortList.begin(), - sortList.end(), - [](const auto &lhs,const auto &rhs) { return lhs.key < rhs.key; }); - - // add sorted items to the result list - TemplateListPtr groupList; - QCString prevKey; - for (const auto &elem : sortList) - { - if (groupList==0 || elem.key!=prevKey) - { - groupList = TemplateList::alloc(); - result->append(std::static_pointer_cast<TemplateListIntf>(groupList)); - prevKey = elem.key; - } - groupList->append(elem.value); - } - return TemplateVariant(std::static_pointer_cast<TemplateListIntf>(result)); - } - } - return v; - } - - private: - static QCString determineSortKey(const TemplateStructIntfPtr s,const QCString &attribName) - { - TemplateVariant v = s->get(attribName); - return v.toString(); - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "relative" filter */ -class FilterRelative -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && v.isString() && v.toString().startsWith("..")) - { - return TRUE; - } - else - { - return FALSE; - } - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "paginate" filter */ -class FilterPaginate -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args) - { - if (v.isValid() && v.isList() && - args.isValid() && args.isInt()) - { - int pageSize = args.toInt(); - const TemplateListIntfPtr list = v.toList(); - TemplateListPtr result = TemplateList::alloc(); - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - TemplateVariant item; - TemplateListPtr pageList; - int i = 0; - for (it->toFirst();(it->current(item));it->toNext()) - { - if (pageList==0) - { - pageList = TemplateList::alloc(); - result->append(std::static_pointer_cast<TemplateListIntf>(pageList)); - } - pageList->append(item); - i++; - if (i==pageSize) // page is full start a new one - { - pageList=0; - i=0; - } - } - return TemplateVariant(std::static_pointer_cast<TemplateListIntf>(result)); - } - else // wrong arguments - { - return v; - } - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "alphaIndex" filter */ -class FilterAlphaIndex -{ - private: - struct ListElem - { - ListElem(std::string k,const TemplateVariant &v) : key(k), value(v) {} - std::string key; - TemplateVariant value; - }; - static QCString keyToLabel(const char *startLetter) - { - //printf(">keyToLabel(%s)\n",qPrint(startLetter)); - const char *p = startLetter; - char c = *p; - QCString result; - if (c<127 && c>31) // printable ASCII character - { - result+=c; - } - else - { - result="0x"; - const char hex[]="0123456789abcdef"; - while ((c=*p++)) - { - result+=hex[static_cast<unsigned char>(c)>>4]; - result+=hex[static_cast<unsigned char>(c)&0xf]; - } - } - //printf("<keyToLabel(%s)\n",qPrint(result)); - return result; - } - static std::string determineSortKey(const TemplateStructIntfPtr s,const QCString &attribName) - { - TemplateVariant v = s->get(attribName); - int index = getPrefixIndex(v.toString()); - return convertUTF8ToUpper(getUTF8CharAt(v.toString().str(),index)); - } - - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args) - { - if (v.isList() && args.isString()) - { - TemplateListIntfPtr list = v.toList(); - if (list) - { - //printf("FilterListSort::apply: v=%s args=%s\n",qPrint(v.toString()),qPrint(args.toString())); - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - - TemplateVariant item; - TemplateListPtr result = TemplateList::alloc(); - - // create list of items based on v using the data in args as a sort key - using SortList = std::vector<ListElem>; - SortList sortList; - sortList.reserve(v.toList()->count()); - for (it->toFirst();(it->current(item));it->toNext()) - { - TemplateStructIntfPtr s = item.toStruct(); - if (s) - { - std::string sortKey = determineSortKey(s,args.toString()); - sortList.emplace_back(sortKey,item); - //printf("sortKey=%s\n",qPrint(sortKey)); - } - } - - // sort the list - std::sort(sortList.begin(), - sortList.end(), - [](const auto &lhs,const auto &rhs) { return lhs.key < rhs.key; }); - - // create an index from the sorted list - std::string letter; - TemplateStructPtr indexNode; - TemplateListPtr indexList; - for (const auto &elem : sortList) - { - if (letter!=elem.key || indexNode==0) - { - // create new indexNode - indexNode = TemplateStruct::alloc(); - indexList = TemplateList::alloc(); - indexNode->set("letter", elem.key); - indexNode->set("label", keyToLabel(elem.key.c_str())); - indexNode->set("items",std::static_pointer_cast<TemplateListIntf>(indexList)); - result->append(std::static_pointer_cast<TemplateStructIntf>(indexNode)); - letter=elem.key; - } - indexList->append(elem.value); - } - return TemplateVariant(std::static_pointer_cast<TemplateListIntf>(result)); - } - } - return v; - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "default" filter */ -class FilterStripPath -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (!v.isValid() || !v.isString()) - { - 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.isString()) - { - return v; - } - QCString s = v.toString(); - return substitute(s," "," "); - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "divisibleby" filter */ -class FilterDivisibleBy -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &n) - { - if (!v.isValid() || !n.isValid()) - { - return TemplateVariant(); - } - if (v.isInt() && n.isInt()) - { - int ni = n.toInt(); - if (ni>0) - { - return TemplateVariant((v.toInt()%ni)==0); - } - else - { - return TemplateVariant(FALSE); - } - } - else - { - return TemplateVariant(); - } - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "isRelativeURL" filter */ -class FilterIsRelativeURL -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && v.isString()) - { - QCString s = v.toString(); - if (!s.isEmpty() && s.at(0)=='!') return TRUE; - } - return FALSE; - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "isRelativeURL" filter */ -class FilterIsAbsoluteURL -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && v.isString()) - { - QCString s = v.toString(); - if (!s.isEmpty() && s.at(0)=='^') return TRUE; - } - return FALSE; - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "lower" filter */ -class FilterLower -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && v.isString()) - { - return v.toString().lower(); - } - return v; - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "upper" filter */ -class FilterUpper -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && v.isString()) - { - return v.toString().upper(); - } - return v; - } -}; - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "upper" filter */ -class FilterHex -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid()) - { - return QCString().sprintf("%x",v.toInt()); - } - return v; - } -}; - - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "e" filter */ -class FilterEscape -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && v.isString()) - { - return convertToHtml(v.toString()); - } - return v; - } -}; - - -//-------------------------------------------------------------------- - -/** @brief The implementation of the "decodeURL" filter - * The leading character is removed from the value in case it is a ^ or !. - * - ^ is used to encode a absolute URL - * - ! is used to encode a relative URL - */ -class FilterDecodeURL -{ - public: - static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) - { - if (v.isValid() && v.isString()) - { - QCString s = v.toString(); - if (!s.isEmpty() && (s.at(0)=='^' || s.at(0)=='!')) - { - return s.mid(1); - } - } - return v; - } -}; - - -//-------------------------------------------------------------------- - -/** @brief Factory singleton for registering and creating filters */ -class TemplateFilterFactory -{ - public: - typedef TemplateVariant (FilterFunction)(const TemplateVariant &v,const TemplateVariant &arg); - - static TemplateFilterFactory &instance() - { - static std::unique_ptr<TemplateFilterFactory> instance; - if (instance==0) instance = std::make_unique<TemplateFilterFactory>(); - return *instance; - } - - TemplateVariant apply(const QCString &name,const TemplateVariant &v,const TemplateVariant &arg, bool &ok) - { - auto it = m_registry.find(name.str()); - if (it!=m_registry.end()) - { - ok=TRUE; - return it->second(v,arg); - } - else - { - ok=FALSE; - return v; - } - } - - void registerFilter(const QCString &name,FilterFunction *func) - { - m_registry.insert(std::make_pair(name.str(),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: - std::unordered_map<std::string,FilterFunction*> m_registry; -}; - -// register a handlers for each filter we support -static TemplateFilterFactory::AutoRegister<FilterAdd> fAdd("add"); -static TemplateFilterFactory::AutoRegister<FilterGet> fGet("get"); -static TemplateFilterFactory::AutoRegister<FilterHex> fHex("hex"); -static TemplateFilterFactory::AutoRegister<FilterRaw> fRaw("raw"); -static TemplateFilterFactory::AutoRegister<FilterKeep> fKeep("keep"); -static TemplateFilterFactory::AutoRegister<FilterList> fList("list"); -static TemplateFilterFactory::AutoRegister<FilterLower> fLower("lower"); -static TemplateFilterFactory::AutoRegister<FilterUpper> fUpper("upper"); -static TemplateFilterFactory::AutoRegister<FilterAppend> fAppend("append"); -static TemplateFilterFactory::AutoRegister<FilterEscape> fEscape("escape"); -static TemplateFilterFactory::AutoRegister<FilterLength> fLength("length"); -static TemplateFilterFactory::AutoRegister<FilterNoWrap> fNoWrap("nowrap"); -static TemplateFilterFactory::AutoRegister<FilterFlatten> fFlatten("flatten"); -static TemplateFilterFactory::AutoRegister<FilterDefault> fDefault("default"); -static TemplateFilterFactory::AutoRegister<FilterPrepend> fPrepend("prepend"); -static TemplateFilterFactory::AutoRegister<FilterGroupBy> fGroupBy("groupBy"); -static TemplateFilterFactory::AutoRegister<FilterRelative> fRelative("relative"); -static TemplateFilterFactory::AutoRegister<FilterListSort> fListSort("listsort"); -static TemplateFilterFactory::AutoRegister<FilterTexLabel> fTexLabel("texLabel"); -static TemplateFilterFactory::AutoRegister<FilterTexIndex> fTexIndex("texIndex"); -static TemplateFilterFactory::AutoRegister<FilterPaginate> fPaginate("paginate"); -static TemplateFilterFactory::AutoRegister<FilterStripPath> fStripPath("stripPath"); -static TemplateFilterFactory::AutoRegister<FilterDecodeURL> fDecodeURL("decodeURL"); -static TemplateFilterFactory::AutoRegister<FilterAlphaIndex> fAlphaIndex("alphaIndex"); -static TemplateFilterFactory::AutoRegister<FilterDivisibleBy> fDivisibleBy("divisibleby"); -static TemplateFilterFactory::AutoRegister<FilterIsRelativeURL> fIsRelativeURL("isRelativeURL"); -static TemplateFilterFactory::AutoRegister<FilterIsAbsoluteURL> fIsAbsoluteURL("isAbsoluteURL"); - -//-------------------------------------------------------------------- - -/** @brief Base class for all nodes in the abstract syntax tree of an - * expression. - */ -class ExprAst -{ - public: - virtual ~ExprAst() = default; - virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(); } -}; - -using ExprAstPtr = std::unique_ptr<ExprAst>; -using ExprAstList = std::vector< ExprAstPtr >; - -/** @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 = 0; -}; - -/** @brief Class representing a variable in the AST */ -class ExprAstVariable : public ExprAst -{ - public: - ExprAstVariable(const QCString &name) : m_name(name) - { TRACE(("ExprAstVariable(%s)\n",name.data())); } - const QCString &name() const { return m_name; } - virtual TemplateVariant resolve(TemplateContext *c) - { - TemplateVariant v = c->get(m_name); - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (!v.isValid()) - { - if (ci) ci->warn(ci->templateName(),ci->line(),"undefined variable '%s' in expression",qPrint(m_name)); - } - return v; - } - private: - QCString m_name; -}; - -class ExprAstFunctionVariable : public ExprAst -{ - public: - ExprAstFunctionVariable(ExprAstPtr &&var, ExprAstList &&args) - : m_var(std::move(var)), m_args(std::move(args)) - { TRACE(("ExprAstFunctionVariable()\n")); - } - virtual TemplateVariant resolve(TemplateContext *c) - { - std::vector<TemplateVariant> args; - for (const auto &exprArg : m_args) - { - TemplateVariant v = exprArg->resolve(c); - args.push_back(v); - } - TemplateVariant v = m_var->resolve(c); - if (v.isFunction()) - { - v = v.call(args); - } - return v; - } - private: - ExprAstPtr m_var; - ExprAstList m_args; -}; - -/** @brief Class representing a filter in the AST */ -class ExprAstFilter : public ExprAst -{ - public: - ExprAstFilter(const QCString &name,ExprAstPtr &&arg) : m_name(name), m_arg(std::move(arg)) - { TRACE(("ExprAstFilter(%s)\n",name.data())); } - const QCString &name() const { return m_name; } - TemplateVariant apply(const TemplateVariant &v,TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return v; // should not happen - TRACE(("Applying filter '%s' to '%s' (type=%d)\n",qPrint(m_name),qPrint(v.toString()),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) - { - ci->warn(ci->templateName(),ci->line(),"unknown filter '%s'",qPrint(m_name)); - } - return result; - } - private: - QCString m_name; - ExprAstPtr m_arg; -}; - -using ExprAstFilterPtr = std::unique_ptr<ExprAstFilter>; - -/** @brief Class representing a filter applied to an expression in the AST */ -class ExprAstFilterAppl : public ExprAst -{ - public: - ExprAstFilterAppl(ExprAstPtr &&expr,ExprAstFilterPtr &&filter) - : m_expr(std::move(expr)), m_filter(std::move(filter)) - { TRACE(("ExprAstFilterAppl\n")); } - virtual TemplateVariant resolve(TemplateContext *c) - { - return m_filter->apply(m_expr->resolve(c),c); - } - private: - ExprAstPtr m_expr; - ExprAstFilterPtr m_filter; -}; - -/** @brief Class representing a string literal in the AST */ -class ExprAstLiteral : public ExprAst -{ - public: - ExprAstLiteral(const QCString &lit) : m_literal(lit) - { TRACE(("ExprAstLiteral(%s)\n",lit.data())); } - 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(ExprAstPtr &&expr) : m_expr(std::move(expr)) - { TRACE(("ExprAstNegate\n")); } - virtual TemplateVariant resolve(TemplateContext *c) - { return TemplateVariant(!m_expr->resolve(c).toBool()); } - private: - ExprAstPtr m_expr; -}; - -class ExprAstUnary : public ExprAst -{ - public: - ExprAstUnary(Operator::Type op,ExprAstPtr &&expr) : m_operator(op), m_expr(std::move(expr)) - { TRACE(("ExprAstUnary %s\n",Operator::toString(op))); } - virtual TemplateVariant resolve(TemplateContext *c) - { - TemplateVariant expr = m_expr->resolve(c); - switch (m_operator) - { - case Operator::Minus: - return -expr.toInt(); - default: - return TemplateVariant(); - } - } - private: - Operator::Type m_operator = Operator::Or; - ExprAstPtr m_expr; -}; - -/** @brief Class representing a binary operator in the AST */ -class ExprAstBinary : public ExprAst -{ - public: - ExprAstBinary(Operator::Type op,ExprAstPtr &&lhs,ExprAstPtr &&rhs) - : m_operator(op), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) - { TRACE(("ExprAstBinary %s\n",Operator::toString(op))); } - virtual TemplateVariant resolve(TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return TemplateVariant(); // should not happen - 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.isString() && rhs.isString()) - { - return lhs.toString()<rhs.toString(); - } - else - { - return lhs.toInt()<rhs.toInt(); - } - case Operator::Greater: - if (lhs.isString() && rhs.isString()) - { - return !(lhs.toString()<rhs.toString()); - } - else - { - return lhs.toInt()>rhs.toInt(); - } - case Operator::LessEqual: - if (lhs.isString() && rhs.isString()) - { - return lhs.toString()==rhs.toString() || lhs.toString()<rhs.toString(); - } - else - { - return lhs.toInt()<=rhs.toInt(); - } - case Operator::GreaterEqual: - if (lhs.isString() && rhs.isString()) - { - return lhs.toString()==rhs.toString() || !(lhs.toString()<rhs.toString()); - } - else - { - return lhs.toInt()>=rhs.toInt(); - } - case Operator::Plus: - { - return TemplateVariant(lhs.toInt() + rhs.toInt()); - } - case Operator::Minus: - { - return TemplateVariant(lhs.toInt() - rhs.toInt()); - } - case Operator::Multiply: - { - return TemplateVariant(lhs.toInt() * rhs.toInt()); - } - case Operator::Divide: - { - int denom = rhs.toInt(); - if (denom!=0) - { - return TemplateVariant(lhs.toInt() / denom); - } - else // divide by zero - { - ci->warn(ci->templateName(),ci->line(),"division by zero while evaluating expression is undefined"); - return 0; - } - } - case Operator::Modulo: - { - int denom = rhs.toInt(); - if (denom!=0) - { - return TemplateVariant(lhs.toInt() % denom); - } - else // module zero - { - ci->warn(ci->templateName(),ci->line(),"modulo zero while evaluating expression is undefined"); - return 0; - } - } - default: - return TemplateVariant(); - } - } - private: - Operator::Type m_operator = Operator::Or; - ExprAstPtr m_lhs; - ExprAstPtr m_rhs; -}; - -//---------------------------------------------------------- - -/** @brief Base class of all nodes in a template's AST */ -class TemplateNode -{ - public: - TemplateNode(TemplateNode *parent) : m_parent(parent) {} - virtual ~TemplateNode() = default; - - virtual void render(TextStream &ts, TemplateContext *c) = 0; - - TemplateNode *parent() { return m_parent; } - - private: - TemplateNode *m_parent = 0; -}; - -using TemplateNodePtr = std::unique_ptr<TemplateNode>; - -//---------------------------------------------------------- - -/** @brief Class representing a lexical token in a template */ -class TemplateToken -{ - public: - enum Type { Text, Variable, Block }; - TemplateToken(Type t,const QCString &d,int l) : type(t), data(d), line(l) {} - Type type = Text; - QCString data; - int line = 0; -}; - -using TemplateTokenPtr = std::unique_ptr<TemplateToken>; -using TemplateTokenStream = std::deque< TemplateTokenPtr >; - -//---------------------------------------------------------- - -/** @brief Class representing a list of AST nodes in a template */ -class TemplateNodeList : public std::vector< TemplateNodePtr > -{ - public: - void render(TextStream &ts,TemplateContext *c) - { - TRACE(("{TemplateNodeList::render\n")); - for (const auto &tn : *this) - { - tn->render(ts,c); - } - TRACE(("}TemplateNodeList::render\n")); - } -}; - -//---------------------------------------------------------- - -/** @brief Parser for templates */ -class TemplateParser -{ - public: - TemplateParser(const TemplateEngine *engine, - const QCString &templateName, - TemplateTokenStream &tokens); - void parse(TemplateNode *parent,int line,const StringVector &stopAt, - TemplateNodeList &nodes); - bool hasNextToken() const; - TemplateTokenPtr takeNextToken(); - void removeNextToken(); - void prependToken(TemplateTokenPtr &&token); - const TemplateToken *currentToken() const; - QCString templateName() const { return m_templateName; } - void warn(const QCString &fileName,int line,const char *fmt,...) const; - private: - const TemplateEngine *m_engine = 0; - QCString m_templateName; - TemplateTokenStream &m_tokens; -}; - -//-------------------------------------------------------------------- - -/** @brief Recursive decent parser for Django style template expressions. - */ -class ExpressionParser -{ - public: - ExpressionParser(const TemplateParser *parser,int line) - : m_parser(parser), m_line(line), m_tokenStream(0) - { - } - virtual ~ExpressionParser() - { - } - - ExprAstPtr parse(const QCString &expr) - { - if (expr.isEmpty()) return 0; - m_tokenStream = expr.data(); - getNextToken(); - return parseExpression(); - } - - 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; - }; - - ExprAstPtr parseExpression() - { - TRACE(("{parseExpression(%s)\n",m_tokenStream)); - ExprAstPtr result { parseOrExpression() }; - TRACE(("}parseExpression(%s)\n",m_tokenStream)); - return result; - } - - ExprAstPtr parseOrExpression() - { - TRACE(("{parseOrExpression(%s)\n",m_tokenStream)); - ExprAstPtr lhs { parseAndExpression() }; - if (lhs) - { - while (m_curToken.type==ExprToken::Operator && - m_curToken.op==Operator::Or) - { - getNextToken(); - ExprAstPtr rhs { parseAndExpression() }; - lhs = std::make_unique<ExprAstBinary>(Operator::Or,std::move(lhs),std::move(rhs)); - } - } - TRACE(("}parseOrExpression(%s)\n",m_tokenStream)); - return lhs; - } - - ExprAstPtr parseAndExpression() - { - TRACE(("{parseAndExpression(%s)\n",m_tokenStream)); - ExprAstPtr lhs { parseNotExpression() }; - if (lhs) - { - while (m_curToken.type==ExprToken::Operator && - m_curToken.op==Operator::And) - { - getNextToken(); - ExprAstPtr rhs { parseNotExpression() }; - lhs = std::make_unique<ExprAstBinary>(Operator::And,std::move(lhs),std::move(rhs)); - } - } - TRACE(("}parseAndExpression(%s)\n",m_tokenStream)); - return lhs; - } - - ExprAstPtr parseNotExpression() - { - TRACE(("{parseNotExpression(%s)\n",m_tokenStream)); - ExprAstPtr result; - if (m_curToken.type==ExprToken::Operator && - m_curToken.op==Operator::Not) - { - getNextToken(); - ExprAstPtr expr = parseCompareExpression(); - if (expr==0) - { - warn(m_parser->templateName(),m_line,"argument missing for not operator"); - return 0; - } - result = std::make_unique<ExprAstNegate>(std::move(expr)); - } - else - { - result = parseCompareExpression(); - } - TRACE(("}parseNotExpression(%s)\n",m_tokenStream)); - return result; - } - - ExprAstPtr parseCompareExpression() - { - TRACE(("{parseCompareExpression(%s)\n",m_tokenStream)); - ExprAstPtr lhs { parseAdditiveExpression() }; - 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(); - ExprAstPtr rhs { parseNotExpression() }; - lhs = std::make_unique<ExprAstBinary>(op,std::move(lhs),std::move(rhs)); - } - } - TRACE(("}parseCompareExpression(%s)\n",m_tokenStream)); - return lhs; - } - - ExprAstPtr parseAdditiveExpression() - { - TRACE(("{parseAdditiveExpression(%s)\n",m_tokenStream)); - ExprAstPtr lhs { parseMultiplicativeExpression() }; - if (lhs) - { - while (m_curToken.type==ExprToken::Operator && - (m_curToken.op==Operator::Plus || m_curToken.op==Operator::Minus)) - { - Operator::Type op = m_curToken.op; - getNextToken(); - ExprAstPtr rhs { parseMultiplicativeExpression() }; - lhs = std::make_unique<ExprAstBinary>(op,std::move(lhs),std::move(rhs)); - } - } - TRACE(("}parseAdditiveExpression(%s)\n",m_tokenStream)); - return lhs; - } - - ExprAstPtr parseMultiplicativeExpression() - { - TRACE(("{parseMultiplicativeExpression(%s)\n",m_tokenStream)); - ExprAstPtr lhs = parseUnaryExpression(); - if (lhs) - { - while (m_curToken.type==ExprToken::Operator && - (m_curToken.op==Operator::Multiply || m_curToken.op==Operator::Divide || m_curToken.op==Operator::Modulo)) - { - Operator::Type op = m_curToken.op; - getNextToken(); - ExprAstPtr rhs = parseUnaryExpression(); - lhs = std::make_unique<ExprAstBinary>(op,std::move(lhs),std::move(rhs)); - } - } - TRACE(("}parseMultiplicativeExpression(%s)\n",m_tokenStream)); - return lhs; - } - - ExprAstPtr parseUnaryExpression() - { - TRACE(("{parseUnaryExpression(%s)\n",m_tokenStream)); - ExprAstPtr result; - if (m_curToken.type==ExprToken::Operator) - { - if (m_curToken.op==Operator::Plus) - { - getNextToken(); - result = parsePrimaryExpression(); - } - else if (m_curToken.op==Operator::Minus) - { - getNextToken(); - ExprAstPtr rhs { parsePrimaryExpression() }; - result = std::make_unique<ExprAstUnary>(m_curToken.op,std::move(rhs)); - } - else - { - result = parsePrimaryExpression(); - } - } - else - { - result = parsePrimaryExpression(); - } - TRACE(("}parseUnaryExpression(%s)\n",m_tokenStream)); - return result; - } - - ExprAstPtr parsePrimaryExpression() - { - TRACE(("{parsePrimary(%s)\n",m_tokenStream)); - ExprAstPtr result; - switch (m_curToken.type) - { - case ExprToken::Number: - result = parseNumber(); - break; - case ExprToken::Identifier: - result = parseFilteredVariable(); - break; - case ExprToken::Literal: - result = parseLiteral(); - break; - case ExprToken::Operator: - if (m_curToken.op==Operator::LeftParen) - { - getNextToken(); // skip over opening bracket - result = parseExpression(); - if (m_curToken.type!=ExprToken::Operator || - m_curToken.op!=Operator::RightParen) - { - warn(m_parser->templateName(),m_line,"missing closing parenthesis"); - } - else - { - getNextToken(); // skip over closing bracket - } - } - else - { - warn(m_parser->templateName(),m_line,"unexpected operator '%s' in expression", - Operator::toString(m_curToken.op)); - abort(); - } - break; - default: - warn(m_parser->templateName(),m_line,"unexpected token in expression"); - } - TRACE(("}parsePrimary(%s)\n",m_tokenStream)); - return result; - } - - ExprAstPtr parseNumber() - { - TRACE(("{parseNumber(%d)\n",m_curToken.num)); - ExprAstPtr num = std::make_unique<ExprAstNumber>(m_curToken.num); - getNextToken(); - TRACE(("}parseNumber()\n")); - return num; - } - - ExprAstPtr parseIdentifier() - { - TRACE(("{parseIdentifier(%s)\n",qPrint(m_curToken.id))); - ExprAstPtr id = std::make_unique<ExprAstVariable>(m_curToken.id); - getNextToken(); - TRACE(("}parseIdentifier()\n")); - return id; - } - - ExprAstPtr parseLiteral() - { - TRACE(("{parseLiteral(%s)\n",qPrint(m_curToken.id))); - ExprAstPtr expr = std::make_unique<ExprAstLiteral>(m_curToken.id); - getNextToken(); - TRACE(("}parseLiteral()\n")); - return expr; - } - - ExprAstPtr parseIdentifierOptionalArgs() - { - TRACE(("{parseIdentifierOptionalArgs(%s)\n",qPrint(m_curToken.id))); - ExprAstPtr expr { parseIdentifier() }; - if (expr) - { - if (m_curToken.type==ExprToken::Operator && - m_curToken.op==Operator::Colon) - { - getNextToken(); - ExprAstList args; - args.push_back(std::unique_ptr<ExprAst>(parsePrimaryExpression())); - while (m_curToken.type==ExprToken::Operator && - m_curToken.op==Operator::Comma) - { - getNextToken(); - args.push_back(std::unique_ptr<ExprAst>(parsePrimaryExpression())); - } - expr = std::make_unique<ExprAstFunctionVariable>(std::move(expr),std::move(args)); - } - } - TRACE(("}parseIdentifierOptionalArgs()\n")); - return expr; - } - - ExprAstPtr parseFilteredVariable() - { - TRACE(("{parseFilteredVariable()\n")); - ExprAstPtr expr = parseIdentifierOptionalArgs(); - if (expr) - { - while (m_curToken.type==ExprToken::Operator && - m_curToken.op==Operator::Filter) - { - getNextToken(); - ExprAstFilterPtr filter = parseFilter(); - if (!filter) break; - expr = std::make_unique<ExprAstFilterAppl>(std::move(expr),std::move(filter)); - } - } - TRACE(("}parseFilteredVariable()\n")); - return expr; - } - - ExprAstFilterPtr parseFilter() - { - TRACE(("{parseFilter(%s)\n",qPrint(m_curToken.id))); - QCString filterName = m_curToken.id; - getNextToken(); - ExprAstPtr argExpr; - if (m_curToken.type==ExprToken::Operator && - m_curToken.op==Operator::Colon) - { - getNextToken(); - argExpr = parsePrimaryExpression(); - } - ExprAstFilterPtr filter = std::make_unique<ExprAstFilter>(filterName,std::move(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 (*p=='\0') // only spaces... - { - m_tokenStream = p; - return FALSE; - } - const char *q = p; - switch (c) - { - case '=': - if (c=='=' && *(p+1)=='=') // equal - { - m_curToken.op = Operator::Equal; - p+=2; - } - break; - case '!': - if (c=='!' && *(p+1)=='=') // not equal - { - m_curToken.op = Operator::NotEqual; - p+=2; - } - break; - case '<': - if (c=='<' && *(p+1)=='=') // less or equal - { - m_curToken.op = Operator::LessEqual; - p+=2; - } - else // less - { - m_curToken.op = Operator::Less; - p++; - } - break; - case '>': - if (c=='>' && *(p+1)=='=') // greater or equal - { - m_curToken.op = Operator::GreaterEqual; - p+=2; - } - else // greater - { - m_curToken.op = Operator::Greater; - p++; - } - break; - case '(': - m_curToken.op = Operator::LeftParen; - p++; - break; - case ')': - m_curToken.op = Operator::RightParen; - p++; - break; - case '|': - m_curToken.op = Operator::Filter; - p++; - break; - case '+': - m_curToken.op = Operator::Plus; - p++; - break; - case '-': - m_curToken.op = Operator::Minus; - p++; - break; - case '*': - m_curToken.op = Operator::Multiply; - p++; - break; - case '/': - m_curToken.op = Operator::Divide; - p++; - break; - case '%': - m_curToken.op = Operator::Modulo; - p++; - break; - case ':': - m_curToken.op = Operator::Colon; - p++; - break; - case ',': - m_curToken.op = Operator::Comma; - p++; - break; - case 'n': - if (strncmp(p,"not ",4)==0) - { - m_curToken.op = Operator::Not; - p+=4; - } - break; - case 'a': - if (strncmp(p,"and ",4)==0) - { - m_curToken.op = Operator::And; - p+=4; - } - break; - case 'o': - if (strncmp(p,"or ",3)==0) - { - m_curToken.op = Operator::Or; - p+=3; - } - break; - default: - break; - } - if (p!=q) // found an operator - { - m_curToken.type = ExprToken::Operator; - } - else // no token found yet - { - if (c>='0' && c<='9') // number? - { - m_curToken.type = ExprToken::Number; - const char *np = p; - m_curToken.num = 0; - while (*np>='0' && *np<='9') - { - m_curToken.num*=10; - m_curToken.num+=*np-'0'; - np++; - } - p=np; - } - else if (c=='_' || (c>='a' && c<='z') || (c>='A' && c<='Z')) // identifier? - { - 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++; - } - if (m_curToken.id=="True") // treat true literal as numerical 1 - { - m_curToken.type = ExprToken::Number; - m_curToken.num = 1; - } - else if (m_curToken.id=="False") // treat false literal as numerical 0 - { - m_curToken.type = ExprToken::Number; - m_curToken.num = 0; - } - } - else if (c=='"' || c=='\'') // string literal - { - 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++; - } - } - if (p==q) // still no valid token found -> error - { - m_curToken.type = ExprToken::Unknown; - s[0]=c; - s[1]=0; - warn(m_parser->templateName(),m_line,"Found unknown token '%s' (%d) while parsing %s",s,c,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,qPrint(m_curToken.id))); - - m_tokenStream = p; - return TRUE; - } - - const TemplateParser *m_parser = 0; - ExprToken m_curToken; - int m_line = 0; - const char *m_tokenStream; -}; - -//---------------------------------------------------------- - -/** @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, - const QCString &extension); - ~TemplateImpl(); - void render(TextStream &ts, TemplateContext *c); - - TemplateEngine *engine() const { return m_engine; } - TemplateBlockContext *blockContext() { return &m_blockContext; } - - private: - TemplateEngine *m_engine = 0; - QCString m_name; - TemplateNodeList m_nodes; - TemplateBlockContext m_blockContext; -}; - -//---------------------------------------------------------- - -TemplateContextImpl::TemplateContextImpl(const TemplateEngine *e) - : m_engine(e), m_indices(TemplateStruct::alloc()) -{ - //printf("%p:TemplateContextImpl::TemplateContextImpl()\n",(void*)this); - m_fromUtf8 = reinterpret_cast<void*>(-1); - push(); - set("index",std::static_pointer_cast<TemplateStructIntf>(m_indices)); -} - -TemplateContextImpl::~TemplateContextImpl() -{ - pop(); - //printf("%p:TemplateContextImpl::~TemplateContextImpl()\n",(void*)this); -} - -void TemplateContextImpl::setEncoding(const QCString &templateName,int line,const QCString &enc) -{ - if (enc==m_encoding) return; // nothing changed - if (m_fromUtf8!=reinterpret_cast<void *>(-1)) - { - portable_iconv_close(m_fromUtf8); - m_fromUtf8 = reinterpret_cast<void*>(-1); - } - m_encoding=enc; - if (!enc.isEmpty()) - { - m_fromUtf8 = portable_iconv_open(enc.data(),"UTF-8"); - if (m_fromUtf8==reinterpret_cast<void*>(-1)) - { - warn(templateName,line,"unsupported character conversion: '%s'->'UTF-8'\n", qPrint(enc)); - } - } - //printf("TemplateContextImpl::setEncoding(%s)\n",qPrint(enc)); -} - -QCString TemplateContextImpl::recode(const QCString &s) -{ - //printf("TemplateContextImpl::recode(%s)\n",qPrint(s)); - size_t iSize = s.length(); - size_t oSize = iSize*4+1; - QCString output(oSize); - size_t iLeft = iSize; - size_t oLeft = oSize; - const char *iPtr = s.data(); - char *oPtr = output.rawData(); - if (!portable_iconv(m_fromUtf8,&iPtr,&iLeft,&oPtr,&oLeft)) - { - oSize -= oLeft; - output.resize(oSize+1); - output.at(oSize)='\0'; - return output; - } - else - { - return s; - } -} - -void TemplateContextImpl::set(const QCString &name,const TemplateVariant &v) -{ - auto &ctx = m_contextStack.front(); - auto it = ctx.find(name.str()); - if (it!=ctx.end()) - { - ctx.erase(it); - } - ctx.insert(std::make_pair(name.str(),v)); - //printf("TemplateContextImpl::set(%s) #stacks=%lu front().size()=%lu\n", - // qPrint(name),m_contextStack.size(),m_contextStack.size()>0 ? m_contextStack.front().size() : 0); -} - - -TemplateVariant TemplateContextImpl::get(const QCString &name) const -{ - int i=name.find('.'); - if (i==-1) // simple name - { - return getPrimary(name); - } - else // obj.prop - { - QCString objName = name.left(i); - TemplateVariant v = getPrimary(objName); - QCString propName = name.mid(i+1); - while (!propName.isEmpty()) - { - //printf("getPrimary(%s) type=%zu:%s\n",qPrint(objName),v.type(),qPrint(v.toString())); - if (v.isStruct() || v.isWeakStruct()) - { - TemplateStructIntfPtr s = v.toStruct(); - if (s) - { - i = propName.find("."); - int l = i==-1 ? propName.length() : i; - v = s->get(propName.left(l)); - if (!v.isValid()) - { - warn(m_templateName,m_line,"requesting non-existing property '%s' for object '%s'",qPrint(propName.left(l)),qPrint(objName)); - } - if (i!=-1) - { - objName = propName.left(i); - propName = propName.mid(i+1); - } - else - { - propName.resize(0); - } - } - else - { - return TemplateVariant(); - } - } - else if (v.isList()) - { - TemplateListIntfPtr list = v.toList(); - if (list) - { - i = propName.find("."); - int l = i==-1 ? propName.length() : i; - bool b; - int index = propName.left(l).toInt(&b); - if (b) - { - v = list->at(index); - } - else - { - warn(m_templateName,m_line,"list index '%s' is not valid",qPrint(propName)); - break; - } - if (i!=-1) - { - propName = propName.mid(i+1); - } - else - { - propName.resize(0); - } - } - else - { - return TemplateVariant(); - } - } - else - { - warn(m_templateName,m_line,"using . on an object '%s' is not an struct or list",qPrint(objName)); - return TemplateVariant(); - } - } - return v; - } -} - -const TemplateVariant *TemplateContextImpl::getRef(const QCString &name) const -{ - for (const auto &ctx : m_contextStack) - { - auto it = ctx.find(name.str()); - if (it!=ctx.end()) - { - return &it->second; - } - } - return 0; // not found -} - -TemplateVariant TemplateContextImpl::getPrimary(const QCString &name) const -{ - const TemplateVariant *v = getRef(name); - return v ? *v : TemplateVariant(); -} - -void TemplateContextImpl::push() -{ - m_contextStack.push_front(std::unordered_map<std::string,TemplateVariant>()); - //printf("TemplateContextImpl::push() #stacks=%lu\n",m_contextStack.size()); -} - -void TemplateContextImpl::pop() -{ - //printf("TemplateContextImpl::pop() #stacks=%lu\n",m_contextStack.size()); - if (m_contextStack.empty()) - { - warn(m_templateName,m_line,"pop() called on empty context stack!\n"); - } - else - { - m_contextStack.pop_front(); - } -} - -TemplateBlockContext *TemplateContextImpl::blockContext() -{ - return &m_blockContext; -} - -void TemplateContextImpl::warn(const QCString &fileName,int line,const char *fmt,...) const -{ - va_list args; - va_start(args,fmt); - va_warn(fileName,line,fmt,args); - va_end(args); - m_engine->printIncludeContext(fileName,line); -} - -void TemplateContextImpl::openSubIndex(const QCString &indexName) -{ - //printf("TemplateContextImpl::openSubIndex(%s)\n",qPrint(indexName)); - auto kv = m_indexStacks.find(indexName.str()); - if (kv==m_indexStacks.end() || kv->second.empty() || kv->second.top().isList()) // error: no stack yet or no entry - { - warn(m_templateName,m_line,"opensubindex for index %s without preceding indexentry",qPrint(indexName)); - return; - } - // get the parent entry to add the list to - auto &stack = kv->second; - TemplateStructPtr entry = std::dynamic_pointer_cast<TemplateStruct>(stack.top().toStruct()); - if (entry) - { - // add new list to the stack - TemplateListPtr list = TemplateList::alloc(); - stack.emplace(std::static_pointer_cast<TemplateListIntf>(list)); - entry->set("children",std::static_pointer_cast<TemplateListIntf>(list)); - entry->set("is_leaf_node",false); - } -} - -void TemplateContextImpl::closeSubIndex(const QCString &indexName) -{ - //printf("TemplateContextImpl::closeSubIndex(%s)\n",qPrint(indexName)); - auto kv = m_indexStacks.find(indexName.str()); - if (kv==m_indexStacks.end() || kv->second.size()<3) - { - warn(m_templateName,m_line,"closesubindex for index %s without matching open",qPrint(indexName)); - } - else - { - auto &stack = kv->second; // stack.size()>2 - if (stack.top().isStruct() || stack.top().isWeakStruct()) - { - stack.pop(); // pop struct - stack.pop(); // pop list - } - else // empty list! correct "is_left_node" attribute of the parent entry - { - stack.pop(); // pop list - TemplateStructPtr entry = std::dynamic_pointer_cast<TemplateStruct>(stack.top().toStruct()); - if (entry) - { - entry->set("is_leaf_node",true); - } - } - } - //fprintf(stderr,"TemplateContextImpl::closeSubIndex(%s) end g_count=%d\n\n",qPrint(indexName),g_count); -} - -static void getPathListFunc(const TemplateStructIntfPtr entry,TemplateListPtr list) -{ - if (entry) - { - TemplateVariant parent = entry->get("parent"); - if (parent.isStruct() || parent.isWeakStruct()) - { - getPathListFunc(parent.toStruct(),list); - } - list->append(entry); - } -} - -static TemplateVariant getPathFunc(const TemplateStructIntfWeakPtr entryWeakRef) -{ - TemplateListPtr result = TemplateList::alloc(); - getPathListFunc(entryWeakRef.lock(),result); - return std::static_pointer_cast<TemplateListIntf>(result); -} - -void TemplateContextImpl::addIndexEntry(const QCString &indexName,const std::vector<TemplateKeyValue> &arguments) -{ - //auto it = arguments.begin(); - //printf("%p:> TemplateContextImpl::addIndexEntry(%s)\n",(void*)this,qPrint(indexName)); - //while (it!=arguments.end()) - //{ - // printf(" key=%s value=%s\n",(*it).key.data(),(*it).value.toString().data()); - // ++it; - //} - TemplateVariant parent(FALSE); - auto kv = m_indexStacks.find(indexName.str()); - if (kv==m_indexStacks.end()) // no stack yet, create it! - { - kv = m_indexStacks.insert(std::make_pair(indexName.str(),std::stack<TemplateVariant>())).first; - } - TemplateListPtr list; - auto &stack = kv->second; - if (stack.empty()) // first item, create empty list and add it to the index - { - list = TemplateList::alloc(); - stack.emplace(std::static_pointer_cast<TemplateListIntf>(list)); - m_indices->set(indexName,std::static_pointer_cast<TemplateListIntf>(list)); // make list available under index - } - else // stack not empty - { - if (stack.top().isStruct() || stack.top().isWeakStruct()) // already an entry in the list - { - // remove current entry from the stack - stack.pop(); - } - else // first entry after opensubindex - { - ASSERT(stack.top().isList()); - } - if (stack.size()>1) - { - TemplateVariant tmp = stack.top(); - stack.pop(); - // To prevent a cyclic dependency between parent and child which causes a memory - // leak, we wrap the parent into a weak reference version. - //parent = TemplateVariant(TemplateStructIntfWeakPtr(stack.top().toStruct())); - stack.push(tmp); - } - // get list to add new item - list = std::dynamic_pointer_cast<TemplateList>(stack.top().toList()); - } - TemplateStructPtr entry = TemplateStruct::alloc(); - // add user specified fields to the entry - for (auto it=arguments.begin();it!=arguments.end();++it) - { - entry->set((*it).key,(*it).value); - } - if (list->count()>0) - { - TemplateStructPtr lastEntry = std::dynamic_pointer_cast<TemplateStruct>(list->at(list->count()-1).toStruct()); - if (lastEntry) - { - lastEntry->set("last",false); - } - } - entry->set("is_leaf_node",true); - entry->set("first",list->count()==0); - entry->set("index",list->count()); - entry->set("parent",parent); - TemplateStructIntfWeakPtr entryWeak(std::static_pointer_cast<TemplateStructIntf>(entry)); - entry->set("path",TemplateVariant([entryWeak](const TemplateVariantList &){ return getPathFunc(entryWeak); })); - entry->set("last",true); - stack.push(TemplateVariant(std::static_pointer_cast<TemplateStructIntf>(entry))); - list->append(TemplateVariant(std::static_pointer_cast<TemplateStructIntf>(entry))); -} - -//---------------------------------------------------------- - -/** @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(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - //printf("TemplateNodeText::render(%s) needsRecoding=%d ci=%p\n",qPrint(m_data),ci->needsRecoding(),ci); - if (ci->spacelessEnabled()) - { - if (ci->needsRecoding()) - { - ts << ci->recode(ci->spacelessIntf()->remove(m_data)); - } - else - { - ts << ci->spacelessIntf()->remove(m_data); - } - } - else - { - if (ci->needsRecoding()) - { - ts << ci->recode(m_data); - } - else - { - 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",qPrint(var))); - ExpressionParser expParser(parser,line); - m_var = expParser.parse(var); - if (m_var==0) - { - parser->warn(m_templateName,line,"invalid expression '%s' for variable",qPrint(var)); - } - } - ~TemplateNodeVariable() - { - } - - void render(TextStream &ts, TemplateContext *c) - { - TRACE(("{TemplateNodeVariable::render\n")); - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - if (m_var) - { - TemplateVariant v = m_var->resolve(c); - if (v.isFunction()) - { - v = v.call(); - } - if (ci->escapeIntf() && !v.raw()) - { - if (ci->needsRecoding()) - { - ts << ci->recode(ci->escapeIntf()->escape(v.toString())); - } - else - { - ts << ci->escapeIntf()->escape(v.toString()); - } - } - else - { - if (ci->needsRecoding()) - { - ts << ci->recode(v.toString()); - } - else - { - ts << v.toString(); - } - } - } - TRACE(("}TemplateNodeVariable::render\n")); - } - - private: - QCString m_templateName; - int m_line = 0; - ExprAstPtr m_var; -}; - -//---------------------------------------------------------- - -/** @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 TemplateNodePtr createInstance(TemplateParser *parser, - TemplateNode *parent, - int line, - const QCString &data) - { - return std::make_unique<T>(parser,parent,line,data); - } - TemplateImpl *getTemplate() - { - TemplateNode *root = this; - while (root && root->parent()) - { - root = root->parent(); - } - return dynamic_cast<TemplateImpl*>(root); - } - protected: - void mkpath(const TemplateContextImpl *ci,const std::string &fileName) - { - size_t i=fileName.find('/'); - std::string outputDir = ci->outputDirectory().str(); - Dir d(outputDir); - if (!d.exists()) - { - Dir rootDir; - if (!rootDir.mkdir(outputDir)) - { - err("tag OUTPUT_DIRECTORY: Output directory '%s' does not " - "exist and cannot be created\n",outputDir.c_str()); - return; - } - d.setPath(outputDir); - } - size_t j=0; - while (i!=std::string::npos) // fileName contains path part - { - if (d.exists()) - { - bool ok = d.mkdir(fileName.substr(j,i-j)); - if (!ok) - { - err("Failed to create directory '%s'\n",(fileName.substr(j,i-j)).c_str()); - break; - } - std::string dirName = outputDir+'/'+fileName.substr(0,i); - d = Dir(dirName); - j = i+1; - } - i=fileName.find('/',i+1); - } - } - QCString m_templateName; - int m_line = 0; -}; - -//---------------------------------------------------------- - -/** @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",qPrint(data))); - if (data.isEmpty()) - { - parser->warn(m_templateName,line,"missing argument for if tag"); - } - StringVector stopAt = { "endif", "elif", "else" }; - - // if 'nodes' - { - m_ifGuardedNodes.push_back(std::make_unique<GuardedNodes>()); - auto &guardedNodes = m_ifGuardedNodes.back(); - ExpressionParser ex(parser,line); - guardedNodes->line = line; - guardedNodes->guardAst = ex.parse(data); - parser->parse(this,line,stopAt,guardedNodes->trueNodes); - } - auto tok = parser->takeNextToken(); - - // elif 'nodes' - while (tok && tok->data.startsWith("elif ")) - { - m_ifGuardedNodes.push_back(std::make_unique<GuardedNodes>()); - auto &guardedNodes = m_ifGuardedNodes.back(); - ExpressionParser ex(parser,line); - guardedNodes->line = tok->line; - guardedNodes->guardAst = ex.parse(tok->data.mid(5)); - parser->parse(this,tok->line,stopAt,guardedNodes->trueNodes); - // proceed to the next token - tok = parser->takeNextToken(); - } - - // else 'nodes' - if (tok && tok->data=="else") - { - stopAt.pop_back(); // remove "else" - stopAt.pop_back(); // remove "elif" - parser->parse(this,line,stopAt,m_falseNodes); - parser->removeNextToken(); // skip over endif - } - TRACE(("}TemplateNodeIf(%s)\n",qPrint(data))); - } - ~TemplateNodeIf() - { - } - - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - //printf("TemplateNodeIf::render #trueNodes=%d #falseNodes=%d\n",m_trueNodes.count(),m_falseNodes.count()); - bool processed=FALSE; - for (auto &nodes : m_ifGuardedNodes) - { - if (nodes->guardAst) - { - TemplateVariant guardValue = nodes->guardAst->resolve(c); - if (guardValue.toBool()) // render nodes for the first guard that evaluated to 'true' - { - nodes->trueNodes.render(ts,c); - processed=TRUE; - break; - } - } - else - { - ci->warn(m_templateName,nodes->line,"invalid expression for if/elif"); - } - } - if (!processed) - { - // all guards are false, render 'else' nodes - m_falseNodes.render(ts,c); - } - } - private: - struct GuardedNodes - { - int line = 0; - ExprAstPtr guardAst; - TemplateNodeList trueNodes; - }; - std::vector< std::unique_ptr<GuardedNodes> > m_ifGuardedNodes; - TemplateNodeList m_falseNodes; -}; - -//---------------------------------------------------------- -/** @brief Class representing a 'for' tag in a template */ -class TemplateNodeRepeat : public TemplateNodeCreator<TemplateNodeRepeat> -{ - public: - TemplateNodeRepeat(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeRepeat>(parser,parent,line) - { - TRACE(("{TemplateNodeRepeat(%s)\n",qPrint(data))); - ExpressionParser expParser(parser,line); - m_expr = expParser.parse(data); - StringVector stopAt = { "endrepeat" }; - parser->parse(this,line,stopAt,m_repeatNodes); - parser->removeNextToken(); // skip over endrepeat - TRACE(("}TemplateNodeRepeat(%s)\n",qPrint(data))); - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - TemplateVariant v; - if (m_expr && (v=m_expr->resolve(c)).isInt()) - { - int i, n = v.toInt(); - for (i=0;i<n;i++) - { - TemplateStructPtr s = TemplateStruct::alloc(); - s->set("counter0", i); - s->set("counter", i+1); - s->set("revcounter", n-i); - s->set("revcounter0", n-i-1); - s->set("first",i==0); - s->set("last", i==n-1); - c->set("repeatloop",std::static_pointer_cast<TemplateStructIntf>(s)); - // render all items for this iteration of the loop - m_repeatNodes.render(ts,c); - } - } - else // simple type... - { - ci->warn(m_templateName,m_line,"for requires a variable of list type!"); - } - } - private: - TemplateNodeList m_repeatNodes; - ExprAstPtr m_expr; -}; - -//---------------------------------------------------------- - -/** @brief Class representing a 'range' tag in a template */ -class TemplateNodeRange : public TemplateNodeCreator<TemplateNodeRange> -{ - public: - TemplateNodeRange(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeRange>(parser,parent,line), m_down(FALSE) - { - TRACE(("{TemplateNodeRange(%s)\n",qPrint(data))); - QCString start,end; - int i1 = data.find(" from "); - int i2 = data.find(" to "); - int i3 = data.find(" downto "); - if (i1==-1) - { - if (data.endsWith(" from")) - { - parser->warn(m_templateName,line,"range missing after 'from' keyword"); - } - else if (data=="from") - { - parser->warn(m_templateName,line,"range needs an iterator variable and a range"); - } - else - { - parser->warn(m_templateName,line,"range is missing 'from' keyword"); - } - } - else if (i2==-1 && i3==-1) - { - if (data.endsWith(" to")) - { - parser->warn(m_templateName,line,"range is missing end value after 'to' keyword"); - } - else if (data.endsWith(" downto")) - { - parser->warn(m_templateName,line,"range is missing end value after 'downto' keyword"); - } - else - { - parser->warn(m_templateName,line,"range is missing 'to' or 'downto' keyword"); - } - } - else - { - m_var = data.left(i1).stripWhiteSpace(); - if (m_var.isEmpty()) - { - parser->warn(m_templateName,line,"range needs an iterator variable"); - } - start = data.mid(i1+6,i2-i1-6).stripWhiteSpace(); - if (i2!=-1) - { - end = data.right(data.length()-i2-4).stripWhiteSpace(); - m_down = FALSE; - } - else if (i3!=-1) - { - end = data.right(data.length()-i3-8).stripWhiteSpace(); - m_down = TRUE; - } - } - ExpressionParser expParser(parser,line); - m_startExpr = expParser.parse(start); - m_endExpr = expParser.parse(end); - - StringVector stopAt = { "endrange" }; - parser->parse(this,line,stopAt,m_loopNodes); - parser->removeNextToken(); // skip over endrange - TRACE(("}TemplateNodeRange(%s)\n",qPrint(data))); - } - - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - //printf("TemplateNodeRange::render #loopNodes=%d\n", - // m_loopNodes.count()); - if (m_startExpr && m_endExpr) - { - TemplateVariant vs = m_startExpr->resolve(c); - TemplateVariant ve = m_endExpr->resolve(c); - if (vs.isInt() && ve.isInt()) - { - size_t s = vs.toInt(); - size_t e = ve.toInt(); - size_t l = m_down ? s-e+1 : e-s+1; - if (l>0) - { - c->push(); - //int index = m_reversed ? list.count() : 0; - const TemplateVariant *parentLoop = c->getRef("forloop"); - size_t index = 0; - size_t i = m_down ? e : s; - bool done=false; - while (!done) - { - // set the forloop meta-data variable - TemplateStructPtr ls = TemplateStruct::alloc(); - ls->set("counter0", index); - ls->set("counter", (index+1)); - ls->set("revcounter", (l-index)); - ls->set("revcounter0", (l-index-1)); - ls->set("first", index==0); - ls->set("last", index==l-1); - ls->set("parentloop",parentLoop ? *parentLoop : TemplateVariant()); - c->set("forloop",std::static_pointer_cast<TemplateStructIntf>(ls)); - - // set the iterator variable - c->set(m_var,i); - - // render all items for this iteration of the loop - m_loopNodes.render(ts,c); - - index++; - if (m_down) - { - i--; - done = i<e; - } - else - { - i++; - done = i>e; - } - } - c->pop(); - } - else - { - ci->warn(m_templateName,m_line,"range %d %s %d is empty!", - s,m_down?"downto":"to",e); - } - } - else if (!vs.isInt()) - { - ci->warn(m_templateName,m_line,"range requires a start value of integer type!"); - } - else if (!ve.isInt()) - { - ci->warn(m_templateName,m_line,"range requires an end value of integer type!"); - } - } - else if (!m_startExpr) - { - ci->warn(m_templateName,m_line,"range has empty start value"); - } - else if (!m_endExpr) - { - ci->warn(m_templateName,m_line,"range has empty end value"); - } - } - - private: - bool m_down = false; - ExprAstPtr m_startExpr; - ExprAstPtr m_endExpr; - QCString m_var; - TemplateNodeList m_loopNodes; -}; - -//---------------------------------------------------------- - -/** @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), m_reversed(FALSE) - { - TRACE(("{TemplateNodeFor(%s)\n",qPrint(data))); - QCString exprStr; - int i = data.find(" in "); - if (i==-1) - { - if (data.endsWith(" in")) - { - parser->warn(m_templateName,line,"for is missing container after 'in' keyword"); - } - else if (data=="in") - { - parser->warn(m_templateName,line,"for needs at least one iterator variable"); - } - else - { - parser->warn(m_templateName,line,"for is missing 'in' keyword"); - } - } - else - { - m_vars = split(data.left(i),","); - if (m_vars.size()==0) - { - parser->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()) - { - parser->warn(m_templateName,line,"for is missing container after 'in' keyword"); - } - } - ExpressionParser expParser(parser,line); - m_expr = expParser.parse(exprStr); - - StringVector stopAt = { "endfor", "empty" }; - parser->parse(this,line,stopAt,m_loopNodes); - auto tok = parser->takeNextToken(); - if (tok && tok->data=="empty") - { - stopAt.pop_back(); - parser->parse(this,line,stopAt,m_emptyNodes); - parser->removeNextToken(); // skip over endfor - } - TRACE(("}TemplateNodeFor(%s)\n",qPrint(data))); - } - - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->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); - if (v.isFunction()) - { - v = v.call(); - } - const TemplateListIntfPtr list = v.toList(); - if (list) - { - size_t 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"); - size_t index = m_reversed ? listSize-1 : 0; - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - TemplateVariant ve; - for (m_reversed ? it->toLast() : it->toFirst(); - (it->current(ve)); - m_reversed ? it->toPrev() : it->toNext()) - { - TemplateStructPtr s = TemplateStruct::alloc(); - s->set("counter0", index); - s->set("counter", index+1); - s->set("revcounter", listSize-index); - s->set("revcounter0", listSize-index-1); - s->set("first", index==0); - s->set("last", index==listSize-1); - s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant()); - c->set("forloop",std::static_pointer_cast<TemplateStructIntf>(s)); - - // add variables for this loop to the context - //obj->addVariableToContext(index,m_vars,c); - uint vi=0; - if (m_vars.size()==1) // loop variable represents an item - { - c->set(m_vars[vi++],ve); - } - else if (m_vars.size()>1 && (ve.isStruct() || ve.isWeakStruct())) - // loop variables represent elements in a list item - { - TemplateStructIntfPtr vs = ve.toStruct(); - if (vs) - { - for (uint i=0;i<m_vars.size();i++,vi++) - { - c->set(m_vars[vi],vs->get(m_vars[vi])); - } - } - } - for (;vi<m_vars.size();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(); - } - else // simple type... - { - ci->warn(m_templateName,m_line,"for requires a variable of list type, got type '%s'!",qPrint(v.typeAsString())); - } - } - } - - private: - bool m_reversed = false; - ExprAstPtr m_expr; - std::vector<QCString> m_vars; - TemplateNodeList m_loopNodes; - TemplateNodeList m_emptyNodes; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'markers' tag in a template */ -class TemplateNodeMsg : public TemplateNodeCreator<TemplateNodeMsg> -{ - public: - TemplateNodeMsg(TemplateParser *parser,TemplateNode *parent,int line,const QCString &) - : TemplateNodeCreator<TemplateNodeMsg>(parser,parent,line) - { - TRACE(("{TemplateNodeMsg()\n")); - StringVector stopAt = { "endmsg" }; - parser->parse(this,line,stopAt,m_nodes); - parser->removeNextToken(); // skip over endmsg - TRACE(("}TemplateNodeMsg()\n")); - } - void render(TextStream &, TemplateContext *c) - { - TRACE(("{TemplateNodeMsg::render\n")); - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - TemplateEscapeIntf *escIntf = ci->escapeIntf(); - ci->setActiveEscapeIntf(0); // avoid escaping things we send to standard out - bool enable = ci->spacelessEnabled(); - ci->enableSpaceless(FALSE); - TextStream t(&std::cout); - m_nodes.render(t,c); - t.flush(); - std::cout << "\n"; - ci->setActiveEscapeIntf(escIntf); - ci->enableSpaceless(enable); - TRACE(("}TemplateNodeMsg::render\n")); - } - private: - TemplateNodeList m_nodes; -}; - - -//---------------------------------------------------------- - -/** @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",qPrint(data))); - m_blockName = data; - if (m_blockName.isEmpty()) - { - parser->warn(parser->templateName(),line,"block tag without name"); - } - StringVector stopAt = { "endblock" }; - parser->parse(this,line,stopAt,m_nodes); - parser->removeNextToken(); // skip over endblock - TRACE(("}TemplateNodeBlock(%s)\n",qPrint(data))); - } - - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - 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(); - TextStream ss; - // 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 - } - QCString super = ss.str(); - // add 'block.super' variable to allow access to parent block content - TemplateStructPtr superBlock = TemplateStruct::alloc(); - superBlock->set("super",TemplateVariant(super.data(),TRUE)); - ci->set("block",std::static_pointer_cast<TemplateStructIntf>(superBlock)); - // render the overruled block contents - t->engine()->enterBlock(nb->m_templateName,nb->m_blockName,nb->m_line); - nb->m_nodes.render(ts,c); - t->engine()->leaveBlock(); - ci->pop(); - // re-add block to the context - ci->blockContext()->push(nb); - } - else // block has no overrule - { - t->engine()->enterBlock(m_templateName,m_blockName,m_line); - m_nodes.render(ts,c); - t->engine()->leaveBlock(); - } - } - } - - 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",qPrint(data))); - ExpressionParser ep(parser,line); - if (data.isEmpty()) - { - parser->warn(m_templateName,line,"extend tag is missing template file argument"); - } - m_extendExpr = ep.parse(data); - StringVector stopAt; - parser->parse(this,line,stopAt,m_nodes); - TRACE(("}TemplateNodeExtend(%s)\n",qPrint(data))); - } - - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - if (m_extendExpr==0) return; - - QCString extendFile = m_extendExpr->resolve(c).toString(); - if (extendFile.isEmpty()) - { - ci->warn(m_templateName,m_line,"invalid parameter for extend command"); - } - - // goto root of tree (template node) - TemplateImpl *t = getTemplate(); - if (t) - { - Template *bt = t->engine()->loadByName(extendFile,m_line); - TemplateImpl *baseTemplate = bt ? dynamic_cast<TemplateImpl*>(bt) : 0; - if (baseTemplate) - { - // fill block context - TemplateBlockContext *bc = ci->blockContext(); - - // add overruling blocks to the context - for (const auto &n : m_nodes) - { - TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n.get()); - if (nb) - { - bc->add(nb); - } - TemplateNodeMsg *msg = dynamic_cast<TemplateNodeMsg*>(n.get()); - if (msg) - { - msg->render(ts,c); - } - } - - // render the base template with the given context - baseTemplate->render(ts,c); - - // clean up - bc->clear(); - } - else - { - ci->warn(m_templateName,m_line,"failed to load template %s for extend",qPrint(extendFile)); - } - t->engine()->unload(bt); - } - } - - private: - ExprAstPtr 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",qPrint(data))); - ExpressionParser ep(parser,line); - if (data.isEmpty()) - { - parser->warn(m_templateName,line,"include tag is missing template file argument"); - } - m_includeExpr = ep.parse(data); - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - if (m_includeExpr) - { - QCString includeFile = m_includeExpr->resolve(c).toString(); - if (includeFile.isEmpty()) - { - ci->warn(m_templateName,m_line,"invalid parameter for include command\n"); - } - else - { - TemplateImpl *t = getTemplate(); - if (t) - { - Template *it = t->engine()->loadByName(includeFile,m_line); - TemplateImpl *incTemplate = it ? dynamic_cast<TemplateImpl*>(it) : 0; - if (incTemplate) - { - incTemplate->render(ts,c); - } - else - { - ci->warn(m_templateName,m_line,"failed to load template '%s' for include",qPrint(includeFile)); - } - t->engine()->unload(it); - } - } - } - } - - private: - ExprAstPtr m_includeExpr; -}; - -//---------------------------------------------------------- - -static void stripLeadingWhiteSpace(QCString &s) -{ - bool skipSpaces=true; - const char *src = s.data(); - char *dst = s.rawData(); - char c; - while ((c=*src++)) - { - if (c=='\n') { *dst++=c; skipSpaces=true; } - else if (c==' ' && skipSpaces) {} - else { *dst++ = c; skipSpaces=false; } - } - s.resize(dst-s.data()+1); -} - -/** @brief Class representing an 'create' 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",qPrint(data))); - if (data.isEmpty()) - { - parser->warn(m_templateName,line,"create tag is missing arguments"); - } - int i = data.find(" from "); - if (i==-1) - { - if (data.endsWith(" from")) - { - parser->warn(m_templateName,line,"create is missing template name after 'from' keyword"); - } - else if (data=="from") - { - parser->warn(m_templateName,line,"create needs a file name and a template name"); - } - else - { - parser->warn(m_templateName,line,"create is missing 'from' keyword"); - } - } - else - { - ExpressionParser ep(parser,line); - m_fileExpr = ep.parse(data.left(i).stripWhiteSpace()); - m_templateExpr = ep.parse(data.mid(i+6).stripWhiteSpace()); - } - } - void render(TextStream &, TemplateContext *c) - { - TRACE(("{TemplateNodeCreate::render\n")); - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - 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()) - { - ci->warn(m_templateName,m_line,"empty template name parameter for create command\n"); - } - else if (outputFile.isEmpty()) - { - ci->warn(m_templateName,m_line,"empty file name parameter for create command\n"); - } - else - { - TemplateImpl *t = getTemplate(); - if (t) - { - QCString extension=outputFile; - int i=extension.findRev('.'); - if (i!=-1) - { - extension=extension.right(extension.length()-i-1); - } - t->engine()->setOutputExtension(extension); - Template *ct = t->engine()->loadByName(templateFile,m_line); - TemplateImpl *createTemplate = ct ? dynamic_cast<TemplateImpl*>(ct) : 0; - if (createTemplate) - { - mkpath(ci,outputFile.str()); - if (!ci->outputDirectory().isEmpty()) - { - outputFile.prepend(ci->outputDirectory()+"/"); - } - //printf("NoteCreate(%s)\n",qPrint(outputFile)); - std::ofstream f(outputFile.str(),std::ofstream::out | std::ofstream::binary); - if (f.is_open()) - { - TextStream ts(&f); - TemplateEscapeIntf *escIntf = ci->escapeIntf(); - ci->selectEscapeIntf(extension); - TextStream os; - createTemplate->render(os,c); - QCString out = os.str(); - stripLeadingWhiteSpace(out); - ts << out; - t->engine()->unload(t); - ci->setActiveEscapeIntf(escIntf); - } - else - { - ci->warn(m_templateName,m_line,"failed to open output file '%s' for create command",qPrint(outputFile)); - } - } - else - { - ci->warn(m_templateName,m_line,"failed to load template '%s' for include",qPrint(templateFile)); - } - t->engine()->setOutputExtension(""); - } - } - } - TRACE(("}TemplateNodeCreate::render\n")); - } - - private: - ExprAstPtr m_templateExpr; - ExprAstPtr m_fileExpr; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'tree' tag in a template */ -class TemplateNodeTree : public TemplateNodeCreator<TemplateNodeTree> -{ - struct TreeContext - { - TreeContext(TemplateNodeTree *o,const TemplateListIntfPtr l,TemplateContext *c) - : object(o), list(l), templateCtx(c) {} - TemplateNodeTree *object; - const TemplateListIntfPtr list; - TemplateContext *templateCtx; - }; - public: - TemplateNodeTree(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeTree>(parser,parent,line) - { - TRACE(("{TemplateNodeTree(%s)\n",qPrint(data))); - ExpressionParser ep(parser,line); - if (data.isEmpty()) - { - parser->warn(m_templateName,line,"recursetree tag is missing data argument"); - } - m_treeExpr = ep.parse(data); - StringVector stopAt = { "endrecursetree" }; - parser->parse(this,line,stopAt,m_treeNodes); - parser->removeNextToken(); // skip over endrecursetree - TRACE(("}TemplateNodeTree(%s)\n",qPrint(data))); - } - QCString renderChildren(const TreeContext *ctx) - { - //printf("TemplateNodeTree::renderChildren(%d)\n",ctx->list->count()); - // render all children of node to a string and return it - TemplateContext *c = ctx->templateCtx; - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return QCString(); // should not happen - TextStream ss; - c->push(); - TemplateVariant node; - TemplateListIntf::ConstIteratorPtr it = ctx->list->createIterator(); - for (it->toFirst();(it->current(node));it->toNext()) - { - c->set("node",node); - bool hasChildren=FALSE; - const TemplateStructIntfPtr ns = node.toStruct(); - if (ns) // node is a struct - { - TemplateVariant v = ns->get("children"); - if (v.isValid()) // with a field 'children' - { - const TemplateListIntfPtr list = v.toList(); - if (list && list->count()>0) // non-empty list - { - TreeContext childCtx(this,list,ctx->templateCtx); - TemplateVariant children( - [childCtx](const TemplateVariantList &) { - return TemplateVariant(childCtx.object->renderChildren(&childCtx),TRUE); - }); - children.setRaw(TRUE); - c->set("children",children); - m_treeNodes.render(ss,c); - hasChildren=TRUE; - } - else if (list==0) - { - ci->warn(m_templateName,m_line,"recursetree: children attribute has type '%s' instead of list\n",qPrint(v.typeAsString())); - } - } - //else - //{ - // ci->warn(m_templateName,m_line,"recursetree: children attribute is not valid"); - //} - } - if (!hasChildren) - { - c->set("children",TemplateVariant("")); // provide default - m_treeNodes.render(ss,c); - } - } - c->pop(); - return ss.str(); - } - void render(TextStream &ts, TemplateContext *c) - { - //printf("TemplateNodeTree::render()\n"); - TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - TemplateVariant v = m_treeExpr->resolve(c); - const TemplateListIntfPtr list = v.toList(); - if (list) - { - TreeContext ctx(this,list,c); - ts << renderChildren(&ctx); - } - else - { - ci->warn(m_templateName,m_line,"recursetree's argument should be a list type"); - } - } - - private: - ExprAstPtr m_treeExpr; - TemplateNodeList m_treeNodes; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'indexentry' tag in a template */ -class TemplateNodeIndexEntry : public TemplateNodeCreator<TemplateNodeIndexEntry> -{ - struct Mapping - { - Mapping(const QCString &n,std::unique_ptr<ExprAst> &&e) : name(n), value(std::move(e)) {} - QCString name; - ExprAstPtr value; - }; - public: - TemplateNodeIndexEntry(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeIndexEntry>(parser,parent,line) - { - TRACE(("{TemplateNodeIndexEntry(%s)\n",qPrint(data))); - ExpressionParser expParser(parser,line); - std::vector<QCString> args = split(data," "); - auto it = args.begin(); - if (it==args.end() || (*it).find('=')!=-1) - { - parser->warn(parser->templateName(),line,"Missing name for indexentry tag"); - } - else - { - m_name = *it; - ++it; - while (it!=args.end()) - { - QCString arg = *it; - int j=arg.find('='); - if (j>0) - { - ExprAstPtr expr = expParser.parse(arg.mid(j+1)); - if (expr) - { - m_args.emplace_back(arg.left(j),std::move(expr)); - } - } - else - { - parser->warn(parser->templateName(),line,"invalid argument '%s' for indexentry tag",qPrint(arg)); - } - ++it; - } - } - TRACE(("}TemplateNodeIndexEntry(%s)\n",qPrint(data))); - } - void render(TextStream &, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - if (!m_name.isEmpty()) - { - ci->setLocation(m_templateName,m_line); - std::vector<TemplateKeyValue> list; - for (const auto &mapping : m_args) - { - list.emplace_back(mapping.name,mapping.value->resolve(c)); - } - ci->addIndexEntry(m_name,list); - } - } - private: - QCString m_name; - std::vector<Mapping> m_args; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'opensubindex' tag in a template */ -class TemplateNodeOpenSubIndex : public TemplateNodeCreator<TemplateNodeOpenSubIndex> -{ - public: - TemplateNodeOpenSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeOpenSubIndex>(parser,parent,line) - { - TRACE(("{TemplateNodeOpenSubIndex(%s)\n",qPrint(data))); - m_name = data.stripWhiteSpace(); - if (m_name.isEmpty()) - { - parser->warn(parser->templateName(),line,"Missing argument for opensubindex tag"); - } - else if (m_name.find(' ')!=-1) - { - parser->warn(parser->templateName(),line,"Expected single argument for opensubindex tag got '%s'",qPrint(data)); - m_name=""; - } - TRACE(("}TemplateNodeOpenSubIndex(%s)\n",qPrint(data))); - } - void render(TextStream &, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - if (!m_name.isEmpty()) - { - ci->setLocation(m_templateName,m_line); - ci->openSubIndex(m_name); - } - } - private: - QCString m_name; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'closesubindex' tag in a template */ -class TemplateNodeCloseSubIndex : public TemplateNodeCreator<TemplateNodeCloseSubIndex> -{ - public: - TemplateNodeCloseSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeCloseSubIndex>(parser,parent,line) - { - TRACE(("{TemplateNodeCloseSubIndex(%s)\n",qPrint(data))); - m_name = data.stripWhiteSpace(); - if (m_name.isEmpty()) - { - parser->warn(parser->templateName(),line,"Missing argument for closesubindex tag"); - } - else if (m_name.find(' ')!=-1 || m_name.isEmpty()) - { - parser->warn(parser->templateName(),line,"Expected single argument for closesubindex tag got '%s'",qPrint(data)); - m_name=""; - } - TRACE(("}TemplateNodeCloseSubIndex(%s)\n",qPrint(data))); - } - void render(TextStream &, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - if (!m_name.isEmpty()) - { - ci->setLocation(m_templateName,m_line); - ci->closeSubIndex(m_name); - } - } - private: - QCString m_name; -}; - - -//---------------------------------------------------------- - -/** @brief Class representing an 'with' tag in a template */ -class TemplateNodeWith : public TemplateNodeCreator<TemplateNodeWith> -{ - struct Mapping - { - Mapping(const QCString &n,ExprAstPtr &&e) : name(n), value(std::move(e)) {} - QCString name; - ExprAstPtr value; - }; - public: - TemplateNodeWith(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeWith>(parser,parent,line) - { - TRACE(("{TemplateNodeWith(%s)\n",qPrint(data))); - ExpressionParser expParser(parser,line); - QCString filteredData = data; - removeSpacesAroundEquals(filteredData); - std::vector<QCString> args = split(filteredData," "); - auto it = args.begin(); - while (it!=args.end()) - { - QCString arg = *it; - int j=arg.find('='); - if (j>0) - { - ExprAstPtr expr = expParser.parse(arg.mid(j+1)); - if (expr) - { - m_args.emplace_back(arg.left(j),std::move(expr)); - } - } - else - { - parser->warn(parser->templateName(),line,"invalid argument '%s' for 'with' tag",qPrint(arg)); - } - ++it; - } - StringVector stopAt = { "endwith" }; - parser->parse(this,line,stopAt,m_nodes); - parser->removeNextToken(); // skip over endwith - TRACE(("}TemplateNodeWith(%s)\n",qPrint(data))); - } - ~TemplateNodeWith() - { - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - c->push(); - for (const auto &mapping : m_args) - { - TemplateVariant value = mapping.value->resolve(c); - ci->set(mapping.name,value); - } - m_nodes.render(ts,c); - c->pop(); - } - private: - TemplateNodeList m_nodes; - std::vector<Mapping> m_args; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'cycle' tag in a template */ -class TemplateNodeCycle : public TemplateNodeCreator<TemplateNodeCycle> -{ - public: - TemplateNodeCycle(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeCycle>(parser,parent,line) - { - TRACE(("{TemplateNodeCycle(%s)\n",qPrint(data))); - m_index=0; - ExpressionParser expParser(parser,line); - std::vector<QCString> args = split(data," "); - auto it = args.begin(); - while (it!=args.end()) - { - ExprAstPtr expr = expParser.parse(*it); - if (expr) - { - m_args.emplace_back(std::move(expr)); - } - ++it; - } - if (m_args.size()<2) - { - parser->warn(parser->templateName(),line,"expected at least two arguments for cycle command, got %zu",m_args.size()); - } - TRACE(("}TemplateNodeCycle(%s)\n",qPrint(data))); - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - if (m_index<m_args.size()) - { - TemplateVariant v = m_args[m_index]->resolve(c); - if (v.isFunction()) - { - v = v.call(); - } - if (ci->escapeIntf() && !v.raw()) - { - if (ci->needsRecoding()) - { - ts << ci->recode(ci->escapeIntf()->escape(v.toString())); - } - else - { - ts << ci->escapeIntf()->escape(v.toString()); - } - } - else - { - if (ci->needsRecoding()) - { - ts << ci->recode(v.toString()); - } - else - { - ts << v.toString(); - } - } - } - if (++m_index==m_args.size()) // wrap around - { - m_index=0; - } - } - private: - size_t m_index = 0; - ExprAstList m_args; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'set' tag in a template */ -class TemplateNodeSet : public TemplateNodeCreator<TemplateNodeSet> -{ - struct Mapping - { - Mapping(const QCString &n,ExprAstPtr &&e) : name(n), value(std::move(e)) {} - QCString name; - ExprAstPtr value; - }; - public: - TemplateNodeSet(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeSet>(parser,parent,line) - { - TRACE(("{TemplateNodeSet(%s)\n",qPrint(data))); - ExpressionParser expParser(parser,line); - // data format: name=expression - int j=data.find('='); - ExprAstPtr expr = 0; - if (j>0 && (expr = expParser.parse(data.mid(j+1)))) - { - m_mapping = std::make_unique<Mapping>(data.left(j),std::move(expr)); - } - TRACE(("}TemplateNodeSet(%s)\n",qPrint(data))); - } - ~TemplateNodeSet() - { - } - void render(TextStream &, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - if (m_mapping) - { - TemplateVariant value = m_mapping->value->resolve(c); - ci->set(m_mapping->name,value); - } - } - private: - std::unique_ptr<Mapping> m_mapping; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'spaceless' tag in a template */ -class TemplateNodeSpaceless : public TemplateNodeCreator<TemplateNodeSpaceless> -{ - public: - TemplateNodeSpaceless(TemplateParser *parser,TemplateNode *parent,int line,const QCString &) - : TemplateNodeCreator<TemplateNodeSpaceless>(parser,parent,line) - { - TRACE(("{TemplateNodeSpaceless()\n")); - StringVector stopAt = { "endspaceless" }; - parser->parse(this,line,stopAt,m_nodes); - parser->removeNextToken(); // skip over endwith - TRACE(("}TemplateNodeSpaceless()\n")); - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - bool wasSpaceless = ci->spacelessEnabled(); - ci->enableSpaceless(TRUE); - m_nodes.render(ts,c); - ci->enableSpaceless(wasSpaceless); - } - private: - TemplateNodeList m_nodes; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'markers' tag in a template */ -class TemplateNodeMarkers : public TemplateNodeCreator<TemplateNodeMarkers> -{ - public: - TemplateNodeMarkers(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeMarkers>(parser,parent,line) - { - TRACE(("{TemplateNodeMarkers(%s)\n",qPrint(data))); - int i = data.find(" in "); - int w = data.find(" with "); - if (i==-1 || w==-1 || w<i) - { - parser->warn(m_templateName,line,"markers tag as wrong format. Expected: markers <var> in <list> with <string_with_markers>"); - } - else - { - ExpressionParser expParser(parser,line); - m_var = data.left(i); - m_listExpr = expParser.parse(data.mid(i+4,w-i-4)); - m_patternExpr = expParser.parse(data.right(data.length()-w-6)); - } - StringVector stopAt = { "endmarkers" }; - parser->parse(this,line,stopAt,m_nodes); - parser->removeNextToken(); // skip over endmarkers - TRACE(("}TemplateNodeMarkers(%s)\n",qPrint(data))); - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - if (!m_var.isEmpty() && m_listExpr && m_patternExpr) - { - TemplateVariant v = m_listExpr->resolve(c); - const TemplateListIntfPtr list = v.toList(); - TemplateVariant patternStr = m_patternExpr->resolve(c); - if (list) - { - if (patternStr.isString()) - { - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - c->push(); - std::string str = patternStr.toString().str(); - - static const reg::Ex marker(R"(@\d+)"); - reg::Iterator re_it(str,marker); - reg::Iterator end; - size_t index=0; - for ( ; re_it!=end ; ++re_it) - { - const auto &match = *re_it; - size_t newIndex = match.position(); - size_t matchLen = match.length(); - std::string part = str.substr(index,newIndex-index); - if (ci->needsRecoding()) - { - ts << ci->recode(QCString(part)); // write text before marker - } - else - { - ts << part; // write text before marker - } - unsigned long entryIndex = std::stoul(match.str().substr(1)); - TemplateVariant var; - size_t i=0; - // search for list element at position id - for (it->toFirst(); (it->current(var)) && i<entryIndex; it->toNext(),i++) {} - if (i==entryIndex) // found element - { - TemplateStructPtr s = TemplateStruct::alloc(); - s->set("id",i); - c->set("markers",std::static_pointer_cast<TemplateStructIntf>(s)); - c->set(m_var,var); // define local variable to hold element of list type - bool wasSpaceless = ci->spacelessEnabled(); - ci->enableSpaceless(TRUE); - m_nodes.render(ts,c); - ci->enableSpaceless(wasSpaceless); - } - else if (i<entryIndex) - { - ci->warn(m_templateName,m_line,"markers list does not an element for marker position %d",i); - } - index=newIndex+matchLen; // set index just after marker - } - if (ci->needsRecoding()) - { - ts << ci->recode(str.substr(index)); // write text after last marker - } - else - { - ts << str.substr(index); // write text after last marker - } - c->pop(); - } - else - { - ci->warn(m_templateName,m_line,"markers requires a parameter of string type after 'with'!"); - } - } - else - { - ci->warn(m_templateName,m_line,"markers requires a parameter of list type after 'in'!"); - } - } - } - private: - TemplateNodeList m_nodes; - QCString m_var; - ExprAstPtr m_listExpr; - ExprAstPtr m_patternExpr; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'tabbing' tag in a template */ -class TemplateNodeTabbing : public TemplateNodeCreator<TemplateNodeTabbing> -{ - public: - TemplateNodeTabbing(TemplateParser *parser,TemplateNode *parent,int line,const QCString &) - : TemplateNodeCreator<TemplateNodeTabbing>(parser,parent,line) - { - TRACE(("{TemplateNodeTabbing()\n")); - StringVector stopAt = { "endtabbing" }; - parser->parse(this,line,stopAt,m_nodes); - parser->removeNextToken(); // skip over endtabbing - TRACE(("}TemplateNodeTabbing()\n")); - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - bool wasTabbing = ci->tabbingEnabled(); - ci->enableTabbing(TRUE); - m_nodes.render(ts,c); - ci->enableTabbing(wasTabbing); - } - private: - TemplateNodeList m_nodes; -}; - -//---------------------------------------------------------- - -/** @brief Class representing an 'markers' tag in a template */ -class TemplateNodeResource : public TemplateNodeCreator<TemplateNodeResource> -{ - public: - TemplateNodeResource(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeResource>(parser,parent,line) - { - TRACE(("{TemplateNodeResource(%s)\n",qPrint(data))); - ExpressionParser ep(parser,line); - int i; - if (data.isEmpty()) - { - parser->warn(m_templateName,line,"resource tag is missing resource file argument"); - m_resExpr.reset(); - m_asExpr.reset(); - } - else if ((i=data.find(" as "))!=-1) // resource a as b - { - m_resExpr = ep.parse(data.left(i)); // part before as - m_asExpr = ep.parse(data.mid(i+4)); // part after as - } - else if ((i=data.find(" append "))!=-1) // resource a appends to b - { - m_resExpr = ep.parse(data.left(i)); // part before append - m_asExpr = ep.parse(data.mid(i+8)); // part after append - m_append = true; - } - else // resource a - { - m_resExpr = ep.parse(data); - m_asExpr.reset(); - } - TRACE(("}TemplateNodeResource(%s)\n",qPrint(data))); - } - void render(TextStream &, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - if (m_resExpr) - { - QCString resourceFile = m_resExpr->resolve(c).toString(); - if (resourceFile.isEmpty()) - { - ci->warn(m_templateName,m_line,"invalid parameter for resource command\n"); - } - else - { - QCString outputDirectory = ci->outputDirectory(); - if (m_asExpr) - { - QCString targetFile = m_asExpr->resolve(c).toString(); - mkpath(ci,targetFile.str()); - if (targetFile.isEmpty()) - { - ci->warn(m_templateName,m_line,"invalid parameter at right side of '%s' for resource command\n", m_append ? "append" : "as"); - } - else - { - ResourceMgr::instance().copyResourceAs(resourceFile,outputDirectory,targetFile,m_append); - } - } - else - { - ResourceMgr::instance().copyResource(resourceFile,outputDirectory); - } - } - } - } - private: - ExprAstPtr m_resExpr; - ExprAstPtr m_asExpr; - bool m_append = false; -}; - -//---------------------------------------------------------- - -/** @brief Class representing the 'encoding' tag in a template */ -class TemplateNodeEncoding : public TemplateNodeCreator<TemplateNodeEncoding> -{ - public: - TemplateNodeEncoding(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) - : TemplateNodeCreator<TemplateNodeEncoding>(parser,parent,line) - { - TRACE(("{TemplateNodeEncoding(%s)\n",qPrint(data))); - ExpressionParser ep(parser,line); - if (data.isEmpty()) - { - parser->warn(m_templateName,line,"encoding tag is missing encoding argument"); - m_encExpr.reset(); - } - else - { - m_encExpr = ep.parse(data); - } - StringVector stopAt = { "endencoding" }; - parser->parse(this,line,stopAt,m_nodes); - parser->removeNextToken(); // skip over endencoding - TRACE(("}TemplateNodeEncoding(%s)\n",qPrint(data))); - } - void render(TextStream &ts, TemplateContext *c) - { - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - ci->setLocation(m_templateName,m_line); - QCString encStr; - if (m_encExpr) - { - encStr = m_encExpr->resolve(c).toString(); - } - QCString oldEncStr = ci->encoding(); - if (!encStr.isEmpty()) - { - ci->setEncoding(m_templateName,m_line,encStr); - } - m_nodes.render(ts,c); - ci->setEncoding(m_templateName,m_line,oldEncStr); - } - private: - ExprAstPtr m_encExpr; - TemplateNodeList m_nodes; -}; - -//---------------------------------------------------------- - -/** @brief Factory class for creating tag AST nodes found in a template */ -class TemplateNodeFactory -{ - public: - typedef TemplateNodePtr (*CreateFunc)(TemplateParser *parser, - TemplateNode *parent, - int line, - const QCString &data); - - static TemplateNodeFactory &instance() - { - static std::unique_ptr<TemplateNodeFactory> instance; - if (instance==0) instance = std::make_unique<TemplateNodeFactory>(); - return *instance; - } - - TemplateNodePtr create(const QCString &name, - TemplateParser *parser, - TemplateNode *parent, - int line, - const QCString &data) - { - auto it = m_registry.find(name.str()); - if (it==m_registry.end()) return 0; - return it->second(parser,parent,line,data); - } - - void registerTemplateNode(const QCString &name,CreateFunc func) - { - m_registry.insert(std::make_pair(name.str(),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: - std::unordered_map<std::string,CreateFunc> 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<TemplateNodeMsg> autoRefMsg("msg"); -static TemplateNodeFactory::AutoRegister<TemplateNodeSet> autoRefSet("set"); -static TemplateNodeFactory::AutoRegister<TemplateNodeTree> autoRefTree("recursetree"); -static TemplateNodeFactory::AutoRegister<TemplateNodeWith> autoRefWith("with"); -static TemplateNodeFactory::AutoRegister<TemplateNodeBlock> autoRefBlock("block"); -static TemplateNodeFactory::AutoRegister<TemplateNodeCycle> autoRefCycle("cycle"); -static TemplateNodeFactory::AutoRegister<TemplateNodeRange> autoRefRange("range"); -static TemplateNodeFactory::AutoRegister<TemplateNodeExtend> autoRefExtend("extend"); -static TemplateNodeFactory::AutoRegister<TemplateNodeCreate> autoRefCreate("create"); -static TemplateNodeFactory::AutoRegister<TemplateNodeRepeat> autoRefRepeat("repeat"); -static TemplateNodeFactory::AutoRegister<TemplateNodeInclude> autoRefInclude("include"); -static TemplateNodeFactory::AutoRegister<TemplateNodeMarkers> autoRefMarkers("markers"); -static TemplateNodeFactory::AutoRegister<TemplateNodeTabbing> autoRefTabbing("tabbing"); -static TemplateNodeFactory::AutoRegister<TemplateNodeResource> autoRefResource("resource"); -static TemplateNodeFactory::AutoRegister<TemplateNodeEncoding> autoRefEncoding("encoding"); -static TemplateNodeFactory::AutoRegister<TemplateNodeSpaceless> autoRefSpaceless("spaceless"); -static TemplateNodeFactory::AutoRegister<TemplateNodeIndexEntry> autoRefIndexEntry("indexentry"); -static TemplateNodeFactory::AutoRegister<TemplateNodeOpenSubIndex> autoRefOpenSubIndex("opensubindex"); -static TemplateNodeFactory::AutoRegister<TemplateNodeCloseSubIndex> autoRefCloseSubIndex("closesubindex"); - -//---------------------------------------------------------- - -TemplateBlockContext::TemplateBlockContext() -{ -} - -TemplateNodeBlock *TemplateBlockContext::get(const QCString &name) const -{ - auto it = m_blocks.find(name.str()); - if (it==m_blocks.end() || it->second.empty()) - { - return 0; - } - else - { - return it->second.back(); - } -} - -TemplateNodeBlock *TemplateBlockContext::pop(const QCString &name) -{ - auto it = m_blocks.find(name.str()); - if (it==m_blocks.end() || it->second.empty()) - { - return 0; - } - else - { - TemplateNodeBlock *bld = it->second.back(); - it->second.pop_back(); - return bld; - } -} - -void TemplateBlockContext::add(TemplateNodeBlock *block) -{ - auto it = m_blocks.find(block->name().str()); - if (it==m_blocks.end()) - { - it = m_blocks.insert(std::make_pair(block->name().str(),NodeBlockList())).first; - } - it->second.push_front(block); -} - -void TemplateBlockContext::add(TemplateBlockContext *ctx) -{ - for (auto &kv : ctx->m_blocks) - { - for (auto &nb : kv.second) - { - add(nb); - } - } -} - -void TemplateBlockContext::clear() -{ - m_blocks.clear(); -} - -void TemplateBlockContext::push(TemplateNodeBlock *block) -{ - auto it = m_blocks.find(block->name().str()); - if (it==m_blocks.end()) - { - it = m_blocks.insert(std::make_pair(block->name().str(),NodeBlockList())).first; - } - it->second.push_back(block); -} - - -//---------------------------------------------------------- - -/** @brief Lexer class for turning a template into a list of tokens */ -class TemplateLexer -{ - public: - TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data); - void tokenize(TemplateTokenStream &tokens); - void setOpenCloseCharacters(char openChar,char closeChar) - { m_openChar=openChar; m_closeChar=closeChar; } - private: - void addToken(TemplateTokenStream &tokens, - const QCString &data,int line,int startPos,int endPos, - TemplateToken::Type type); - void reset(); - const TemplateEngine *m_engine = 0; - QCString m_fileName; - QCString m_data; - char m_openChar = 0; - char m_closeChar = 0; -}; - -TemplateLexer::TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data) : - m_engine(engine), m_fileName(fileName), m_data(data) -{ - m_openChar='{'; - m_closeChar='}'; -} - -void TemplateLexer::tokenize(TemplateTokenStream &tokens) -{ - enum LexerStates - { - StateText, - StateBeginTemplate, - StateTag, - StateEndTag, - StateComment, - StateEndComment, - StateMaybeVar, - StateVariable, - StateEndVariable - }; - - if (m_data.isEmpty()) return; - 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==m_openChar) // {{ or {% or {# or something else - { - state=StateBeginTemplate; - } - else if (c!=' ' && c!='\t' && c!='\n') // non-whitespace text - { - emptyOutputLine=FALSE; - } - break; - case StateBeginTemplate: - switch (c) - { - case '%': // {% - state=StateTag; - markStartPos=pos-1; - break; - case '#': // {# - state=StateComment; - markStartPos=pos-1; - break; - case '{': // {{ - if (m_openChar=='{') - { - state=StateMaybeVar; - } - else - { - state=StateVariable; - } - markStartPos=pos-1; - break; - default: - state=StateText; - emptyOutputLine=FALSE; - break; - } - break; - case StateTag: - if (c=='\n') - { - warn(m_fileName,line,"unexpected new line inside %c%%...%%%c block",m_openChar,m_closeChar); - m_engine->printIncludeContext(m_fileName,line); - } - else if (c=='%') // %} or something else - { - state=StateEndTag; - } - break; - case StateEndTag: - if (c==m_closeChar) // %} - { - // found tag! - state=StateText; - addToken(tokens,m_data,line,lastTokenPos, - emptyOutputLine ? startLinePos : markStartPos, - TemplateToken::Text); - addToken(tokens,m_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 %c%%...%%%c block",m_openChar,m_closeChar); - m_engine->printIncludeContext(m_fileName,line); - } - state=StateTag; - } - break; - case StateComment: - if (c=='\n') - { - warn(m_fileName,line,"unexpected new line inside %c#...#%c block",m_openChar,m_closeChar); - m_engine->printIncludeContext(m_fileName,line); - } - else if (c=='#') // #} or something else - { - state=StateEndComment; - } - break; - case StateEndComment: - if (c==m_closeChar) // #} - { - // found comment tag! - state=StateText; - addToken(tokens,m_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 %c#...#%c block",m_openChar,m_closeChar); - m_engine->printIncludeContext(m_fileName,line); - } - 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: - emptyOutputLine=FALSE; // assume a variable expands to content - if (c=='\n') - { - warn(m_fileName,line,"unexpected new line inside %c{...}%c block",m_openChar,m_closeChar); - m_engine->printIncludeContext(m_fileName,line); - } - else if (c=='}') // }} or something else - { - state=StateEndVariable; - } - break; - case StateEndVariable: - if (c==m_closeChar) // }} - { - // found variable tag! - state=StateText; - addToken(tokens,m_data,line,lastTokenPos, - emptyOutputLine ? startLinePos : markStartPos, - TemplateToken::Text); - addToken(tokens,m_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 %c{...}%c block",m_openChar,m_closeChar); - m_engine->printIncludeContext(m_fileName,line); - } - 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,line, - lastTokenPos,pos, - TemplateToken::Text); - } -} - -void TemplateLexer::addToken(TemplateTokenStream &tokens, - const QCString &data,int line, - int startPos,int endPos, - TemplateToken::Type type) -{ - if (startPos<endPos) - { - int len = endPos-startPos; - QCString text = data.mid(startPos,len); - if (type!=TemplateToken::Text) text = text.stripWhiteSpace(); - tokens.push_back(std::make_unique<TemplateToken>(type,text,line)); - } -} - -//---------------------------------------------------------- - -TemplateParser::TemplateParser(const TemplateEngine *engine, - const QCString &templateName, - TemplateTokenStream &tokens) : - m_engine(engine), m_templateName(templateName), m_tokens(tokens) -{ -} - -void TemplateParser::parse( - TemplateNode *parent,int line,const StringVector &stopAt, - TemplateNodeList &nodes) -{ - TRACE(("{TemplateParser::parse\n")); - // process the tokens. Build node list - while (hasNextToken()) - { - auto tok = takeNextToken(); - TRACE(("%p:Token type=%d data='%s' line=%d\n", - (void*)parent,tok->type,qPrint(tok->data),tok->line)); - switch(tok->type) - { - case TemplateToken::Text: - nodes.push_back(std::make_unique<TemplateNodeText>(this,parent,tok->line,tok->data)); - break; - case TemplateToken::Variable: // {{ var }} - nodes.push_back(std::make_unique<TemplateNodeVariable>(this,parent,tok->line,tok->data)); - break; - case TemplateToken::Block: // {% tag %} - { - QCString command = tok->data; - int sep = command.find(' '); - if (sep!=-1) - { - command=command.left(sep); - } - TemplateToken *tok_ptr = tok.get(); - if (std::find(stopAt.begin(),stopAt.end(),command.str())!=stopAt.end()) - { - prependToken(std::move(tok)); - TRACE(("}TemplateParser::parse: stop\n")); - return; - } - QCString arg; - if (sep!=-1) - { - arg = tok_ptr->data.mid(sep+1); - } - TemplateNodePtr node = TemplateNodeFactory::instance().create( - command,this,parent,tok_ptr->line,arg); - if (node) - { - nodes.push_back(std::move(node)); - } - else if (command=="empty" || command=="else" || - command=="endif" || command=="endfor" || - command=="endblock" || command=="endwith" || - command=="endrecursetree" || command=="endspaceless" || - command=="endmarkers" || command=="endmsg" || - command=="endrepeat" || command=="elif" || - command=="endrange" || command=="endtabbing" || - command=="endencoding") - { - warn(m_templateName,tok_ptr->line,"Found tag '%s' without matching start tag",qPrint(command)); - } - else - { - warn(m_templateName,tok_ptr->line,"Unknown tag '%s'",qPrint(command)); - } - } - break; - } - } - if (!stopAt.empty()) - { - QCString options; - for (const auto &s : stopAt) - { - if (!options.isEmpty()) options+=", "; - options+=s.c_str(); - } - warn(m_templateName,line,"Unclosed tag in template, expected one of: %s", - qPrint(options)); - } - TRACE(("}TemplateParser::parse: last token\n")); -} - -bool TemplateParser::hasNextToken() const -{ - return !m_tokens.empty(); -} - -TemplateTokenPtr TemplateParser::takeNextToken() -{ - if (m_tokens.empty()) return TemplateTokenPtr(); - auto tok = std::move(m_tokens.front()); - m_tokens.pop_front(); - return tok; -} - -const TemplateToken *TemplateParser::currentToken() const -{ - return m_tokens.front().get(); -} - -void TemplateParser::removeNextToken() -{ - m_tokens.pop_front(); -} - -void TemplateParser::prependToken(TemplateTokenPtr &&token) -{ - m_tokens.push_front(std::move(token)); -} - -void TemplateParser::warn(const QCString &fileName,int line,const char *fmt,...) const -{ - va_list args; - va_start(args,fmt); - va_warn(fileName,line,fmt,args); - va_end(args); - m_engine->printIncludeContext(fileName,line); -} - - - -//---------------------------------------------------------- - - -TemplateImpl::TemplateImpl(TemplateEngine *engine,const QCString &name,const QCString &data, - const QCString &extension) - : TemplateNode(0) -{ - //printf("%p:TemplateImpl::TemplateImpl(%s)\n",(void*)this,qPrint(name)); - m_name = name; - m_engine = engine; - TemplateLexer lexer(engine,name,data); - if (extension=="tex") - { - lexer.setOpenCloseCharacters('<','>'); - } - TemplateTokenStream tokens; - lexer.tokenize(tokens); - TemplateParser parser(engine,name,tokens); - parser.parse(this,1,StringVector(),m_nodes); -} - -TemplateImpl::~TemplateImpl() -{ - //printf("%p:TemplateImpl::~TemplateImpl(%s)\n",(void*)this,qPrint(m_name)); -} - -void TemplateImpl::render(TextStream &ts, TemplateContext *c) -{ - TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c); - if (ci==0) return; // should not happen - if (!m_nodes.empty()) - { - TemplateNodeExtend *ne = dynamic_cast<TemplateNodeExtend*>(m_nodes.front().get()); - if (ne==0) // normal template, add blocks to block context - { - TemplateBlockContext *bc = ci->blockContext(); - for (const auto &n : m_nodes) - { - TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n.get()); - if (nb) - { - bc->add(nb); - } - } - } - m_nodes.render(ts,c); - } -} - -//---------------------------------------------------------- - -/** @brief Private data of the template engine */ -class TemplateEngine::Private -{ - class IncludeEntry - { - public: - enum Type { Template, Block }; - IncludeEntry(Type type,const QCString &fileName,const QCString &blockName,int line) - : m_type(type), m_fileName(fileName), m_blockName(blockName), m_line(line) {} - Type type() const { return m_type; } - QCString fileName() const { return m_fileName; } - QCString blockName() const { return m_blockName; } - int line() const { return m_line; } - - private: - Type m_type = Template; - QCString m_fileName; - QCString m_blockName; - int m_line = 0; - }; - public: - Private(TemplateEngine *engine) : m_engine(engine) - { - //printf("%p:TemplateEngine::Private::Private()\n",(void*)this); - } - ~Private() - { - //printf("%p:TemplateEngine::Private::~Private()\n",(void*)this); - } - Template *loadByName(const QCString &fileName,int line) - { - //for (int i=0;i<m_indent;i++) printf(" "); - //m_indent++; - //printf("loadByName(%s,%d) {\n",qPrint(fileName),line); - m_includeStack.emplace_back(IncludeEntry::Template,fileName,QCString(),line); - auto kv = m_templateCache.find(fileName.str()); - if (kv==m_templateCache.end()) // first time template is referenced - { - QCString filePath = m_templateDirName+"/"+fileName; - std::ifstream f(filePath.str(),std::ifstream::in | std::ifstream::binary); - if (f.is_open()) // read template from disk - { - FileInfo fi(filePath.str()); - size_t size=fi.size(); - QCString data(size+1); - f.read(data.rawData(),size); - if (!f.fail()) - { - kv = m_templateCache.insert( - std::make_pair(fileName.str(), - std::make_unique<TemplateImpl>(m_engine,filePath,data,m_extension))).first; - } - } - else // fallback to default built-in template - { - const QCString data = ResourceMgr::instance().getAsString(fileName); - if (!data.isEmpty()) - { - kv = m_templateCache.insert( - std::make_pair(fileName.str(), - std::make_unique<TemplateImpl>(m_engine,fileName,data,m_extension))).first; - } - else - { - err("Could not open template file %s\n",qPrint(fileName)); - } - } - } - return kv!=m_templateCache.end() ? kv->second.get() : 0; - } - - void unload(Template * /*t*/) - { - //(void)t; - //m_indent--; - //for (int i=0;i<m_indent;i++) printf(" "); - //printf("}\n"); - m_includeStack.pop_back(); - } - - void enterBlock(const QCString &fileName,const QCString &blockName,int line) - { - //for (int i=0;i<m_indent;i++) printf(" "); - //m_indent++; - //printf("enterBlock(%s,%s,%d) {\n",qPrint(fileName),qPrint(blockName),line); - m_includeStack.emplace_back(IncludeEntry::Block,fileName,blockName,line); - } - - void leaveBlock() - { - //m_indent--; - //for (int i=0;i<m_indent;i++) printf(" "); - //printf("}\n"); - m_includeStack.pop_back(); - } - - void printIncludeContext(const QCString &fileName,int line) const - { - auto it = m_includeStack.rbegin(); - while (it!=m_includeStack.rend()) - { - const IncludeEntry &ie = *it; - ++it; - const IncludeEntry *next = it!=m_includeStack.rend() ? &(*it) : 0; - if (ie.type()==IncludeEntry::Template) - { - if (next) - { - warn(fileName,line," inside template '%s' included from template '%s' at line %d",qPrint(ie.fileName()),qPrint(next->fileName()),ie.line()); - } - } - else // ie.type()==IncludeEntry::Block - { - warn(fileName,line," included by block '%s' inside template '%s' at line %d",qPrint(ie.blockName()), - qPrint(ie.fileName()),ie.line()); - } - } - } - - void setOutputExtension(const QCString &extension) - { - m_extension = extension; - } - - QCString outputExtension() const - { - return m_extension; - } - - void setTemplateDir(const QCString &dirName) - { - m_templateDirName = dirName; - } - - private: - std::unordered_map< std::string, std::unique_ptr<Template> > m_templateCache; - //mutable int m_indent; - TemplateEngine *m_engine = 0; - std::vector<IncludeEntry> m_includeStack; - QCString m_extension; - QCString m_templateDirName; -}; - -TemplateEngine::TemplateEngine() : p(std::make_unique<Private>(this)) -{ -} - -TemplateEngine::~TemplateEngine() -{ -} - -std::unique_ptr<TemplateContext> TemplateEngine::createContext() const -{ - return std::make_unique<TemplateContextImpl>(this); -} - -Template *TemplateEngine::loadByName(const QCString &fileName,int line) -{ - return p->loadByName(fileName,line); -} - -void TemplateEngine::unload(Template *t) -{ - p->unload(t); -} - -void TemplateEngine::enterBlock(const QCString &fileName,const QCString &blockName,int line) -{ - p->enterBlock(fileName,blockName,line); -} - -void TemplateEngine::leaveBlock() -{ - p->leaveBlock(); -} - -void TemplateEngine::printIncludeContext(const QCString &fileName,int line) const -{ - p->printIncludeContext(fileName,line); -} - -void TemplateEngine::setOutputExtension(const QCString &extension) -{ - p->setOutputExtension(extension); -} - -QCString TemplateEngine::outputExtension() const -{ - return p->outputExtension(); -} - -void TemplateEngine::setTemplateDir(const QCString &dirName) -{ - p->setTemplateDir(dirName); -} - -//----------------------------------------------------------------------------------------- - -QCString TemplateVariant::listToString() const -{ - QCString result="["; - const TemplateListIntfPtr list = toList(); - if (list) - { - bool first=true; - TemplateVariant ve; - TemplateListIntf::ConstIteratorPtr it = list->createIterator(); - for (it->toFirst();it->current(ve);it->toNext()) - { - if (!first) result+=",\n"; - result+="'"+ve.toString()+"'"; - first=false; - } - } - result+="]"; - return result; -} - -QCString TemplateVariant::structToString() const -{ - QCString result="{"; - const TemplateStructIntfPtr strukt = toStruct(); - if (strukt) - { - bool first=true; - for (const auto &s : strukt->fields()) - { - if (!first) result+=","; - result+=s; - if (!isWeakStruct()) // avoid endless recursion - { - result+=":'"; - result+=strukt->get(QCString(s)).toString(); - result+="'"; - } - first=false; - } - } - result+="}"; - return result; -} - |