diff --git a/src/template.cpp b/src/template.cpp index 16d1c98c612..2590486135a 100644 --- a/src/template.cpp +++ b/src/template.cpp @@ -305,12 +305,12 @@ int TemplateVariant::toInt() const return result; } -const TemplateStructIntf *TemplateVariant::toStruct() const +TemplateStructIntf *TemplateVariant::toStruct() const { return p->type==Struct ? p->strukt : 0; } -const TemplateListIntf *TemplateVariant::toList() const +TemplateListIntf *TemplateVariant::toList() const { return p->type==List ? p->list : 0; } @@ -624,6 +624,14 @@ class TemplateBlockContext QDict< QList > 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 */ @@ -650,25 +658,28 @@ class TemplateContextImpl : public TemplateContext { TemplateEscapeIntf **ppIntf = m_escapeIntfDict.find(ext); m_activeEscapeIntf = ppIntf ? *ppIntf : 0; } - void setActiveEscapeIntf(TemplateEscapeIntf *intf) - { m_activeEscapeIntf = intf; } - void setSpacelessIntf(TemplateSpacelessIntf *intf) - { m_spacelessIntf = intf; } + void setActiveEscapeIntf(TemplateEscapeIntf *intf) { m_activeEscapeIntf = intf; } + void setSpacelessIntf(TemplateSpacelessIntf *intf) { m_spacelessIntf = intf; } // internal methods TemplateBlockContext *blockContext(); TemplateVariant getPrimary(const QCString &name) const; void setLocation(const QCString &templateName,int line) { m_templateName=templateName; m_line=line; } - QCString templateName() const { return m_templateName; } - int line() const { return m_line; } - QCString outputDirectory() const { return m_outputDir; } - TemplateEscapeIntf *escapeIntf() const { return m_activeEscapeIntf; } + QCString templateName() const { return m_templateName; } + int line() const { return m_line; } + QCString outputDirectory() const { return m_outputDir; } + TemplateEscapeIntf *escapeIntf() const { return m_activeEscapeIntf; } TemplateSpacelessIntf *spacelessIntf() const { return m_spacelessIntf; } - void enableSpaceless(bool b) { m_spacelessEnabled=b; } - bool spacelessEnabled() const { return m_spacelessEnabled && m_spacelessIntf; } + void enableSpaceless(bool b) { m_spacelessEnabled=b; } + bool spacelessEnabled() const { return m_spacelessEnabled && m_spacelessIntf; } void warn(const char *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 QValueList &arguments); + private: const TemplateEngine *m_engine; QCString m_templateName; @@ -680,6 +691,8 @@ class TemplateContextImpl : public TemplateContext TemplateEscapeIntf *m_activeEscapeIntf; TemplateSpacelessIntf *m_spacelessIntf; bool m_spacelessEnabled; + TemplateAutoRef m_indices; + QDict< QStack > m_indexStacks; }; //----------------------------------------------------------------------------- @@ -1831,11 +1844,13 @@ class TemplateImpl : public TemplateNode, public Template TemplateContextImpl::TemplateContextImpl(const TemplateEngine *e) : m_engine(e), m_templateName(""), m_line(1), m_activeEscapeIntf(0), - m_spacelessIntf(0), m_spacelessEnabled(FALSE) + m_spacelessIntf(0), m_spacelessEnabled(FALSE), m_indices(TemplateStruct::alloc()) { + m_indexStacks.setAutoDelete(TRUE); m_contextStack.setAutoDelete(TRUE); m_escapeIntfDict.setAutoDelete(TRUE); push(); + set("index",m_indices.get()); } TemplateContextImpl::~TemplateContextImpl() @@ -1969,6 +1984,102 @@ void TemplateContextImpl::warn(const char *fileName,int line,const char *fmt,... m_engine->printIncludeContext(fileName,line); } +void TemplateContextImpl::openSubIndex(const QCString &indexName) +{ + //printf("TemplateContextImpl::openSubIndex(%s)\n",indexName.data()); + QStack *stack = m_indexStacks.find(indexName); + if (!stack || stack->isEmpty() || stack->top()->type()==TemplateVariant::List) // error: no stack yet or no entry + { + warn(m_templateName,m_line,"opensubindex for index %s without preceding indexentry",indexName.data()); + return; + } + // get the parent entry to add the list to + TemplateStruct *entry = dynamic_cast(stack->top()->toStruct()); + if (entry) + { + // add new list to the stack + TemplateList *list = TemplateList::alloc(); + stack->push(new TemplateVariant(list)); + entry->set("children",list); + entry->set("is_leaf_node",false); + } +} + +void TemplateContextImpl::closeSubIndex(const QCString &indexName) +{ + //printf("TemplateContextImpl::closeSubIndex(%s)\n",indexName.data()); + QStack *stack = m_indexStacks.find(indexName); + if (!stack || stack->count()<3) + { + warn(m_templateName,m_line,"closesubindex for index %s without matching open",indexName.data()); + } + else // stack->count()>=2 + { + if (stack->top()->type()==TemplateVariant::Struct) + { + delete stack->pop(); // pop struct + delete stack->pop(); // pop list + } + else // empty list! correct "is_left_node" attribute of the parent entry + { + delete stack->pop(); // pop list + TemplateStruct *entry = dynamic_cast(stack->top()->toStruct()); + if (entry) + { + entry->set("is_leaf_node",true); + } + } + } +} + +void TemplateContextImpl::addIndexEntry(const QCString &indexName,const QValueList &arguments) +{ + QValueListConstIterator it = arguments.begin(); + //printf("TemplateContextImpl::addIndexEntry(%s)\n",indexName.data()); + //while (it!=arguments.end()) + //{ + // printf(" key=%s value=%s\n",(*it).key.data(),(*it).value.toString().data()); + // ++it; + //} + QStack *stack = m_indexStacks.find(indexName); + if (!stack) // no stack yet, create it! + { + stack = new QStack; + stack->setAutoDelete(TRUE); + m_indexStacks.insert(indexName,stack); + } + TemplateList *list = 0; + if (stack->isEmpty()) // first item, create empty list and add it to the index + { + list = TemplateList::alloc(); + stack->push(new TemplateVariant(list)); + m_indices->set(indexName,list); // make list available under index + } + else // stack not empty + { + if (stack->top()->type()==TemplateVariant::Struct) // already an entry in the list + { + // remove current entry from the stack + delete stack->pop(); + } + else // first entry after opensubindex + { + ASSERT(stack->top()->type()==TemplateVariant::List); + } + // get list to add new item + list = dynamic_cast(stack->top()->toList()); + } + TemplateStruct *entry = TemplateStruct::alloc(); + // add user specified fields to the entry + for (it=arguments.begin();it!=arguments.end();++it) + { + entry->set((*it).key,(*it).value); + } + entry->set("is_leaf_node",true); + stack->push(new TemplateVariant(entry)); + list->append(entry); +} + //---------------------------------------------------------- /** @brief Class representing a piece of plain text in a template */ @@ -3020,6 +3131,148 @@ class TemplateNodeTree : public TemplateNodeCreator TemplateNodeList m_treeNodes; }; +//---------------------------------------------------------- + +/** @brief Class representing an 'indexentry' tag in a template */ +class TemplateNodeIndexEntry : public TemplateNodeCreator +{ + struct Mapping + { + Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {} + ~Mapping() { delete value; } + QCString name; + ExprAst *value; + }; + public: + TemplateNodeIndexEntry(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeIndexEntry(%s)\n",data.data())); + m_args.setAutoDelete(TRUE); + ExpressionParser expParser(parser,line); + QValueList args = split(data," "); + QValueListIterator 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) + { + ExprAst *expr = expParser.parse(arg.mid(j+1)); + if (expr) + { + m_args.append(new Mapping(arg.left(j),expr)); + } + } + else + { + parser->warn(parser->templateName(),line,"invalid argument '%s' for indexentry tag",arg.data()); + } + ++it; + } + } + TRACE(("}TemplateNodeIndexEntry(%s)\n",data.data())); + } + void render(FTextStream &, TemplateContext *c) + { + if (!m_name.isEmpty()) + { + TemplateContextImpl *ci = dynamic_cast(c); + ci->setLocation(m_templateName,m_line); + QListIterator it(m_args); + Mapping *mapping; + QValueList list; + for (it.toFirst();(mapping=it.current());++it) + { + list.append(TemplateKeyValue(mapping->name,mapping->value->resolve(c))); + } + ci->addIndexEntry(m_name,list); + } + } + private: + QCString m_name; + QList m_args; +}; + +//---------------------------------------------------------- + +/** @brief Class representing an 'opensubindex' tag in a template */ +class TemplateNodeOpenSubIndex : public TemplateNodeCreator +{ + public: + TemplateNodeOpenSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeOpenSubIndex(%s)\n",data.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'",data.data()); + m_name=""; + } + TRACE(("}TemplateNodeOpenSubIndex(%s)\n",data.data())); + } + void render(FTextStream &, TemplateContext *c) + { + if (!m_name.isEmpty()) + { + TemplateContextImpl *ci = dynamic_cast(c); + 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 +{ + public: + TemplateNodeCloseSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) + : TemplateNodeCreator(parser,parent,line) + { + TRACE(("{TemplateNodeCloseSubIndex(%s)\n",data.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'",data.data()); + m_name=""; + } + TRACE(("}TemplateNodeCloseSubIndex(%s)\n",data.data())); + } + void render(FTextStream &, TemplateContext *c) + { + if (!m_name.isEmpty()) + { + TemplateContextImpl *ci = dynamic_cast(c); + ci->setLocation(m_templateName,m_line); + ci->closeSubIndex(m_name); + } + } + private: + QCString m_name; +}; + + //---------------------------------------------------------- /** @brief Class representing an 'with' tag in a template */ @@ -3395,6 +3648,9 @@ static TemplateNodeFactory::AutoRegister autoRefRepeat("r static TemplateNodeFactory::AutoRegister autoRefInclude("include"); static TemplateNodeFactory::AutoRegister autoRefMarkers("markers"); static TemplateNodeFactory::AutoRegister autoRefSpaceless("spaceless"); +static TemplateNodeFactory::AutoRegister autoRefIndexEntry("indexentry"); +static TemplateNodeFactory::AutoRegister autoRefOpenSubIndex("opensubindex"); +static TemplateNodeFactory::AutoRegister autoRefCloseSubIndex("closesubindex"); //---------------------------------------------------------- diff --git a/src/template.h b/src/template.h index c227530cdd9..cb4a96f1c90 100644 --- a/src/template.h +++ b/src/template.h @@ -191,12 +191,12 @@ class TemplateVariant /** Returns the pointer to list referenced by this variant * or 0 if this variant does not have list type. */ - const TemplateListIntf *toList() const; + TemplateListIntf *toList() const; /** Returns the pointer to struct referenced by this variant * or 0 if this variant does not have struct type. */ - const TemplateStructIntf *toStruct() const; + TemplateStructIntf *toStruct() const; /** Return the result of apply this function with \a args. * Returns an empty string if the variant type is not a function. @@ -399,7 +399,7 @@ class TemplateSpacelessIntf * A key is searched starting with the dictionary at the top of the stack * and searching downwards until it is found. The stack is used to create * local scopes. - * @note This object must be created by TemplateEngine + * @note This object must be created by TemplateEngine::createContext() */ class TemplateContext {