10 #include <qvaluelist.h>
16 #include "ftextstream.h"
20 #define ENABLE_TRACING 0
23 #define TRACE(x) printf x
30 //-------------------------------------------------------------------
32 static QValueList<QCString> split(const QCString &str,const QCString &sep,
33 bool allowEmptyEntries=FALSE,bool cleanup=TRUE)
35 QValueList<QCString> lst;
38 int i = str.find( sep, j );
42 if ( str.mid(j,i-j).length() > 0 )
46 lst.append(str.mid(j,i-j).stripWhiteSpace());
50 lst.append(str.mid(j,i-j));
53 else if (allowEmptyEntries)
61 int l = str.length() - 1;
62 if (str.mid(j,l-j+1).length()>0)
66 lst.append(str.mid(j,l-j+1).stripWhiteSpace());
70 lst.append(str.mid(j,l-j+1));
73 else if (allowEmptyEntries)
81 //----------------------------------------------------------------------------
84 static QCString replace(const char *s,char csrc,char cdst)
87 for (char *p=result.data();*p;p++)
89 if (*p==csrc) *p=cdst;
95 //- TemplateVariant implementation -------------------------------------------
97 /** @brief Private data of a template variant object */
98 class TemplateVariant::Private
101 Private() : raw(FALSE) {}
106 TemplateStructIntf *strukt;
107 TemplateListIntf *list;
112 TemplateVariant::TemplateVariant()
118 TemplateVariant::TemplateVariant(bool b)
125 TemplateVariant::TemplateVariant(int v)
132 TemplateVariant::TemplateVariant(const char *s,bool raw)
140 TemplateVariant::TemplateVariant(const QCString &s,bool raw)
148 TemplateVariant::TemplateVariant(TemplateStructIntf *s)
156 TemplateVariant::TemplateVariant(TemplateListIntf *l)
164 TemplateVariant::TemplateVariant(const TemplateVariant::Delegate &delegate)
168 p->delegate = delegate;
171 TemplateVariant::~TemplateVariant()
173 if (p->type==Struct) p->strukt->release();
174 else if (p->type==List) p->list->release();
178 TemplateVariant::TemplateVariant(const TemplateVariant &v)
186 case Bool: p->boolVal = v.p->boolVal; break;
187 case Integer: p->intVal = v.p->intVal; break;
188 case String: p->strVal = v.p->strVal; break;
189 case Struct: p->strukt = v.p->strukt; p->strukt->addRef(); break;
190 case List: p->list = v.p->list; p->list->addRef(); break;
191 case Function: p->delegate= v.p->delegate;break;
195 TemplateVariant &TemplateVariant::operator=(const TemplateVariant &v)
197 // assignment can change the type of the variable, so we have to be
198 // careful with reference counted content.
199 TemplateStructIntf *tmpStruct = p->type==Struct ? p->strukt : 0;
200 TemplateListIntf *tmpList = p->type==List ? p->list : 0;
201 Type tmpType = p->type;
208 case Bool: p->boolVal = v.p->boolVal; break;
209 case Integer: p->intVal = v.p->intVal; break;
210 case String: p->strVal = v.p->strVal; break;
211 case Struct: p->strukt = v.p->strukt; p->strukt->addRef(); break;
212 case List: p->list = v.p->list; p->list->addRef(); break;
213 case Function: p->delegate= v.p->delegate;break;
216 // release overwritten reference counted values
217 if (tmpType==Struct && tmpStruct) tmpStruct->release();
218 else if (tmpType==List && tmpList ) tmpList->release();
222 QCString TemplateVariant::toString() const
230 result=p->boolVal ? "true" : "false";
233 result=QCString().setNum(p->intVal);
251 bool TemplateVariant::toBool() const
262 result = p->intVal!=0;
265 result = !p->strVal.isEmpty(); // && p->strVal!="false" && p->strVal!="0";
271 result = p->list->count()!=0;
280 int TemplateVariant::toInt() const
288 result = p->boolVal ? 1 : 0;
294 result = p->strVal.toInt();
299 result = p->list->count();
308 TemplateStructIntf *TemplateVariant::toStruct() const
310 return p->type==Struct ? p->strukt : 0;
313 TemplateListIntf *TemplateVariant::toList() const
315 return p->type==List ? p->list : 0;
318 TemplateVariant TemplateVariant::call(const QValueList<TemplateVariant> &args)
320 if (p->type==Function) return p->delegate(args);
321 return TemplateVariant();
324 bool TemplateVariant::operator==(TemplateVariant &other)
330 if (p->type==TemplateVariant::List && other.p->type==TemplateVariant::List)
332 return p->list==other.p->list; // TODO: improve me
334 else if (p->type==TemplateVariant::Struct && other.p->type==TemplateVariant::Struct)
336 return p->strukt==other.p->strukt; // TODO: improve me
340 return toString()==other.toString();
344 TemplateVariant::Type TemplateVariant::type() const
349 bool TemplateVariant::isValid() const
351 return p->type!=None;
354 void TemplateVariant::setRaw(bool b)
359 bool TemplateVariant::raw() const
364 //- Template struct implementation --------------------------------------------
367 /** @brief Private data of a template struct object */
368 class TemplateStruct::Private
371 Private() : fields(17), refCount(0)
372 { fields.setAutoDelete(TRUE); }
373 QDict<TemplateVariant> fields;
377 TemplateStruct::TemplateStruct()
382 TemplateStruct::~TemplateStruct()
387 int TemplateStruct::addRef()
389 return ++p->refCount;
392 int TemplateStruct::release()
394 int count = --p->refCount;
402 void TemplateStruct::set(const char *name,const TemplateVariant &v)
404 TemplateVariant *pv = p->fields.find(name);
405 if (pv) // change existing field
409 else // insert new field
411 p->fields.insert(name,new TemplateVariant(v));
415 TemplateVariant TemplateStruct::get(const char *name) const
417 TemplateVariant *v = p->fields.find(name);
418 return v ? *v : TemplateVariant();
421 TemplateStruct *TemplateStruct::alloc()
423 return new TemplateStruct;
426 //- Template list implementation ----------------------------------------------
429 /** @brief Private data of a template list object */
430 class TemplateList::Private
433 Private() : index(-1), refCount(0) {}
434 QValueList<TemplateVariant> elems;
440 TemplateList::TemplateList()
445 TemplateList::~TemplateList()
450 int TemplateList::addRef()
452 return ++p->refCount;
455 int TemplateList::release()
457 int count = --p->refCount;
465 int TemplateList::count() const
467 return p->elems.count();
470 void TemplateList::append(const TemplateVariant &v)
476 class TemplateListConstIterator : public TemplateListIntf::ConstIterator
479 TemplateListConstIterator(const TemplateList &l) : m_list(l) { m_index=-1; }
480 virtual ~TemplateListConstIterator() {}
481 virtual void toFirst()
483 m_it = m_list.p->elems.begin();
486 virtual void toLast()
488 m_it = m_list.p->elems.fromLast();
489 m_index=m_list.count()-1;
491 virtual void toNext()
493 if (m_it!=m_list.p->elems.end())
499 virtual void toPrev()
511 virtual bool current(TemplateVariant &v) const
513 if (m_index<0 || m_it==m_list.p->elems.end())
515 v = TemplateVariant();
525 const TemplateList &m_list;
526 QValueList<TemplateVariant>::ConstIterator m_it;
530 TemplateListIntf::ConstIterator *TemplateList::createIterator() const
532 return new TemplateListConstIterator(*this);
535 TemplateVariant TemplateList::at(int index) const
537 if (index>=0 && index<(int)p->elems.count())
539 return p->elems[index];
543 return TemplateVariant();
547 TemplateList *TemplateList::alloc()
549 return new TemplateList;
552 //- Operator types ------------------------------------------------------------
554 /** @brief Class representing operators that can appear in template expressions */
558 /* Operator precedence (low to high)
572 Or, And, Not, In, Equal, NotEqual, Less, Greater, LessEqual,
573 GreaterEqual, Plus, Minus, Multiply, Divide, Modulo, Filter, Colon, Comma,
574 LeftParen, RightParen,
578 static const char *toString(Type op)
582 case Or: return "or";
583 case And: return "and";
584 case Not: return "not";
585 case In: return "in";
586 case Equal: return "==";
587 case NotEqual: return "!=";
588 case Less: return "<";
589 case Greater: return ">";
590 case LessEqual: return "<=";
591 case GreaterEqual: return ">=";
592 case Plus: return "+";
593 case Minus: return "-";
594 case Multiply: return "*";
595 case Divide: return "/";
596 case Modulo: return "%";
597 case Filter: return "|";
598 case Colon: return ":";
599 case Comma: return ",";
600 case LeftParen: return "(";
601 case RightParen: return ")";
602 case Last: return "?";
608 //-----------------------------------------------------------------------------
610 class TemplateNodeBlock;
612 /** @brief Class holding stacks of blocks available in the context */
613 class TemplateBlockContext
616 TemplateBlockContext();
617 TemplateNodeBlock *get(const QCString &name) const;
618 TemplateNodeBlock *pop(const QCString &name) const;
619 void add(TemplateNodeBlock *block);
620 void add(TemplateBlockContext *ctx);
621 void push(TemplateNodeBlock *block);
624 QDict< QList<TemplateNodeBlock> > m_blocks;
627 /** @brief A container to store a key-value pair */
628 struct TemplateKeyValue
630 TemplateKeyValue() {}
631 TemplateKeyValue(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
633 TemplateVariant value;
636 /** @brief Internal class representing the implementation of a template
638 class TemplateContextImpl : public TemplateContext
641 TemplateContextImpl(const TemplateEngine *e);
642 virtual ~TemplateContextImpl();
644 // TemplateContext methods
647 void set(const char *name,const TemplateVariant &v);
648 TemplateVariant get(const QCString &name) const;
649 const TemplateVariant *getRef(const QCString &name) const;
650 void setOutputDirectory(const QCString &dir)
651 { m_outputDir = dir; }
652 void setEscapeIntf(const QCString &ext,TemplateEscapeIntf *intf)
654 int i=(!ext.isEmpty() && ext.at(0)=='.') ? 1 : 0;
655 m_escapeIntfDict.insert(ext.mid(i),new TemplateEscapeIntf*(intf));
657 void selectEscapeIntf(const QCString &ext)
658 { TemplateEscapeIntf **ppIntf = m_escapeIntfDict.find(ext);
659 m_activeEscapeIntf = ppIntf ? *ppIntf : 0;
661 void setActiveEscapeIntf(TemplateEscapeIntf *intf) { m_activeEscapeIntf = intf; }
662 void setSpacelessIntf(TemplateSpacelessIntf *intf) { m_spacelessIntf = intf; }
665 TemplateBlockContext *blockContext();
666 TemplateVariant getPrimary(const QCString &name) const;
667 void setLocation(const QCString &templateName,int line)
668 { m_templateName=templateName; m_line=line; }
669 QCString templateName() const { return m_templateName; }
670 int line() const { return m_line; }
671 QCString outputDirectory() const { return m_outputDir; }
672 TemplateEscapeIntf *escapeIntf() const { return m_activeEscapeIntf; }
673 TemplateSpacelessIntf *spacelessIntf() const { return m_spacelessIntf; }
674 void enableSpaceless(bool b) { m_spacelessEnabled=b; }
675 bool spacelessEnabled() const { return m_spacelessEnabled && m_spacelessIntf; }
676 void warn(const char *fileName,int line,const char *fmt,...) const;
678 // index related functions
679 void openSubIndex(const QCString &indexName);
680 void closeSubIndex(const QCString &indexName);
681 void addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments);
684 const TemplateEngine *m_engine;
685 QCString m_templateName;
687 QCString m_outputDir;
688 QList< QDict<TemplateVariant> > m_contextStack;
689 TemplateBlockContext m_blockContext;
690 QDict<TemplateEscapeIntf*> m_escapeIntfDict;
691 TemplateEscapeIntf *m_activeEscapeIntf;
692 TemplateSpacelessIntf *m_spacelessIntf;
693 bool m_spacelessEnabled;
694 TemplateAutoRef<TemplateStruct> m_indices;
695 QDict< QStack<TemplateVariant> > m_indexStacks;
698 //-----------------------------------------------------------------------------
700 /** @brief The implementation of the "add" filter */
704 static int variantIntValue(const TemplateVariant &v,bool &isInt)
706 isInt = v.type()==TemplateVariant::Integer;
707 if (!isInt && v.type()==TemplateVariant::String)
709 return v.toString().toInt(&isInt);
711 return isInt ? v.toInt() : 0;
713 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
720 int lhsValue = variantIntValue(v,lhsIsInt);
722 int rhsValue = variantIntValue(arg,rhsIsInt);
723 if (lhsIsInt && rhsIsInt)
725 return lhsValue+rhsValue;
727 else if (v.type()==TemplateVariant::String && arg.type()==TemplateVariant::String)
729 return TemplateVariant(v.toString() + arg.toString());
738 //-----------------------------------------------------------------------------
740 /** @brief The implementation of the "prepend" filter */
744 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
746 if (v.type()==TemplateVariant::String && arg.type()==TemplateVariant::String)
748 return TemplateVariant(arg.toString() + v.toString());
757 //--------------------------------------------------------------------
759 /** @brief The implementation of the "length" filter */
763 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
767 return TemplateVariant();
769 if (v.type()==TemplateVariant::List)
771 return TemplateVariant(v.toList()->count());
773 else if (v.type()==TemplateVariant::String)
775 return TemplateVariant((int)v.toString().length());
779 return TemplateVariant();
784 //--------------------------------------------------------------------
786 /** @brief The implementation of the "default" filter */
790 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
796 else if (v.type()==TemplateVariant::String && v.toString().isEmpty())
807 //--------------------------------------------------------------------
809 /** @brief The implementation of the "default" filter */
810 class FilterStripPath
813 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
815 if (!v.isValid() || v.type()!=TemplateVariant::String)
819 QCString result = v.toString();
820 int i=result.findRev('/');
823 result=result.mid(i+1);
825 i=result.findRev('\\');
828 result=result.mid(i+1);
834 //--------------------------------------------------------------------
836 /** @brief The implementation of the "default" filter */
840 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
842 if (!v.isValid() || v.type()!=TemplateVariant::String)
846 QCString s = v.toString();
847 return substitute(s," "," ");
851 //--------------------------------------------------------------------
853 /** @brief The implementation of the "divisibleby" filter */
854 class FilterDivisibleBy
857 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &n)
859 if (!v.isValid() || !n.isValid())
861 return TemplateVariant();
863 if (v.type()==TemplateVariant::Integer && n.type()==TemplateVariant::Integer)
865 return TemplateVariant((v.toInt()%n.toInt())==0);
869 return TemplateVariant();
875 //--------------------------------------------------------------------
877 /** @brief Factory singleton for registering and creating filters */
878 class TemplateFilterFactory
881 typedef TemplateVariant (FilterFunction)(const TemplateVariant &v,const TemplateVariant &arg);
883 static TemplateFilterFactory *instance()
885 static TemplateFilterFactory *instance = 0;
886 if (instance==0) instance = new TemplateFilterFactory;
890 TemplateVariant apply(const QCString &name,const TemplateVariant &v,const TemplateVariant &arg, bool &ok)
892 FilterFunction *func = (FilterFunction*)m_registry.find(name);
896 return (*func)(v,arg);
905 void registerFilter(const QCString &name,FilterFunction *func)
907 m_registry.insert(name,(void*)func);
910 /** @brief Helper class for registering a filter function */
911 template<class T> class AutoRegister
914 AutoRegister<T>(const QCString &key)
916 TemplateFilterFactory::instance()->registerFilter(key,&T::apply);
921 QDict<void> m_registry;
924 // register a handlers for each filter we support
925 static TemplateFilterFactory::AutoRegister<FilterAdd> fAdd("add");
926 static TemplateFilterFactory::AutoRegister<FilterAdd> fAppend("append");
927 static TemplateFilterFactory::AutoRegister<FilterLength> fLength("length");
928 static TemplateFilterFactory::AutoRegister<FilterNoWrap> fNoWrap("nowrap");
929 static TemplateFilterFactory::AutoRegister<FilterDefault> fDefault("default");
930 static TemplateFilterFactory::AutoRegister<FilterPrepend> fPrepend("prepend");
931 static TemplateFilterFactory::AutoRegister<FilterStripPath> fStripPath("stripPath");
932 static TemplateFilterFactory::AutoRegister<FilterDivisibleBy> fDivisibleBy("divisibleby");
934 //--------------------------------------------------------------------
936 /** @brief Base class for all nodes in the abstract syntax tree of an
942 virtual ~ExprAst() {}
943 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(); }
946 /** @brief Class representing a number in the AST */
947 class ExprAstNumber : public ExprAst
950 ExprAstNumber(int num) : m_number(num)
951 { TRACE(("ExprAstNumber(%d)\n",num)); }
952 int number() const { return m_number; }
953 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_number); }
958 /** @brief Class representing a variable in the AST */
959 class ExprAstVariable : public ExprAst
962 ExprAstVariable(const char *name) : m_name(name)
963 { TRACE(("ExprAstVariable(%s)\n",name)); }
964 const QCString &name() const { return m_name; }
965 virtual TemplateVariant resolve(TemplateContext *c)
967 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
968 TemplateVariant v = c->get(m_name);
971 ci->warn(ci->templateName(),ci->line(),"undefined variable '%s' in expression",m_name.data());
979 class ExprAstFunctionVariable : public ExprAst
982 ExprAstFunctionVariable(ExprAst *var,const QList<ExprAst> &args)
983 : m_var(var), m_args(args)
984 { TRACE(("ExprAstFunctionVariable()\n"));
985 m_args.setAutoDelete(TRUE);
987 virtual TemplateVariant resolve(TemplateContext *c)
989 QValueList<TemplateVariant> args;
990 for (uint i=0;i<m_args.count();i++)
992 TemplateVariant v = m_args.at(i)->resolve(c);
995 TemplateVariant v = m_var->resolve(c);
996 if (v.type()==TemplateVariant::Function)
1004 QList<ExprAst> m_args;
1007 /** @brief Class representing a filter in the AST */
1008 class ExprAstFilter : public ExprAst
1011 ExprAstFilter(const char *name,ExprAst *arg) : m_name(name), m_arg(arg)
1012 { TRACE(("ExprAstFilter(%s)\n",name)); }
1013 ~ExprAstFilter() { delete m_arg; }
1014 const QCString &name() const { return m_name; }
1015 TemplateVariant apply(const TemplateVariant &v,TemplateContext *c)
1017 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1018 TRACE(("Applying filter '%s' to '%s' (type=%d)\n",m_name.data(),v.toString().data(),v.type()));
1019 TemplateVariant arg;
1020 if (m_arg) arg = m_arg->resolve(c);
1022 TemplateVariant result = TemplateFilterFactory::instance()->apply(m_name,v,arg,ok);
1025 ci->warn(ci->templateName(),ci->line(),"unknown filter '%s'",m_name.data());
1034 /** @brief Class representing a filter applied to an expression in the AST */
1035 class ExprAstFilterAppl : public ExprAst
1038 ExprAstFilterAppl(ExprAst *expr,ExprAstFilter *filter)
1039 : m_expr(expr), m_filter(filter)
1040 { TRACE(("ExprAstFilterAppl\n")); }
1041 ~ExprAstFilterAppl() { delete m_expr; delete m_filter; }
1042 virtual TemplateVariant resolve(TemplateContext *c)
1044 return m_filter->apply(m_expr->resolve(c),c);
1048 ExprAstFilter *m_filter;
1051 /** @brief Class representing a string literal in the AST */
1052 class ExprAstLiteral : public ExprAst
1055 ExprAstLiteral(const char *lit) : m_literal(lit)
1056 { TRACE(("ExprAstLiteral(%s)\n",lit)); }
1057 const QCString &literal() const { return m_literal; }
1058 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_literal); }
1063 /** @brief Class representing a negation (not) operator in the AST */
1064 class ExprAstNegate : public ExprAst
1067 ExprAstNegate(ExprAst *expr) : m_expr(expr)
1068 { TRACE(("ExprAstNegate\n")); }
1069 ~ExprAstNegate() { delete m_expr; }
1070 virtual TemplateVariant resolve(TemplateContext *c)
1071 { return TemplateVariant(!m_expr->resolve(c).toBool()); }
1076 class ExprAstUnary : public ExprAst
1079 ExprAstUnary(Operator::Type op,ExprAst *exp) : m_operator(op), m_exp(exp)
1080 { TRACE(("ExprAstUnary %s\n",Operator::toString(op))); }
1081 ~ExprAstUnary() { delete m_exp; }
1082 virtual TemplateVariant resolve(TemplateContext *c)
1084 TemplateVariant exp = m_exp->resolve(c);
1087 case Operator::Minus:
1088 return -exp.toInt();
1090 return TemplateVariant();
1094 Operator::Type m_operator;
1098 /** @brief Class representing a binary operator in the AST */
1099 class ExprAstBinary : public ExprAst
1102 ExprAstBinary(Operator::Type op,ExprAst *lhs,ExprAst *rhs)
1103 : m_operator(op), m_lhs(lhs), m_rhs(rhs)
1104 { TRACE(("ExprAstBinary %s\n",Operator::toString(op))); }
1105 ~ExprAstBinary() { delete m_lhs; delete m_rhs; }
1106 virtual TemplateVariant resolve(TemplateContext *c)
1108 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1109 TemplateVariant lhs = m_lhs->resolve(c);
1110 TemplateVariant rhs = m_rhs ? m_rhs->resolve(c) : TemplateVariant();
1114 return TemplateVariant(lhs.toBool() || rhs.toBool());
1116 return TemplateVariant(lhs.toBool() && rhs.toBool());
1117 case Operator::Equal:
1118 return TemplateVariant(lhs == rhs);
1119 case Operator::NotEqual:
1120 return TemplateVariant(!(lhs == rhs));
1121 case Operator::Less:
1122 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1124 return lhs.toString()<rhs.toString();
1128 return lhs.toInt()<rhs.toInt();
1130 case Operator::Greater:
1131 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1133 return !(lhs.toString()<rhs.toString());
1137 return lhs.toInt()>rhs.toInt();
1139 case Operator::LessEqual:
1140 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1142 return lhs.toString()==rhs.toString() || lhs.toString()<rhs.toString();
1146 return lhs.toInt()<=rhs.toInt();
1148 case Operator::GreaterEqual:
1149 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1151 return lhs.toString()==rhs.toString() || !(lhs.toString()<rhs.toString());
1155 return lhs.toInt()>=rhs.toInt();
1157 case Operator::Plus:
1159 return TemplateVariant(lhs.toInt() + rhs.toInt());
1161 case Operator::Minus:
1163 return TemplateVariant(lhs.toInt() - rhs.toInt());
1165 case Operator::Multiply:
1167 return TemplateVariant(lhs.toInt() * rhs.toInt());
1169 case Operator::Divide:
1171 int denom = rhs.toInt();
1174 return TemplateVariant(lhs.toInt() / denom);
1176 else // divide by zero
1178 ci->warn(ci->templateName(),ci->line(),"division by zero while evaluating expression is undefined");
1182 case Operator::Modulo:
1184 int denom = rhs.toInt();
1187 return TemplateVariant(lhs.toInt() % denom);
1191 ci->warn(ci->templateName(),ci->line(),"modulo zero while evaluating expression is undefined");
1196 return TemplateVariant();
1200 Operator::Type m_operator;
1205 //----------------------------------------------------------
1207 /** @brief Base class of all nodes in a template's AST */
1211 TemplateNode(TemplateNode *parent) : m_parent(parent) {}
1212 virtual ~TemplateNode() {}
1214 virtual void render(FTextStream &ts, TemplateContext *c) = 0;
1216 TemplateNode *parent() { return m_parent; }
1219 TemplateNode *m_parent;
1222 //----------------------------------------------------------
1224 /** @brief Parser for templates */
1225 class TemplateParser
1228 TemplateParser(const TemplateEngine *engine,
1229 const QCString &templateName,QList<TemplateToken> &tokens);
1230 void parse(TemplateNode *parent,int line,const QStrList &stopAt,
1231 QList<TemplateNode> &nodes);
1232 bool hasNextToken() const;
1233 TemplateToken *takeNextToken();
1234 void removeNextToken();
1235 void prependToken(const TemplateToken *token);
1236 const TemplateToken *currentToken() const;
1237 QCString templateName() const { return m_templateName; }
1238 void warn(const char *fileName,int line,const char *fmt,...) const;
1240 const TemplateEngine *m_engine;
1241 QCString m_templateName;
1242 QList<TemplateToken> &m_tokens;
1245 //--------------------------------------------------------------------
1247 /** @brief Recursive decent parser for Django style template expressions.
1249 class ExpressionParser
1252 ExpressionParser(const TemplateParser *parser,int line)
1253 : m_parser(parser), m_line(line), m_tokenStream(0)
1256 virtual ~ExpressionParser()
1260 ExprAst *parse(const char *expr)
1262 if (expr==0) return 0;
1263 m_tokenStream = expr;
1265 return parseExpression();
1270 /** @brief Class representing a token within an expression. */
1274 ExprToken() : type(Unknown), num(-1), op(Operator::Or)
1279 Unknown, Operator, Number, Identifier, Literal
1288 ExprAst *parseExpression()
1290 TRACE(("{parseExpression(%s)\n",m_tokenStream));
1291 ExprAst *result = parseOrExpression();
1292 TRACE(("}parseExpression(%s)\n",m_tokenStream));
1296 ExprAst *parseOrExpression()
1298 TRACE(("{parseOrExpression(%s)\n",m_tokenStream));
1299 ExprAst *lhs = parseAndExpression();
1302 while (m_curToken.type==ExprToken::Operator &&
1303 m_curToken.op==Operator::Or)
1306 ExprAst *rhs = parseAndExpression();
1307 lhs = new ExprAstBinary(Operator::Or,lhs,rhs);
1310 TRACE(("}parseOrExpression(%s)\n",m_tokenStream));
1314 ExprAst *parseAndExpression()
1316 TRACE(("{parseAndExpression(%s)\n",m_tokenStream));
1317 ExprAst *lhs = parseNotExpression();
1320 while (m_curToken.type==ExprToken::Operator &&
1321 m_curToken.op==Operator::And)
1324 ExprAst *rhs = parseNotExpression();
1325 lhs = new ExprAstBinary(Operator::And,lhs,rhs);
1328 TRACE(("}parseAndExpression(%s)\n",m_tokenStream));
1332 ExprAst *parseNotExpression()
1334 TRACE(("{parseNotExpression(%s)\n",m_tokenStream));
1336 if (m_curToken.type==ExprToken::Operator &&
1337 m_curToken.op==Operator::Not)
1340 ExprAst *expr = parseCompareExpression();
1343 warn(m_parser->templateName(),m_line,"argument missing for not operator");
1346 result = new ExprAstNegate(expr);
1350 result = parseCompareExpression();
1352 TRACE(("}parseNotExpression(%s)\n",m_tokenStream));
1356 ExprAst *parseCompareExpression()
1358 TRACE(("{parseCompareExpression(%s)\n",m_tokenStream));
1359 ExprAst *lhs = parseAdditiveExpression();
1362 Operator::Type op = m_curToken.op;
1363 if (m_curToken.type==ExprToken::Operator &&
1364 (op==Operator::Less ||
1365 op==Operator::Greater ||
1366 op==Operator::Equal ||
1367 op==Operator::NotEqual ||
1368 op==Operator::LessEqual ||
1369 op==Operator::GreaterEqual
1374 ExprAst *rhs = parseNotExpression();
1375 lhs = new ExprAstBinary(op,lhs,rhs);
1378 TRACE(("}parseCompareExpression(%s)\n",m_tokenStream));
1382 ExprAst *parseAdditiveExpression()
1384 TRACE(("{parseAdditiveExpression(%s)\n",m_tokenStream));
1385 ExprAst *lhs = parseMultiplicativeExpression();
1388 while (m_curToken.type==ExprToken::Operator &&
1389 (m_curToken.op==Operator::Plus || m_curToken.op==Operator::Minus))
1391 Operator::Type op = m_curToken.op;
1393 ExprAst *rhs = parseMultiplicativeExpression();
1394 lhs = new ExprAstBinary(op,lhs,rhs);
1397 TRACE(("}parseAdditiveExpression(%s)\n",m_tokenStream));
1401 ExprAst *parseMultiplicativeExpression()
1403 TRACE(("{parseMultiplicativeExpression(%s)\n",m_tokenStream));
1404 ExprAst *lhs = parseUnaryExpression();
1407 while (m_curToken.type==ExprToken::Operator &&
1408 (m_curToken.op==Operator::Multiply || m_curToken.op==Operator::Divide || m_curToken.op==Operator::Modulo))
1410 Operator::Type op = m_curToken.op;
1412 ExprAst *rhs = parseUnaryExpression();
1413 lhs = new ExprAstBinary(op,lhs,rhs);
1416 TRACE(("}parseMultiplicativeExpression(%s)\n",m_tokenStream));
1420 ExprAst *parseUnaryExpression()
1422 TRACE(("{parseUnaryExpression(%s)\n",m_tokenStream));
1424 if (m_curToken.type==ExprToken::Operator)
1426 if (m_curToken.op==Operator::Plus)
1429 result = parsePrimaryExpression();
1431 else if (m_curToken.op==Operator::Minus)
1434 ExprAst *rhs = parsePrimaryExpression();
1435 result = new ExprAstUnary(m_curToken.op,rhs);
1439 result = parsePrimaryExpression();
1444 result = parsePrimaryExpression();
1446 TRACE(("}parseUnaryExpression(%s)\n",m_tokenStream));
1450 ExprAst *parsePrimaryExpression()
1452 TRACE(("{parsePrimary(%s)\n",m_tokenStream));
1454 switch (m_curToken.type)
1456 case ExprToken::Number:
1457 result = parseNumber();
1459 case ExprToken::Identifier:
1460 result = parseFilteredVariable();
1462 case ExprToken::Literal:
1463 result = parseLiteral();
1465 case ExprToken::Operator:
1466 if (m_curToken.op==Operator::LeftParen)
1468 getNextToken(); // skip over opening bracket
1469 result = parseExpression();
1470 if (m_curToken.type!=ExprToken::Operator ||
1471 m_curToken.op!=Operator::RightParen)
1473 warn(m_parser->templateName(),m_line,"missing closing parenthesis");
1477 getNextToken(); // skip over closing bracket
1482 warn(m_parser->templateName(),m_line,"unexpected operator '%s' in expression",
1483 Operator::toString(m_curToken.op));
1487 warn(m_parser->templateName(),m_line,"unexpected token in expression");
1489 TRACE(("}parsePrimary(%s)\n",m_tokenStream));
1493 ExprAst *parseNumber()
1495 TRACE(("{parseNumber(%d)\n",m_curToken.num));
1496 ExprAst *num = new ExprAstNumber(m_curToken.num);
1498 TRACE(("}parseNumber()\n"));
1502 ExprAst *parseIdentifier()
1504 TRACE(("{parseIdentifier(%s)\n",m_curToken.id.data()));
1505 ExprAst *id = new ExprAstVariable(m_curToken.id);
1507 TRACE(("}parseIdentifier()\n"));
1511 ExprAst *parseLiteral()
1513 TRACE(("{parseLiteral(%s)\n",m_curToken.id.data()));
1514 ExprAst *lit = new ExprAstLiteral(m_curToken.id);
1516 TRACE(("}parseLiteral()\n"));
1520 ExprAst *parseIdentifierOptionalArgs()
1522 TRACE(("{parseIdentifierOptionalArgs(%s)\n",m_curToken.id.data()));
1523 ExprAst *expr = parseIdentifier();
1526 if (m_curToken.type==ExprToken::Operator &&
1527 m_curToken.op==Operator::Colon)
1530 ExprAst *argExpr = parsePrimaryExpression();
1531 QList<ExprAst> args;
1532 args.append(argExpr);
1533 while (m_curToken.type==ExprToken::Operator &&
1534 m_curToken.op==Operator::Comma)
1537 argExpr = parsePrimaryExpression();
1538 args.append(argExpr);
1540 expr = new ExprAstFunctionVariable(expr,args);
1543 TRACE(("}parseIdentifierOptionalArgs()\n"));
1547 ExprAst *parseFilteredVariable()
1549 TRACE(("{parseFilteredVariable()\n"));
1550 ExprAst *expr = parseIdentifierOptionalArgs();
1553 while (m_curToken.type==ExprToken::Operator &&
1554 m_curToken.op==Operator::Filter)
1557 ExprAstFilter *filter = parseFilter();
1559 expr = new ExprAstFilterAppl(expr,filter);
1562 TRACE(("}parseFilteredVariable()\n"));
1566 ExprAstFilter *parseFilter()
1568 TRACE(("{parseFilter(%s)\n",m_curToken.id.data()));
1569 QCString filterName = m_curToken.id;
1572 if (m_curToken.type==ExprToken::Operator &&
1573 m_curToken.op==Operator::Colon)
1576 argExpr = parsePrimaryExpression();
1578 ExprAstFilter *filter = new ExprAstFilter(filterName,argExpr);
1579 TRACE(("}parseFilter()\n"));
1586 const char *p = m_tokenStream;
1589 if (p==0 || *p=='\0') return FALSE;
1590 while (*p==' ') p++; // skip over spaces
1596 if (c=='=' && *(p+1)=='=') // equal
1598 m_curToken.op = Operator::Equal;
1603 if (c=='!' && *(p+1)=='=') // not equal
1605 m_curToken.op = Operator::NotEqual;
1610 if (c=='<' && *(p+1)=='=') // less or equal
1612 m_curToken.op = Operator::LessEqual;
1617 m_curToken.op = Operator::Less;
1622 if (c=='>' && *(p+1)=='=') // greater or equal
1624 m_curToken.op = Operator::GreaterEqual;
1629 m_curToken.op = Operator::Greater;
1634 m_curToken.op = Operator::LeftParen;
1638 m_curToken.op = Operator::RightParen;
1642 m_curToken.op = Operator::Filter;
1646 m_curToken.op = Operator::Plus;
1650 m_curToken.op = Operator::Minus;
1654 m_curToken.op = Operator::Multiply;
1658 m_curToken.op = Operator::Divide;
1662 m_curToken.op = Operator::Modulo;
1666 m_curToken.op = Operator::Colon;
1670 m_curToken.op = Operator::Comma;
1674 if (strncmp(p,"not ",4)==0)
1676 m_curToken.op = Operator::Not;
1681 if (strncmp(p,"and ",4)==0)
1683 m_curToken.op = Operator::And;
1688 if (strncmp(p,"or ",3)==0)
1690 m_curToken.op = Operator::Or;
1697 if (p!=q) // found an operator
1699 m_curToken.type = ExprToken::Operator;
1701 else // no token found yet
1703 if (c>='0' && c<='9') // number?
1705 m_curToken.type = ExprToken::Number;
1708 while (*np>='0' && *np<='9')
1711 m_curToken.num+=*np-'0';
1716 else if (c=='_' || (c>='a' && c<='z') || (c>='A' && c<='Z')) // identifier?
1718 m_curToken.type = ExprToken::Identifier;
1723 (c=='_' || c=='.' ||
1724 (c>='a' && c<='z') ||
1725 (c>='A' && c<='Z') ||
1733 if (m_curToken.id=="True") // treat true literal as numerical 1
1735 m_curToken.type = ExprToken::Number;
1738 else if (m_curToken.id=="False") // treat false literal as numerical 0
1740 m_curToken.type = ExprToken::Number;
1744 else if (c=='"' || c=='\'') // string literal
1746 m_curToken.type = ExprToken::Literal;
1747 m_curToken.id.resize(0);
1751 while ((c=*p) && (c!=tokenChar || (c==tokenChar && cp=='\\')))
1754 if (c!='\\' || cp=='\\') // don't add escapes
1761 if (*p==tokenChar) p++;
1764 if (p==q) // still no valid token found -> error
1766 m_curToken.type = ExprToken::Unknown;
1770 warn(m_parser->templateName(),m_line,"Found unknown token %s while parsing %s",s,m_tokenStream);
1774 //TRACE(("token type=%d op=%d num=%d id=%s\n",
1775 // m_curToken.type,m_curToken.op,m_curToken.num,m_curToken.id.data()));
1781 const TemplateParser *m_parser;
1782 ExprToken m_curToken;
1784 const char *m_tokenStream;
1787 //----------------------------------------------------------
1789 /** @brief Class representing a lexical token in a template */
1793 enum Type { Text, Variable, Block };
1794 TemplateToken(Type t,const char *d,int l) : type(t), data(d), line(l) {}
1800 //----------------------------------------------------------
1802 /** @brief Class representing a list of AST nodes in a template */
1803 class TemplateNodeList : public QList<TemplateNode>
1808 setAutoDelete(TRUE);
1810 void render(FTextStream &ts,TemplateContext *c)
1812 TRACE(("{TemplateNodeList::render\n"));
1813 QListIterator<TemplateNode> it(*this);
1815 for (it.toFirst();(tn=it.current());++it)
1819 TRACE(("}TemplateNodeList::render\n"));
1823 //----------------------------------------------------------
1825 /** @brief Internal class representing the implementation of a template */
1826 class TemplateImpl : public TemplateNode, public Template
1829 TemplateImpl(TemplateEngine *e,const QCString &name,const QCString &data);
1830 void render(FTextStream &ts, TemplateContext *c);
1832 TemplateEngine *engine() const { return m_engine; }
1833 TemplateBlockContext *blockContext() { return &m_blockContext; }
1836 TemplateEngine *m_engine;
1838 TemplateNodeList m_nodes;
1839 TemplateBlockContext m_blockContext;
1842 //----------------------------------------------------------
1845 TemplateContextImpl::TemplateContextImpl(const TemplateEngine *e)
1846 : m_engine(e), m_templateName("<unknown>"), m_line(1), m_activeEscapeIntf(0),
1847 m_spacelessIntf(0), m_spacelessEnabled(FALSE), m_indices(TemplateStruct::alloc())
1849 m_indexStacks.setAutoDelete(TRUE);
1850 m_contextStack.setAutoDelete(TRUE);
1851 m_escapeIntfDict.setAutoDelete(TRUE);
1853 set("index",m_indices.get());
1856 TemplateContextImpl::~TemplateContextImpl()
1861 void TemplateContextImpl::set(const char *name,const TemplateVariant &v)
1863 TemplateVariant *pv = m_contextStack.getFirst()->find(name);
1866 m_contextStack.getFirst()->remove(name);
1868 m_contextStack.getFirst()->insert(name,new TemplateVariant(v));
1871 TemplateVariant TemplateContextImpl::get(const QCString &name) const
1873 int i=name.find('.');
1874 if (i==-1) // simple name
1876 return getPrimary(name);
1881 QCString objName = name.left(i);
1882 v = getPrimary(objName);
1883 QCString propName = name.mid(i+1);
1884 while (!propName.isEmpty())
1886 //printf("getPrimary(%s) type=%d:%s\n",objName.data(),v.type(),v.toString().data());
1887 if (v.type()==TemplateVariant::Struct)
1889 i = propName.find(".");
1890 int l = i==-1 ? propName.length() : i;
1891 v = v.toStruct()->get(propName.left(l));
1894 warn(m_templateName,m_line,"requesting non-existing property '%s' for object '%s'",propName.left(l).data(),objName.data());
1898 objName = propName.left(i);
1899 propName = propName.mid(i+1);
1906 else if (v.type()==TemplateVariant::List)
1908 i = propName.find(".");
1909 int l = i==-1 ? propName.length() : i;
1911 int index = propName.left(l).toInt(&b);
1914 v = v.toList()->at(index);
1918 warn(m_templateName,m_line,"list index '%s' is not valid",propName.data());
1923 propName = propName.mid(i+1);
1932 warn(m_templateName,m_line,"using . on an object '%s' is not an struct or list",objName.data());
1933 return TemplateVariant();
1940 const TemplateVariant *TemplateContextImpl::getRef(const QCString &name) const
1942 QListIterator< QDict<TemplateVariant> > it(m_contextStack);
1943 QDict<TemplateVariant> *dict;
1944 for (it.toFirst();(dict=it.current());++it)
1946 TemplateVariant *v = dict->find(name);
1949 return 0; // not found
1952 TemplateVariant TemplateContextImpl::getPrimary(const QCString &name) const
1954 const TemplateVariant *v = getRef(name);
1955 return v ? *v : TemplateVariant();
1958 void TemplateContextImpl::push()
1960 QDict<TemplateVariant> *dict = new QDict<TemplateVariant>;
1961 dict->setAutoDelete(TRUE);
1962 m_contextStack.prepend(dict);
1965 void TemplateContextImpl::pop()
1967 if (!m_contextStack.removeFirst())
1969 warn(m_templateName,m_line,"pop() called on empty context stack!\n");
1973 TemplateBlockContext *TemplateContextImpl::blockContext()
1975 return &m_blockContext;
1978 void TemplateContextImpl::warn(const char *fileName,int line,const char *fmt,...) const
1982 va_warn(fileName,line,fmt,args);
1984 m_engine->printIncludeContext(fileName,line);
1987 void TemplateContextImpl::openSubIndex(const QCString &indexName)
1989 //printf("TemplateContextImpl::openSubIndex(%s)\n",indexName.data());
1990 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
1991 if (!stack || stack->isEmpty() || stack->top()->type()==TemplateVariant::List) // error: no stack yet or no entry
1993 warn(m_templateName,m_line,"opensubindex for index %s without preceding indexentry",indexName.data());
1996 // get the parent entry to add the list to
1997 TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2000 // add new list to the stack
2001 TemplateList *list = TemplateList::alloc();
2002 stack->push(new TemplateVariant(list));
2003 entry->set("children",list);
2004 entry->set("is_leaf_node",false);
2008 void TemplateContextImpl::closeSubIndex(const QCString &indexName)
2010 //printf("TemplateContextImpl::closeSubIndex(%s)\n",indexName.data());
2011 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2012 if (!stack || stack->count()<3)
2014 warn(m_templateName,m_line,"closesubindex for index %s without matching open",indexName.data());
2016 else // stack->count()>=2
2018 if (stack->top()->type()==TemplateVariant::Struct)
2020 delete stack->pop(); // pop struct
2021 delete stack->pop(); // pop list
2023 else // empty list! correct "is_left_node" attribute of the parent entry
2025 delete stack->pop(); // pop list
2026 TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2029 entry->set("is_leaf_node",true);
2035 void TemplateContextImpl::addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments)
2037 QValueListConstIterator<TemplateKeyValue> it = arguments.begin();
2038 //printf("TemplateContextImpl::addIndexEntry(%s)\n",indexName.data());
2039 //while (it!=arguments.end())
2041 // printf(" key=%s value=%s\n",(*it).key.data(),(*it).value.toString().data());
2044 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2045 if (!stack) // no stack yet, create it!
2047 stack = new QStack<TemplateVariant>;
2048 stack->setAutoDelete(TRUE);
2049 m_indexStacks.insert(indexName,stack);
2051 TemplateList *list = 0;
2052 if (stack->isEmpty()) // first item, create empty list and add it to the index
2054 list = TemplateList::alloc();
2055 stack->push(new TemplateVariant(list));
2056 m_indices->set(indexName,list); // make list available under index
2058 else // stack not empty
2060 if (stack->top()->type()==TemplateVariant::Struct) // already an entry in the list
2062 // remove current entry from the stack
2063 delete stack->pop();
2065 else // first entry after opensubindex
2067 ASSERT(stack->top()->type()==TemplateVariant::List);
2069 // get list to add new item
2070 list = dynamic_cast<TemplateList*>(stack->top()->toList());
2072 TemplateStruct *entry = TemplateStruct::alloc();
2073 // add user specified fields to the entry
2074 for (it=arguments.begin();it!=arguments.end();++it)
2076 entry->set((*it).key,(*it).value);
2078 if (list->count()>0)
2080 TemplateStruct *lastEntry = dynamic_cast<TemplateStruct*>(list->at(list->count()-1).toStruct());
2081 lastEntry->set("last",false);
2083 entry->set("is_leaf_node",true);
2084 entry->set("first",list->count()==0);
2085 entry->set("last",true);
2086 stack->push(new TemplateVariant(entry));
2087 list->append(entry);
2090 //----------------------------------------------------------
2092 /** @brief Class representing a piece of plain text in a template */
2093 class TemplateNodeText : public TemplateNode
2096 TemplateNodeText(TemplateParser *,TemplateNode *parent,int,const QCString &data)
2097 : TemplateNode(parent), m_data(data)
2099 TRACE(("TemplateNodeText('%s')\n",replace(data,'\n',' ').data()));
2102 void render(FTextStream &ts, TemplateContext *c)
2104 //printf("TemplateNodeText::render(%s)\n",m_data.data());
2105 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2106 if (ci->spacelessEnabled())
2108 ts << ci->spacelessIntf()->remove(m_data);
2119 //----------------------------------------------------------
2121 /** @brief Class representing a variable in a template */
2122 class TemplateNodeVariable : public TemplateNode
2125 TemplateNodeVariable(TemplateParser *parser,TemplateNode *parent,int line,const QCString &var)
2126 : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line)
2128 TRACE(("TemplateNodeVariable(%s)\n",var.data()));
2129 ExpressionParser expParser(parser,line);
2130 m_var = expParser.parse(var);
2133 parser->warn(m_templateName,line,"invalid expression '%s' for variable",var.data());
2136 ~TemplateNodeVariable()
2141 void render(FTextStream &ts, TemplateContext *c)
2143 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2144 ci->setLocation(m_templateName,m_line);
2147 TemplateVariant v = m_var->resolve(c);
2148 if (v.type()==TemplateVariant::Function)
2150 v = v.call(QValueList<TemplateVariant>());
2152 //printf("TemplateNodeVariable::render(%s) raw=%d\n",value.data(),v.raw());
2153 if (ci->escapeIntf() && !v.raw())
2155 ts << ci->escapeIntf()->escape(v.toString());
2165 QCString m_templateName;
2168 QList<ExprAst> m_args;
2171 //----------------------------------------------------------
2173 /** @brief Helper class for creating template AST tag nodes and returning
2174 * the template for a given node.
2176 template<class T> class TemplateNodeCreator : public TemplateNode
2179 TemplateNodeCreator(TemplateParser *parser,TemplateNode *parent,int line)
2180 : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line) {}
2181 static TemplateNode *createInstance(TemplateParser *parser,
2182 TemplateNode *parent,
2184 const QCString &data)
2186 return new T(parser,parent,line,data);
2188 TemplateImpl *getTemplate()
2190 TemplateNode *root = this;
2191 while (root && root->parent())
2193 root = root->parent();
2195 return dynamic_cast<TemplateImpl*>(root);
2198 QCString m_templateName;
2202 //----------------------------------------------------------
2204 /** @brief Class representing an 'if' tag in a template */
2205 class TemplateNodeIf : public TemplateNodeCreator<TemplateNodeIf>
2208 TemplateNodeIf(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) :
2209 TemplateNodeCreator<TemplateNodeIf>(parser,parent,line)
2211 m_ifGuardedNodes.setAutoDelete(TRUE);
2212 TRACE(("{TemplateNodeIf(%s)\n",data.data()));
2215 parser->warn(m_templateName,line,"missing argument for if tag");
2218 stopAt.append("endif");
2219 stopAt.append("elif");
2220 stopAt.append("else");
2223 GuardedNodes *guardedNodes = new GuardedNodes;
2224 ExpressionParser ex(parser,line);
2225 guardedNodes->line = line;
2226 guardedNodes->guardAst = ex.parse(data);
2227 parser->parse(this,line,stopAt,guardedNodes->trueNodes);
2228 m_ifGuardedNodes.append(guardedNodes);
2229 TemplateToken *tok = parser->takeNextToken();
2232 while (tok && tok->data.left(5)=="elif ")
2234 ExpressionParser ex(parser,line);
2235 guardedNodes = new GuardedNodes;
2236 guardedNodes->line = tok->line;
2237 guardedNodes->guardAst = ex.parse(tok->data.mid(5));
2238 parser->parse(this,tok->line,stopAt,guardedNodes->trueNodes);
2239 m_ifGuardedNodes.append(guardedNodes);
2240 // proceed to the next token
2242 tok = parser->takeNextToken();
2246 if (tok && tok->data=="else")
2248 stopAt.removeLast(); // remove "else"
2249 stopAt.removeLast(); // remove "elif"
2250 parser->parse(this,line,stopAt,m_falseNodes);
2251 parser->removeNextToken(); // skip over endif
2254 TRACE(("}TemplateNodeIf(%s)\n",data.data()));
2260 void render(FTextStream &ts, TemplateContext *c)
2262 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2263 ci->setLocation(m_templateName,m_line);
2264 //printf("TemplateNodeIf::render #trueNodes=%d #falseNodes=%d\n",m_trueNodes.count(),m_falseNodes.count());
2265 bool processed=FALSE;
2266 QListIterator<GuardedNodes> li(m_ifGuardedNodes);
2267 GuardedNodes *nodes;
2268 for (li.toFirst();(nodes=li.current()) && !processed;++li)
2270 if (nodes->guardAst)
2272 TemplateVariant guardValue = nodes->guardAst->resolve(c);
2273 if (guardValue.toBool()) // render nodes for the first guard that evaluated to 'true'
2275 nodes->trueNodes.render(ts,c);
2281 ci->warn(m_templateName,nodes->line,"invalid expression for if/elif");
2286 // all guards are false, render 'else' nodes
2287 m_falseNodes.render(ts,c);
2293 GuardedNodes() : guardAst(0) {}
2294 ~GuardedNodes() { delete guardAst; }
2297 TemplateNodeList trueNodes;
2299 QList<GuardedNodes> m_ifGuardedNodes;
2300 TemplateNodeList m_falseNodes;
2303 //----------------------------------------------------------
2304 /** @brief Class representing a 'for' tag in a template */
2305 class TemplateNodeRepeat : public TemplateNodeCreator<TemplateNodeRepeat>
2308 TemplateNodeRepeat(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2309 : TemplateNodeCreator<TemplateNodeRepeat>(parser,parent,line)
2311 TRACE(("{TemplateNodeRepeat(%s)\n",data.data()));
2312 ExpressionParser expParser(parser,line);
2313 m_expr = expParser.parse(data);
2315 stopAt.append("endrepeat");
2316 parser->parse(this,line,stopAt,m_repeatNodes);
2317 parser->removeNextToken(); // skip over endrepeat
2318 TRACE(("}TemplateNodeRepeat(%s)\n",data.data()));
2320 ~TemplateNodeRepeat()
2324 void render(FTextStream &ts, TemplateContext *c)
2326 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2327 ci->setLocation(m_templateName,m_line);
2329 if (m_expr && (v=m_expr->resolve(c)).type()==TemplateVariant::Integer)
2331 int i, n = v.toInt();
2334 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2335 s->set("counter0", (int)i);
2336 s->set("counter", (int)(i+1));
2337 s->set("revcounter", (int)(n-i));
2338 s->set("revcounter0", (int)(n-i-1));
2339 s->set("first",i==0);
2340 s->set("last", i==n-1);
2341 c->set("repeatloop",s.get());
2342 // render all items for this iteration of the loop
2343 m_repeatNodes.render(ts,c);
2346 else // simple type...
2348 ci->warn(m_templateName,m_line,"for requires a variable of list type!");
2352 TemplateNodeList m_repeatNodes;
2356 //----------------------------------------------------------
2358 /** @brief Class representing a 'range' tag in a template */
2359 class TemplateNodeRange : public TemplateNodeCreator<TemplateNodeRange>
2362 TemplateNodeRange(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2363 : TemplateNodeCreator<TemplateNodeRange>(parser,parent,line)
2365 TRACE(("{TemplateNodeRange(%s)\n",data.data()));
2367 int i1 = data.find(" from ");
2368 int i2 = data.find(" to ");
2369 int i3 = data.find(" downto ");
2372 if (data.right(5)==" from")
2374 parser->warn(m_templateName,line,"range missing after 'from' keyword");
2376 else if (data=="from")
2378 parser->warn(m_templateName,line,"range needs an iterator variable and a range");
2382 parser->warn(m_templateName,line,"range is missing 'from' keyword");
2385 else if (i2==-1 && i3==-1)
2387 if (data.right(3)==" to")
2389 parser->warn(m_templateName,line,"range is missing end value after 'to' keyword");
2391 else if (data.right(7)==" downto")
2393 parser->warn(m_templateName,line,"range is missing end value after 'downto' keyword");
2397 parser->warn(m_templateName,line,"range is missing 'to' or 'downto' keyword");
2402 m_var = data.left(i1).stripWhiteSpace();
2403 if (m_var.isEmpty())
2405 parser->warn(m_templateName,line,"range needs an iterator variable");
2407 start = data.mid(i1+6,i2-i1-6).stripWhiteSpace();
2410 end = data.right(data.length()-i2-4).stripWhiteSpace();
2415 end = data.right(data.length()-i3-8).stripWhiteSpace();
2419 ExpressionParser expParser(parser,line);
2420 m_startExpr = expParser.parse(start);
2421 m_endExpr = expParser.parse(end);
2424 stopAt.append("endrange");
2425 parser->parse(this,line,stopAt,m_loopNodes);
2426 parser->removeNextToken(); // skip over endrange
2427 TRACE(("}TemplateNodeRange(%s)\n",data.data()));
2430 ~TemplateNodeRange()
2436 void render(FTextStream &ts, TemplateContext *c)
2438 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2439 ci->setLocation(m_templateName,m_line);
2440 //printf("TemplateNodeRange::render #loopNodes=%d\n",
2441 // m_loopNodes.count());
2442 if (m_startExpr && m_endExpr)
2444 TemplateVariant vs = m_startExpr->resolve(c);
2445 TemplateVariant ve = m_endExpr->resolve(c);
2446 if (vs.type()==TemplateVariant::Integer && ve.type()==TemplateVariant::Integer)
2450 int l = m_down ? s-e+1 : e-s+1;
2454 //int index = m_reversed ? list.count() : 0;
2456 const TemplateVariant *parentLoop = c->getRef("forloop");
2458 int i = m_down ? e : s;
2462 // set the forloop meta-data variable
2463 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2464 s->set("counter0", (int)index);
2465 s->set("counter", (int)(index+1));
2466 s->set("revcounter", (int)(l-index));
2467 s->set("revcounter0", (int)(l-index-1));
2468 s->set("first",index==0);
2469 s->set("last", (int)index==l-1);
2470 s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
2471 c->set("forloop",s.get());
2473 // set the iterator variable
2476 // render all items for this iteration of the loop
2477 m_loopNodes.render(ts,c);
2495 ci->warn(m_templateName,m_line,"range %d %s %d is empty!",
2496 s,m_down?"downto":"to",e);
2499 else if (vs.type()!=TemplateVariant::Integer)
2501 ci->warn(m_templateName,m_line,"range requires a start value of integer type!");
2503 else if (ve.type()!=TemplateVariant::Integer)
2505 ci->warn(m_templateName,m_line,"range requires an end value of integer type!");
2508 else if (!m_startExpr)
2510 ci->warn(m_templateName,m_line,"range has empty start value");
2512 else if (!m_endExpr)
2514 ci->warn(m_templateName,m_line,"range has empty end value");
2520 ExprAst *m_startExpr;
2523 TemplateNodeList m_loopNodes;
2526 //----------------------------------------------------------
2528 /** @brief Class representing a 'for' tag in a template */
2529 class TemplateNodeFor : public TemplateNodeCreator<TemplateNodeFor>
2532 TemplateNodeFor(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2533 : TemplateNodeCreator<TemplateNodeFor>(parser,parent,line)
2535 TRACE(("{TemplateNodeFor(%s)\n",data.data()));
2537 int i = data.find(" in ");
2540 if (data.right(3)==" in")
2542 parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
2544 else if (data=="in")
2546 parser->warn(m_templateName,line,"for needs at least one iterator variable");
2550 parser->warn(m_templateName,line,"for is missing 'in' keyword");
2555 m_vars = split(data.left(i),",");
2556 if (m_vars.count()==0)
2558 parser->warn(m_templateName,line,"for needs at least one iterator variable");
2561 int j = data.find(" reversed",i);
2562 m_reversed = (j!=-1);
2564 if (j==-1) j=data.length();
2567 exprStr = data.mid(i+4,j-i-4); // skip over " in " part
2569 if (exprStr.isEmpty())
2571 parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
2574 ExpressionParser expParser(parser,line);
2575 m_expr = expParser.parse(exprStr);
2578 stopAt.append("endfor");
2579 stopAt.append("empty");
2580 parser->parse(this,line,stopAt,m_loopNodes);
2581 TemplateToken *tok = parser->takeNextToken();
2582 if (tok && tok->data=="empty")
2584 stopAt.removeLast();
2585 parser->parse(this,line,stopAt,m_emptyNodes);
2586 parser->removeNextToken(); // skip over endfor
2589 TRACE(("}TemplateNodeFor(%s)\n",data.data()));
2597 void render(FTextStream &ts, TemplateContext *c)
2599 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2600 ci->setLocation(m_templateName,m_line);
2601 //printf("TemplateNodeFor::render #loopNodes=%d #emptyNodes=%d\n",
2602 // m_loopNodes.count(),m_emptyNodes.count());
2605 TemplateVariant v = m_expr->resolve(c);
2606 const TemplateListIntf *list = v.toList();
2609 uint listSize = list->count();
2610 if (listSize==0) // empty for loop
2612 m_emptyNodes.render(ts,c);
2616 //int index = m_reversed ? list.count() : 0;
2618 const TemplateVariant *parentLoop = c->getRef("forloop");
2619 uint index = m_reversed ? listSize-1 : 0;
2620 TemplateListIntf::ConstIterator *it = list->createIterator();
2621 for (m_reversed ? it->toLast() : it->toFirst();
2623 m_reversed ? it->toPrev() : it->toNext())
2625 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2626 s->set("counter0", (int)index);
2627 s->set("counter", (int)(index+1));
2628 s->set("revcounter", (int)(listSize-index));
2629 s->set("revcounter0", (int)(listSize-index-1));
2630 s->set("first",index==0);
2631 s->set("last", index==listSize-1);
2632 s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
2633 c->set("forloop",s.get());
2635 // add variables for this loop to the context
2636 //obj->addVariableToContext(index,m_vars,c);
2638 if (m_vars.count()==1) // loop variable represents an item
2640 c->set(m_vars[vi++],v);
2642 else if (m_vars.count()>1 && v.type()==TemplateVariant::Struct)
2643 // loop variables represent elements in a list item
2645 for (uint i=0;i<m_vars.count();i++,vi++)
2647 c->set(m_vars[vi],v.toStruct()->get(m_vars[vi]));
2650 for (;vi<m_vars.count();vi++)
2652 c->set(m_vars[vi],TemplateVariant());
2655 // render all items for this iteration of the loop
2656 m_loopNodes.render(ts,c);
2658 if (m_reversed) index--; else index++;
2663 else // simple type...
2665 ci->warn(m_templateName,m_line,"for requires a variable of list type!");
2673 QValueList<QCString> m_vars;
2674 TemplateNodeList m_loopNodes;
2675 TemplateNodeList m_emptyNodes;
2678 //----------------------------------------------------------
2680 /** @brief Class representing an 'markers' tag in a template */
2681 class TemplateNodeMsg : public TemplateNodeCreator<TemplateNodeMsg>
2684 TemplateNodeMsg(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
2685 : TemplateNodeCreator<TemplateNodeMsg>(parser,parent,line)
2687 TRACE(("{TemplateNodeMsg()\n"));
2689 stopAt.append("endmsg");
2690 parser->parse(this,line,stopAt,m_nodes);
2691 parser->removeNextToken(); // skip over endmarkers
2692 TRACE(("}TemplateNodeMsg()\n"));
2694 void render(FTextStream &, TemplateContext *c)
2696 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2697 ci->setLocation(m_templateName,m_line);
2698 TemplateEscapeIntf *escIntf = ci->escapeIntf();
2699 ci->setActiveEscapeIntf(0); // avoid escaping things we send to standard out
2700 bool enable = ci->spacelessEnabled();
2701 ci->enableSpaceless(FALSE);
2702 FTextStream ts(stdout);
2703 m_nodes.render(ts,c);
2705 ci->setActiveEscapeIntf(escIntf);
2706 ci->enableSpaceless(enable);
2709 TemplateNodeList m_nodes;
2713 //----------------------------------------------------------
2715 /** @brief Class representing a 'block' tag in a template */
2716 class TemplateNodeBlock : public TemplateNodeCreator<TemplateNodeBlock>
2719 TemplateNodeBlock(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2720 : TemplateNodeCreator<TemplateNodeBlock>(parser,parent,line)
2722 TRACE(("{TemplateNodeBlock(%s)\n",data.data()));
2724 if (m_blockName.isEmpty())
2726 parser->warn(parser->templateName(),line,"block tag without name");
2729 stopAt.append("endblock");
2730 parser->parse(this,line,stopAt,m_nodes);
2731 parser->removeNextToken(); // skip over endblock
2732 TRACE(("}TemplateNodeBlock(%s)\n",data.data()));
2735 void render(FTextStream &ts, TemplateContext *c)
2737 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
2738 ci->setLocation(m_templateName,m_line);
2739 TemplateImpl *t = getTemplate();
2742 // remove block from the context, so block.super can work
2743 TemplateNodeBlock *nb = ci->blockContext()->pop(m_blockName);
2744 if (nb) // block is overruled
2748 FTextStream ss(&super);
2749 // get super block of block nb
2750 TemplateNodeBlock *sb = ci->blockContext()->get(m_blockName);
2751 if (sb && sb!=nb && sb!=this) // nb and sb both overrule this block
2753 sb->render(ss,c); // render parent of nb to string
2755 else if (nb!=this) // only nb overrules this block
2757 m_nodes.render(ss,c); // render parent of nb to string
2759 // add 'block.super' variable to allow access to parent block content
2760 TemplateAutoRef<TemplateStruct> superBlock(TemplateStruct::alloc());
2761 superBlock->set("super",TemplateVariant(super.data(),TRUE));
2762 ci->set("block",superBlock.get());
2763 // render the overruled block contents
2764 t->engine()->enterBlock(nb->m_templateName,nb->m_blockName,nb->m_line);
2765 nb->m_nodes.render(ts,c);
2766 t->engine()->leaveBlock();
2768 // re-add block to the context
2769 ci->blockContext()->push(nb);
2771 else // block has no overrule
2773 t->engine()->enterBlock(m_templateName,m_blockName,m_line);
2774 m_nodes.render(ts,c);
2775 t->engine()->leaveBlock();
2780 QCString name() const
2786 QCString m_blockName;
2787 TemplateNodeList m_nodes;
2790 //----------------------------------------------------------
2792 /** @brief Class representing a 'extend' tag in a template */
2793 class TemplateNodeExtend : public TemplateNodeCreator<TemplateNodeExtend>
2796 TemplateNodeExtend(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2797 : TemplateNodeCreator<TemplateNodeExtend>(parser,parent,line)
2799 TRACE(("{TemplateNodeExtend(%s)\n",data.data()));
2800 ExpressionParser ep(parser,line);
2803 parser->warn(m_templateName,line,"extend tag is missing template file argument");
2805 m_extendExpr = ep.parse(data);
2807 parser->parse(this,line,stopAt,m_nodes);
2808 TRACE(("}TemplateNodeExtend(%s)\n",data.data()));
2810 ~TemplateNodeExtend()
2812 delete m_extendExpr;
2815 void render(FTextStream &ts, TemplateContext *c)
2817 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2818 ci->setLocation(m_templateName,m_line);
2819 if (m_extendExpr==0) return;
2821 QCString extendFile = m_extendExpr->resolve(c).toString();
2822 if (extendFile.isEmpty())
2824 ci->warn(m_templateName,m_line,"invalid parameter for extend command");
2827 // goto root of tree (template node)
2828 TemplateImpl *t = getTemplate();
2831 Template *bt = t->engine()->loadByName(extendFile,m_line);
2832 TemplateImpl *baseTemplate = bt ? dynamic_cast<TemplateImpl*>(bt) : 0;
2835 // fill block context
2836 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
2837 TemplateBlockContext *bc = ci->blockContext();
2839 // add overruling blocks to the context
2840 QListIterator<TemplateNode> li(m_nodes);
2842 for (li.toFirst();(n=li.current());++li)
2844 TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
2849 TemplateNodeMsg *msg = dynamic_cast<TemplateNodeMsg*>(n);
2856 // render the base template with the given context
2857 baseTemplate->render(ts,c);
2861 t->engine()->unload(t);
2865 ci->warn(m_templateName,m_line,"failed to load template %s for extend",extendFile.data());
2871 ExprAst *m_extendExpr;
2872 TemplateNodeList m_nodes;
2875 /** @brief Class representing an 'include' tag in a template */
2876 class TemplateNodeInclude : public TemplateNodeCreator<TemplateNodeInclude>
2879 TemplateNodeInclude(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2880 : TemplateNodeCreator<TemplateNodeInclude>(parser,parent,line)
2882 TRACE(("TemplateNodeInclude(%s)\n",data.data()));
2883 ExpressionParser ep(parser,line);
2886 parser->warn(m_templateName,line,"include tag is missing template file argument");
2888 m_includeExpr = ep.parse(data);
2890 ~TemplateNodeInclude()
2892 delete m_includeExpr;
2894 void render(FTextStream &ts, TemplateContext *c)
2896 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2897 ci->setLocation(m_templateName,m_line);
2900 QCString includeFile = m_includeExpr->resolve(c).toString();
2901 if (includeFile.isEmpty())
2903 ci->warn(m_templateName,m_line,"invalid parameter for include command\n");
2907 TemplateImpl *t = getTemplate();
2910 Template *it = t->engine()->loadByName(includeFile,m_line);
2911 TemplateImpl *incTemplate = it ? dynamic_cast<TemplateImpl*>(it) : 0;
2914 incTemplate->render(ts,c);
2915 t->engine()->unload(t);
2919 ci->warn(m_templateName,m_line,"failed to load template '%s' for include",includeFile.data()?includeFile.data():"");
2927 ExprAst *m_includeExpr;
2930 //----------------------------------------------------------
2932 /** @brief Class representing an 'create' tag in a template */
2933 class TemplateNodeCreate : public TemplateNodeCreator<TemplateNodeCreate>
2936 TemplateNodeCreate(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2937 : TemplateNodeCreator<TemplateNodeCreate>(parser,parent,line)
2939 TRACE(("TemplateNodeCreate(%s)\n",data.data()));
2940 ExpressionParser ep(parser,line);
2943 parser->warn(m_templateName,line,"create tag is missing arguments");
2945 int i = data.find(" from ");
2948 if (data.right(3)==" from")
2950 parser->warn(m_templateName,line,"create is missing template name after 'from' keyword");
2952 else if (data=="from")
2954 parser->warn(m_templateName,line,"create needs a file name and a template name");
2958 parser->warn(m_templateName,line,"create is missing 'from' keyword");
2963 ExpressionParser ep(parser,line);
2964 m_fileExpr = ep.parse(data.left(i).stripWhiteSpace());
2965 m_templateExpr = ep.parse(data.mid(i+6).stripWhiteSpace());
2968 ~TemplateNodeCreate()
2970 delete m_templateExpr;
2973 void render(FTextStream &, TemplateContext *c)
2975 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2976 ci->setLocation(m_templateName,m_line);
2977 if (m_templateExpr && m_fileExpr)
2979 QCString templateFile = m_templateExpr->resolve(c).toString();
2980 QCString outputFile = m_fileExpr->resolve(c).toString();
2981 if (templateFile.isEmpty())
2983 ci->warn(m_templateName,m_line,"empty template name parameter for create command\n");
2985 else if (outputFile.isEmpty())
2987 ci->warn(m_templateName,m_line,"empty file name parameter for create command\n");
2991 TemplateImpl *t = getTemplate();
2994 Template *ct = t->engine()->loadByName(templateFile,m_line);
2995 TemplateImpl *createTemplate = ct ? dynamic_cast<TemplateImpl*>(ct) : 0;
2998 QCString extension=outputFile;
2999 int i=extension.findRev('.');
3002 extension=extension.right(extension.length()-i-1);
3004 if (!ci->outputDirectory().isEmpty())
3006 outputFile.prepend(ci->outputDirectory()+"/");
3008 QFile f(outputFile);
3009 if (f.open(IO_WriteOnly))
3011 TemplateEscapeIntf *escIntf = ci->escapeIntf();
3012 ci->selectEscapeIntf(extension);
3014 createTemplate->render(ts,c);
3015 t->engine()->unload(t);
3016 ci->setActiveEscapeIntf(escIntf);
3020 ci->warn(m_templateName,m_line,"failed to open output file '%s' for create command",outputFile.data());
3025 ci->warn(m_templateName,m_line,"failed to load template '%s' for include",templateFile.data());
3033 ExprAst *m_templateExpr;
3034 ExprAst *m_fileExpr;
3037 //----------------------------------------------------------
3039 /** @brief Class representing an 'tree' tag in a template */
3040 class TemplateNodeTree : public TemplateNodeCreator<TemplateNodeTree>
3044 TreeContext(TemplateNodeTree *o,const TemplateListIntf *l,TemplateContext *c)
3045 : object(o), list(l), templateCtx(c) {}
3046 TemplateNodeTree *object;
3047 const TemplateListIntf *list;
3048 TemplateContext *templateCtx;
3051 TemplateNodeTree(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3052 : TemplateNodeCreator<TemplateNodeTree>(parser,parent,line)
3054 TRACE(("{TemplateNodeTree(%s)\n",data.data()));
3055 ExpressionParser ep(parser,line);
3058 parser->warn(m_templateName,line,"recursetree tag is missing data argument");
3060 m_treeExpr = ep.parse(data);
3062 stopAt.append("endrecursetree");
3063 parser->parse(this,line,stopAt,m_treeNodes);
3064 parser->removeNextToken(); // skip over endrecursetree
3065 TRACE(("}TemplateNodeTree(%s)\n",data.data()));
3071 static TemplateVariant renderChildrenStub(const void *ctx, const QValueList<TemplateVariant> &)
3073 return TemplateVariant(((TreeContext*)ctx)->object->
3074 renderChildren((const TreeContext*)ctx),TRUE);
3076 QCString renderChildren(const TreeContext *ctx)
3078 //printf("TemplateNodeTree::renderChildren(%d)\n",ctx->list->count());
3079 // render all children of node to a string and return it
3081 FTextStream ss(&result);
3082 TemplateContext *c = ctx->templateCtx;
3084 TemplateVariant node;
3085 TemplateListIntf::ConstIterator *it = ctx->list->createIterator();
3086 for (it->toFirst();(it->current(node));it->toNext())
3088 c->set("node",node);
3089 bool hasChildren=FALSE;
3090 const TemplateStructIntf *ns = node.toStruct();
3091 if (ns) // node is a struct
3093 TemplateVariant v = ns->get("children");
3094 if (v.isValid()) // with a field 'children'
3096 const TemplateListIntf *list = v.toList();
3097 if (list && list->count()>0) // non-empty list
3099 TreeContext childCtx(this,list,ctx->templateCtx);
3100 // TemplateVariant children(&childCtx,renderChildrenStub);
3101 TemplateVariant children(TemplateVariant::Delegate::fromFunction(&childCtx,renderChildrenStub));
3102 children.setRaw(TRUE);
3103 c->set("children",children);
3104 m_treeNodes.render(ss,c);
3111 c->set("children",TemplateVariant("")); // provide default
3112 m_treeNodes.render(ss,c);
3116 return result.data();
3118 void render(FTextStream &ts, TemplateContext *c)
3120 //printf("TemplateNodeTree::render()\n");
3121 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3122 ci->setLocation(m_templateName,m_line);
3123 TemplateVariant v = m_treeExpr->resolve(c);
3124 const TemplateListIntf *list = v.toList();
3127 TreeContext ctx(this,list,c);
3128 ts << renderChildren(&ctx);
3132 ci->warn(m_templateName,m_line,"recursetree's argument should be a list type");
3137 ExprAst *m_treeExpr;
3138 TemplateNodeList m_treeNodes;
3141 //----------------------------------------------------------
3143 /** @brief Class representing an 'indexentry' tag in a template */
3144 class TemplateNodeIndexEntry : public TemplateNodeCreator<TemplateNodeIndexEntry>
3148 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3149 ~Mapping() { delete value; }
3154 TemplateNodeIndexEntry(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3155 : TemplateNodeCreator<TemplateNodeIndexEntry>(parser,parent,line)
3157 TRACE(("{TemplateNodeIndexEntry(%s)\n",data.data()));
3158 m_args.setAutoDelete(TRUE);
3159 ExpressionParser expParser(parser,line);
3160 QValueList<QCString> args = split(data," ");
3161 QValueListIterator<QCString> it = args.begin();
3162 if (it==args.end() || (*it).find('=')!=-1)
3164 parser->warn(parser->templateName(),line,"Missing name for indexentry tag");
3170 while (it!=args.end())
3173 int j=arg.find('=');
3176 ExprAst *expr = expParser.parse(arg.mid(j+1));
3179 m_args.append(new Mapping(arg.left(j),expr));
3184 parser->warn(parser->templateName(),line,"invalid argument '%s' for indexentry tag",arg.data());
3189 TRACE(("}TemplateNodeIndexEntry(%s)\n",data.data()));
3191 void render(FTextStream &, TemplateContext *c)
3193 if (!m_name.isEmpty())
3195 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3196 ci->setLocation(m_templateName,m_line);
3197 QListIterator<Mapping> it(m_args);
3199 QValueList<TemplateKeyValue> list;
3200 for (it.toFirst();(mapping=it.current());++it)
3202 list.append(TemplateKeyValue(mapping->name,mapping->value->resolve(c)));
3204 ci->addIndexEntry(m_name,list);
3209 QList<Mapping> m_args;
3212 //----------------------------------------------------------
3214 /** @brief Class representing an 'opensubindex' tag in a template */
3215 class TemplateNodeOpenSubIndex : public TemplateNodeCreator<TemplateNodeOpenSubIndex>
3218 TemplateNodeOpenSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3219 : TemplateNodeCreator<TemplateNodeOpenSubIndex>(parser,parent,line)
3221 TRACE(("{TemplateNodeOpenSubIndex(%s)\n",data.data()));
3222 m_name = data.stripWhiteSpace();
3223 if (m_name.isEmpty())
3225 parser->warn(parser->templateName(),line,"Missing argument for opensubindex tag");
3227 else if (m_name.find(' ')!=-1)
3229 parser->warn(parser->templateName(),line,"Expected single argument for opensubindex tag got '%s'",data.data());
3232 TRACE(("}TemplateNodeOpenSubIndex(%s)\n",data.data()));
3234 void render(FTextStream &, TemplateContext *c)
3236 if (!m_name.isEmpty())
3238 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3239 ci->setLocation(m_templateName,m_line);
3240 ci->openSubIndex(m_name);
3247 //----------------------------------------------------------
3249 /** @brief Class representing an 'closesubindex' tag in a template */
3250 class TemplateNodeCloseSubIndex : public TemplateNodeCreator<TemplateNodeCloseSubIndex>
3253 TemplateNodeCloseSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3254 : TemplateNodeCreator<TemplateNodeCloseSubIndex>(parser,parent,line)
3256 TRACE(("{TemplateNodeCloseSubIndex(%s)\n",data.data()));
3257 m_name = data.stripWhiteSpace();
3258 if (m_name.isEmpty())
3260 parser->warn(parser->templateName(),line,"Missing argument for closesubindex tag");
3262 else if (m_name.find(' ')!=-1 || m_name.isEmpty())
3264 parser->warn(parser->templateName(),line,"Expected single argument for closesubindex tag got '%s'",data.data());
3267 TRACE(("}TemplateNodeCloseSubIndex(%s)\n",data.data()));
3269 void render(FTextStream &, TemplateContext *c)
3271 if (!m_name.isEmpty())
3273 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3274 ci->setLocation(m_templateName,m_line);
3275 ci->closeSubIndex(m_name);
3283 //----------------------------------------------------------
3285 /** @brief Class representing an 'with' tag in a template */
3286 class TemplateNodeWith : public TemplateNodeCreator<TemplateNodeWith>
3290 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3291 ~Mapping() { delete value; }
3296 TemplateNodeWith(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3297 : TemplateNodeCreator<TemplateNodeWith>(parser,parent,line)
3299 TRACE(("{TemplateNodeWith(%s)\n",data.data()));
3300 m_args.setAutoDelete(TRUE);
3301 ExpressionParser expParser(parser,line);
3302 QValueList<QCString> args = split(data," ");
3303 QValueListIterator<QCString> it = args.begin();
3304 while (it!=args.end())
3307 int j=arg.find('=');
3310 ExprAst *expr = expParser.parse(arg.mid(j+1));
3313 m_args.append(new Mapping(arg.left(j),expr));
3318 parser->warn(parser->templateName(),line,"invalid argument '%s' for with tag",arg.data());
3323 stopAt.append("endwith");
3324 parser->parse(this,line,stopAt,m_nodes);
3325 parser->removeNextToken(); // skip over endwith
3326 TRACE(("}TemplateNodeWith(%s)\n",data.data()));
3331 void render(FTextStream &ts, TemplateContext *c)
3333 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3334 ci->setLocation(m_templateName,m_line);
3336 QListIterator<Mapping> it(m_args);
3338 for (it.toFirst();(mapping=it.current());++it)
3340 TemplateVariant value = mapping->value->resolve(c);
3341 ci->set(mapping->name,value);
3343 m_nodes.render(ts,c);
3347 TemplateNodeList m_nodes;
3348 QList<Mapping> m_args;
3351 //----------------------------------------------------------
3353 /** @brief Class representing an 'set' tag in a template */
3354 class TemplateNodeCycle : public TemplateNodeCreator<TemplateNodeCycle>
3357 TemplateNodeCycle(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3358 : TemplateNodeCreator<TemplateNodeCycle>(parser,parent,line)
3360 TRACE(("{TemplateNodeCycle(%s)\n",data.data()));
3361 m_args.setAutoDelete(TRUE);
3363 ExpressionParser expParser(parser,line);
3364 QValueList<QCString> args = split(data," ");
3365 QValueListIterator<QCString> it = args.begin();
3366 while (it!=args.end())
3368 ExprAst *expr = expParser.parse(*it);
3371 m_args.append(expr);
3375 if (m_args.count()<2)
3377 parser->warn(parser->templateName(),line,"expected at least two arguments for cycle command, got %d",m_args.count());
3379 TRACE(("}TemplateNodeCycle(%s)\n",data.data()));
3381 void render(FTextStream &ts, TemplateContext *c)
3383 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3384 ci->setLocation(m_templateName,m_line);
3385 if (m_index<m_args.count())
3387 TemplateVariant v = m_args.at(m_index)->resolve(c);
3388 if (v.type()==TemplateVariant::Function)
3390 v = v.call(QValueList<TemplateVariant>());
3392 if (ci->escapeIntf() && !v.raw())
3394 ts << ci->escapeIntf()->escape(v.toString());
3401 if (++m_index==m_args.count()) // wrap around
3408 QList<ExprAst> m_args;
3411 //----------------------------------------------------------
3413 /** @brief Class representing an 'set' tag in a template */
3414 class TemplateNodeSet : public TemplateNodeCreator<TemplateNodeSet>
3418 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3419 ~Mapping() { delete value; }
3424 TemplateNodeSet(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3425 : TemplateNodeCreator<TemplateNodeSet>(parser,parent,line)
3427 TRACE(("{TemplateNodeSet(%s)\n",data.data()));
3428 m_args.setAutoDelete(TRUE);
3429 ExpressionParser expParser(parser,line);
3430 QValueList<QCString> args = split(data," ");
3431 QValueListIterator<QCString> it = args.begin();
3432 while (it!=args.end())
3435 int j=arg.find('=');
3438 ExprAst *expr = expParser.parse(arg.mid(j+1));
3441 m_args.append(new Mapping(arg.left(j),expr));
3446 parser->warn(parser->templateName(),line,"invalid argument '%s' for with tag",arg.data());
3450 TRACE(("}TemplateNodeSet(%s)\n",data.data()));
3452 void render(FTextStream &, TemplateContext *c)
3454 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3455 ci->setLocation(m_templateName,m_line);
3456 QListIterator<Mapping> it(m_args);
3458 for (it.toFirst();(mapping=it.current());++it)
3460 TemplateVariant value = mapping->value->resolve(c);
3461 ci->set(mapping->name,value);
3465 QList<Mapping> m_args;
3468 //----------------------------------------------------------
3470 /** @brief Class representing an 'spaceless' tag in a template */
3471 class TemplateNodeSpaceless : public TemplateNodeCreator<TemplateNodeSpaceless>
3474 TemplateNodeSpaceless(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
3475 : TemplateNodeCreator<TemplateNodeSpaceless>(parser,parent,line)
3477 TRACE(("{TemplateNodeSpaceless()\n"));
3479 stopAt.append("endspaceless");
3480 parser->parse(this,line,stopAt,m_nodes);
3481 parser->removeNextToken(); // skip over endwith
3482 TRACE(("}TemplateNodeSpaceless()\n"));
3484 void render(FTextStream &ts, TemplateContext *c)
3486 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3487 ci->setLocation(m_templateName,m_line);
3488 bool wasSpaceless = ci->spacelessEnabled();
3489 ci->enableSpaceless(TRUE);
3490 m_nodes.render(ts,c);
3491 ci->enableSpaceless(wasSpaceless);
3494 TemplateNodeList m_nodes;
3497 //----------------------------------------------------------
3499 /** @brief Class representing an 'markers' tag in a template */
3500 class TemplateNodeMarkers : public TemplateNodeCreator<TemplateNodeMarkers>
3503 TemplateNodeMarkers(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3504 : TemplateNodeCreator<TemplateNodeMarkers>(parser,parent,line)
3506 TRACE(("{TemplateNodeMarkers(%s)\n",data.data()));
3507 int i = data.find(" in ");
3508 int w = data.find(" with ");
3509 if (i==-1 || w==-1 || w<i)
3511 parser->warn(m_templateName,line,"markers tag as wrong format. Expected: markers <var> in <list> with <string_with_markers>");
3515 ExpressionParser expParser(parser,line);
3516 m_var = data.left(i);
3517 m_listExpr = expParser.parse(data.mid(i+4,w-i-4));
3518 m_patternExpr = expParser.parse(data.right(data.length()-w-6));
3521 stopAt.append("endmarkers");
3522 parser->parse(this,line,stopAt,m_nodes);
3523 parser->removeNextToken(); // skip over endmarkers
3524 TRACE(("}TemplateNodeMarkers(%s)\n",data.data()));
3526 void render(FTextStream &ts, TemplateContext *c)
3528 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3529 ci->setLocation(m_templateName,m_line);
3530 if (!m_var.isEmpty() && m_listExpr && m_patternExpr)
3532 TemplateVariant v = m_listExpr->resolve(c);
3533 const TemplateListIntf *list = v.toList();
3534 TemplateVariant patternStr = m_patternExpr->resolve(c);
3537 if (patternStr.type()==TemplateVariant::String)
3539 TemplateListIntf::ConstIterator *it = list->createIterator();
3541 QCString str = patternStr.toString();
3542 QRegExp marker("@[0-9]+"); // pattern for a marker, i.e. @0, @1 ... @12, etc
3543 int index=0,newIndex,matchLen;
3544 while ((newIndex=marker.match(str,index,&matchLen))!=-1)
3546 ts << str.mid(index,newIndex-index); // write text before marker
3548 uint entryIndex = str.mid(newIndex+1,matchLen-1).toUInt(&ok); // get marker id
3549 TemplateVariant var;
3551 // search for list element at position id
3552 for (it->toFirst(); (it->current(var)) && i<entryIndex; it->toNext(),i++) {}
3553 if (ok && i==entryIndex) // found element
3555 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
3556 s->set("id",(int)i);
3557 c->set("markers",s.get());
3558 c->set(m_var,var); // define local variable to hold element of list type
3559 bool wasSpaceless = ci->spacelessEnabled();
3560 ci->enableSpaceless(TRUE);
3561 m_nodes.render(ts,c);
3562 ci->enableSpaceless(wasSpaceless);
3566 ci->warn(m_templateName,m_line,"markers pattern string has invalid markers '%s'",str.data());
3568 else if (i<entryIndex)
3570 ci->warn(m_templateName,m_line,"markers list does not an element for marker position %d",i);
3572 index=newIndex+matchLen; // set index just after marker
3574 ts << str.right(str.length()-index); // write text after last marker
3579 ci->warn(m_templateName,m_line,"markers requires a parameter of string type after 'with'!");
3584 ci->warn(m_templateName,m_line,"markers requires a parameter of list type after 'in'!");
3589 TemplateNodeList m_nodes;
3591 ExprAst *m_listExpr;
3592 ExprAst *m_patternExpr;
3595 //----------------------------------------------------------
3597 /** @brief Factory class for creating tag AST nodes found in a template */
3598 class TemplateNodeFactory
3601 typedef TemplateNode *(*CreateFunc)(TemplateParser *parser,
3602 TemplateNode *parent,
3604 const QCString &data);
3606 static TemplateNodeFactory *instance()
3608 static TemplateNodeFactory *instance = 0;
3609 if (instance==0) instance = new TemplateNodeFactory;
3613 TemplateNode *create(const QCString &name,
3614 TemplateParser *parser,
3615 TemplateNode *parent,
3617 const QCString &data)
3619 if (m_registry.find(name)==0) return 0;
3620 return ((CreateFunc)m_registry[name])(parser,parent,line,data);
3623 void registerTemplateNode(const QCString &name,CreateFunc func)
3625 m_registry.insert(name,(void*)func);
3628 /** @brief Helper class for registering a template AST node */
3629 template<class T> class AutoRegister
3632 AutoRegister<T>(const QCString &key)
3634 TemplateNodeFactory::instance()->registerTemplateNode(key,T::createInstance);
3639 QDict<void> m_registry;
3642 // register a handler for each start tag we support
3643 static TemplateNodeFactory::AutoRegister<TemplateNodeIf> autoRefIf("if");
3644 static TemplateNodeFactory::AutoRegister<TemplateNodeFor> autoRefFor("for");
3645 static TemplateNodeFactory::AutoRegister<TemplateNodeMsg> autoRefMsg("msg");
3646 static TemplateNodeFactory::AutoRegister<TemplateNodeSet> autoRefSet("set");
3647 static TemplateNodeFactory::AutoRegister<TemplateNodeTree> autoRefTree("recursetree");
3648 static TemplateNodeFactory::AutoRegister<TemplateNodeWith> autoRefWith("with");
3649 static TemplateNodeFactory::AutoRegister<TemplateNodeBlock> autoRefBlock("block");
3650 static TemplateNodeFactory::AutoRegister<TemplateNodeCycle> autoRefCycle("cycle");
3651 static TemplateNodeFactory::AutoRegister<TemplateNodeRange> autoRefRange("range");
3652 static TemplateNodeFactory::AutoRegister<TemplateNodeExtend> autoRefExtend("extend");
3653 static TemplateNodeFactory::AutoRegister<TemplateNodeCreate> autoRefCreate("create");
3654 static TemplateNodeFactory::AutoRegister<TemplateNodeRepeat> autoRefRepeat("repeat");
3655 static TemplateNodeFactory::AutoRegister<TemplateNodeInclude> autoRefInclude("include");
3656 static TemplateNodeFactory::AutoRegister<TemplateNodeMarkers> autoRefMarkers("markers");
3657 static TemplateNodeFactory::AutoRegister<TemplateNodeSpaceless> autoRefSpaceless("spaceless");
3658 static TemplateNodeFactory::AutoRegister<TemplateNodeIndexEntry> autoRefIndexEntry("indexentry");
3659 static TemplateNodeFactory::AutoRegister<TemplateNodeOpenSubIndex> autoRefOpenSubIndex("opensubindex");
3660 static TemplateNodeFactory::AutoRegister<TemplateNodeCloseSubIndex> autoRefCloseSubIndex("closesubindex");
3662 //----------------------------------------------------------
3664 TemplateBlockContext::TemplateBlockContext() : m_blocks(257)
3666 m_blocks.setAutoDelete(TRUE);
3669 TemplateNodeBlock *TemplateBlockContext::get(const QCString &name) const
3671 QList<TemplateNodeBlock> *list = m_blocks.find(name);
3672 if (list==0 || list->count()==0)
3678 return list->getLast();
3682 TemplateNodeBlock *TemplateBlockContext::pop(const QCString &name) const
3684 QList<TemplateNodeBlock> *list = m_blocks.find(name);
3685 if (list==0 || list->count()==0)
3691 return list->take(list->count()-1);
3695 void TemplateBlockContext::add(TemplateNodeBlock *block)
3697 QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
3700 list = new QList<TemplateNodeBlock>;
3701 m_blocks.insert(block->name(),list);
3703 list->prepend(block);
3706 void TemplateBlockContext::add(TemplateBlockContext *ctx)
3708 QDictIterator< QList<TemplateNodeBlock> > di(ctx->m_blocks);
3709 QList<TemplateNodeBlock> *list;
3710 for (di.toFirst();(list=di.current());++di)
3712 QListIterator<TemplateNodeBlock> li(*list);
3713 TemplateNodeBlock *nb;
3714 for (li.toFirst();(nb=li.current());++li)
3721 void TemplateBlockContext::clear()
3726 void TemplateBlockContext::push(TemplateNodeBlock *block)
3728 QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
3731 list = new QList<TemplateNodeBlock>;
3732 m_blocks.insert(block->name(),list);
3734 list->append(block);
3738 //----------------------------------------------------------
3740 /** @brief Lexer class for turning a template into a list of tokens */
3744 TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data);
3745 void tokenize(QList<TemplateToken> &tokens);
3747 void addToken(QList<TemplateToken> &tokens,
3748 const char *data,int line,int startPos,int endPos,
3749 TemplateToken::Type type);
3751 const TemplateEngine *m_engine;
3752 QCString m_fileName;
3756 TemplateLexer::TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data) :
3757 m_engine(engine), m_fileName(fileName), m_data(data)
3761 void TemplateLexer::tokenize(QList<TemplateToken> &tokens)
3776 const char *p=m_data.data();
3777 int state=StateText;
3781 bool emptyOutputLine=TRUE;
3784 int markStartPos=-1;
3785 for (;(c=*p);p++,pos++)
3790 if (c=='{') // {{ or {% or {# or something else
3792 state=StateBeginTemplate;
3794 else if (c!=' ' && c!='\t' && c!='\n') // non-whitepace text
3796 emptyOutputLine=FALSE;
3799 case StateBeginTemplate:
3811 state=StateMaybeVar;
3816 emptyOutputLine=FALSE;
3823 warn(m_fileName,line,"unexpected new line inside {%%...%%} block");
3824 m_engine->printIncludeContext(m_fileName,line);
3826 else if (c=='%') // %} or something else
3836 addToken(tokens,m_data.data(),line,lastTokenPos,
3837 emptyOutputLine ? startLinePos : markStartPos,
3838 TemplateToken::Text);
3839 addToken(tokens,m_data.data(),line,markStartPos+2,
3840 pos-1,TemplateToken::Block);
3841 lastTokenPos = pos+1;
3843 else // something else
3847 warn(m_fileName,line,"unexpected new line inside {%%...%%} block");
3848 m_engine->printIncludeContext(m_fileName,line);
3856 warn(m_fileName,line,"unexpected new line inside {#...#} block");
3857 m_engine->printIncludeContext(m_fileName,line);
3859 else if (c=='#') // #} or something else
3861 state=StateEndComment;
3864 case StateEndComment:
3867 // found comment tag!
3869 addToken(tokens,m_data.data(),line,lastTokenPos,
3870 emptyOutputLine ? startLinePos : markStartPos,
3871 TemplateToken::Text);
3872 lastTokenPos = pos+1;
3874 else // something else
3878 warn(m_fileName,line,"unexpected new line inside {#...#} block");
3879 m_engine->printIncludeContext(m_fileName,line);
3896 state=StateVariable;
3903 warn(m_fileName,line,"unexpected new line inside {{...}} block");
3904 m_engine->printIncludeContext(m_fileName,line);
3906 else if (c=='}') // }} or something else
3908 state=StateEndVariable;
3911 case StateEndVariable:
3914 // found variable tag!
3916 addToken(tokens,m_data.data(),line,lastTokenPos,
3917 emptyOutputLine ? startLinePos : markStartPos,
3918 TemplateToken::Text);
3919 addToken(tokens,m_data.data(),line,markStartPos+2,
3920 pos-1,TemplateToken::Variable);
3921 lastTokenPos = pos+1;
3923 else // something else
3927 warn(m_fileName,line,"unexpected new line inside {{...}} block");
3928 m_engine->printIncludeContext(m_fileName,line);
3930 state=StateVariable;
3934 if (c=='\n') // new line
3938 // if the current line only contain commands and whitespace,
3939 // then skip it in the output by moving lastTokenPos
3940 if (markStartPos!=-1 && emptyOutputLine) lastTokenPos = startLinePos;
3944 emptyOutputLine=TRUE;
3947 if (lastTokenPos<pos)
3949 addToken(tokens,m_data.data(),line,
3951 TemplateToken::Text);
3955 void TemplateLexer::addToken(QList<TemplateToken> &tokens,
3956 const char *data,int line,
3957 int startPos,int endPos,
3958 TemplateToken::Type type)
3960 if (startPos<endPos)
3962 int len = endPos-startPos+1;
3963 QCString text(len+1);
3964 qstrncpy(text.data(),data+startPos,len);
3966 if (type!=TemplateToken::Text) text = text.stripWhiteSpace();
3967 tokens.append(new TemplateToken(type,text,line));
3971 //----------------------------------------------------------
3973 TemplateParser::TemplateParser(const TemplateEngine *engine,
3974 const QCString &templateName,
3975 QList<TemplateToken> &tokens) :
3976 m_engine(engine), m_templateName(templateName), m_tokens(tokens)
3980 void TemplateParser::parse(
3981 TemplateNode *parent,int line,const QStrList &stopAt,
3982 QList<TemplateNode> &nodes)
3984 TRACE(("{TemplateParser::parse\n"));
3985 // process the tokens. Build node list
3986 while (hasNextToken())
3988 TemplateToken *tok = takeNextToken();
3989 //printf("%p:Token type=%d data='%s' line=%d\n",
3990 // parent,tok->type,tok->data.data(),tok->line);
3993 case TemplateToken::Text:
3994 nodes.append(new TemplateNodeText(this,parent,tok->line,tok->data));
3996 case TemplateToken::Variable: // {{ var }}
3997 nodes.append(new TemplateNodeVariable(this,parent,tok->line,tok->data));
3999 case TemplateToken::Block: // {% tag %}
4001 QCString command = tok->data;
4002 int sep = command.find(' ');
4005 command=command.left(sep);
4007 if (stopAt.contains(command))
4010 TRACE(("}TemplateParser::parse: stop\n"));
4016 arg = tok->data.mid(sep+1);
4018 TemplateNode *node = TemplateNodeFactory::instance()->
4019 create(command,this,parent,tok->line,arg);
4024 else if (command=="empty" || command=="else" ||
4025 command=="endif" || command=="endfor" ||
4026 command=="endblock" || command=="endwith" ||
4027 command=="endrecursetree" || command=="endspaceless" ||
4028 command=="endmarkers" || command=="endmsg" ||
4029 command=="endrepeat" || command=="elif" ||
4030 command=="endrange")
4032 warn(m_templateName,tok->line,"Found tag '%s' without matching start tag",command.data());
4036 warn(m_templateName,tok->line,"Unknown tag '%s'",command.data());
4043 if (!stopAt.isEmpty())
4045 QStrListIterator it(stopAt);
4048 for (it.toFirst();(s=it.current());++it)
4050 if (!options.isEmpty()) options+=", ";
4053 warn(m_templateName,line,"Unclosed tag in template, expected one of: %s",
4056 TRACE(("}TemplateParser::parse: last token\n"));
4059 bool TemplateParser::hasNextToken() const
4061 return !m_tokens.isEmpty();
4064 TemplateToken *TemplateParser::takeNextToken()
4066 return m_tokens.take(0);
4069 const TemplateToken *TemplateParser::currentToken() const
4071 return m_tokens.getFirst();
4074 void TemplateParser::removeNextToken()
4076 m_tokens.removeFirst();
4079 void TemplateParser::prependToken(const TemplateToken *token)
4081 m_tokens.prepend(token);
4084 void TemplateParser::warn(const char *fileName,int line,const char *fmt,...) const
4088 va_warn(fileName,line,fmt,args);
4090 m_engine->printIncludeContext(fileName,line);
4095 //----------------------------------------------------------
4098 TemplateImpl::TemplateImpl(TemplateEngine *engine,const QCString &name,const QCString &data)
4103 TemplateLexer lexer(engine,name,data);
4104 QList<TemplateToken> tokens;
4105 tokens.setAutoDelete(TRUE);
4106 lexer.tokenize(tokens);
4107 TemplateParser parser(engine,name,tokens);
4108 parser.parse(this,1,QStrList(),m_nodes);
4111 void TemplateImpl::render(FTextStream &ts, TemplateContext *c)
4113 if (!m_nodes.isEmpty())
4115 TemplateNodeExtend *ne = dynamic_cast<TemplateNodeExtend*>(m_nodes.getFirst());
4116 if (ne==0) // normal template, add blocks to block context
4118 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4119 TemplateBlockContext *bc = ci->blockContext();
4120 QListIterator<TemplateNode> li(m_nodes);
4122 for (li.toFirst();(n=li.current());++li)
4124 TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
4131 m_nodes.render(ts,c);
4135 //----------------------------------------------------------
4137 /** @brief Private data of the template engine */
4138 class TemplateEngine::Private
4143 enum Type { Template, Block };
4144 IncludeEntry(Type type,const QCString &fileName,const QCString &blockName,int line)
4145 : m_type(type), m_fileName(fileName), m_blockName(blockName), m_line(line) {}
4146 Type type() const { return m_type; }
4147 QCString fileName() const { return m_fileName; }
4148 QCString blockName() const { return m_blockName; }
4149 int line() const { return m_line; }
4153 QCString m_fileName;
4154 QCString m_blockName;
4158 Private(TemplateEngine *engine) : m_templateCache(17) /*, m_indent(0)*/, m_engine(engine)
4160 m_templateCache.setAutoDelete(TRUE);
4161 m_includeStack.setAutoDelete(TRUE);
4163 Template *loadByName(const QCString &fileName,int line)
4165 //for (int i=0;i<m_indent;i++) printf(" ");
4167 //printf("loadByName(%s,%d) {\n",fileName.data(),line);
4168 m_includeStack.append(new IncludeEntry(IncludeEntry::Template,fileName,QCString(),line));
4169 Template *templ = m_templateCache.find(fileName);
4173 if (f.open(IO_ReadOnly))
4176 char *data = new char[size+1];
4180 if (f.readBlock(data,f.size()))
4182 templ = new TemplateImpl(m_engine,fileName,data);
4183 m_templateCache.insert(fileName,templ);
4190 err("Cound not open template file %s\n",fileName.data());
4195 void unload(Template * /*t*/)
4199 //for (int i=0;i<m_indent;i++) printf(" ");
4201 m_includeStack.removeLast();
4204 void enterBlock(const QCString &fileName,const QCString &blockName,int line)
4206 //for (int i=0;i<m_indent;i++) printf(" ");
4208 //printf("enterBlock(%s,%s,%d) {\n",fileName.data(),blockName.data(),line);
4209 m_includeStack.append(new IncludeEntry(IncludeEntry::Block,fileName,blockName,line));
4215 //for (int i=0;i<m_indent;i++) printf(" ");
4217 m_includeStack.removeLast();
4220 void printIncludeContext(const char *fileName,int line) const
4222 QListIterator<IncludeEntry> li(m_includeStack);
4224 IncludeEntry *ie=li.current();
4225 while ((ie=li.current()))
4228 IncludeEntry *next=li.current();
4229 if (ie->type()==IncludeEntry::Template)
4233 warn(fileName,line," inside template '%s' included from template '%s' at line %d",ie->fileName().data(),next->fileName().data(),ie->line());
4236 else // ie->type()==IncludeEntry::Block
4238 warn(fileName,line," included by block '%s' inside template '%s' at line %d",ie->blockName().data(),
4239 ie->fileName().data(),ie->line());
4245 QDict<Template> m_templateCache;
4246 //mutable int m_indent;
4247 TemplateEngine *m_engine;
4248 QList<IncludeEntry> m_includeStack;
4251 TemplateEngine::TemplateEngine()
4253 p = new Private(this);
4256 TemplateEngine::~TemplateEngine()
4261 TemplateContext *TemplateEngine::createContext() const
4263 return new TemplateContextImpl(this);
4266 Template *TemplateEngine::loadByName(const QCString &fileName,int line)
4268 return p->loadByName(fileName,line);
4271 void TemplateEngine::unload(Template *t)
4276 void TemplateEngine::enterBlock(const QCString &fileName,const QCString &blockName,int line)
4278 p->enterBlock(fileName,blockName,line);
4281 void TemplateEngine::leaveBlock()
4286 void TemplateEngine::printIncludeContext(const char *fileName,int line) const
4288 p->printIncludeContext(fileName,line);