1 /******************************************************************************
3 * Copyright (C) 1997-2014 by Dimitri van Heesch.
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
25 #include <qvaluelist.h>
33 #include "ftextstream.h"
37 #define ENABLE_TRACING 0
40 #define TRACE(x) printf x
47 //-------------------------------------------------------------------
49 static QValueList<QCString> split(const QCString &str,const QCString &sep,
50 bool allowEmptyEntries=FALSE,bool cleanup=TRUE)
52 QValueList<QCString> lst;
55 int i = str.find( sep, j );
59 if ( str.mid(j,i-j).length() > 0 )
63 lst.append(str.mid(j,i-j).stripWhiteSpace());
67 lst.append(str.mid(j,i-j));
70 else if (allowEmptyEntries)
78 int l = str.length() - 1;
79 if (str.mid(j,l-j+1).length()>0)
83 lst.append(str.mid(j,l-j+1).stripWhiteSpace());
87 lst.append(str.mid(j,l-j+1));
90 else if (allowEmptyEntries)
98 //----------------------------------------------------------------------------
101 static QCString replace(const char *s,char csrc,char cdst)
104 for (char *p=result.data();*p;p++)
106 if (*p==csrc) *p=cdst;
112 //- TemplateVariant implementation -------------------------------------------
114 /** @brief Private data of a template variant object */
115 class TemplateVariant::Private
118 Private() : raw(FALSE) {}
123 TemplateStructIntf *strukt;
124 TemplateListIntf *list;
129 TemplateVariant::TemplateVariant()
135 TemplateVariant::TemplateVariant(bool b)
142 TemplateVariant::TemplateVariant(int v)
149 TemplateVariant::TemplateVariant(const char *s,bool raw)
157 TemplateVariant::TemplateVariant(const QCString &s,bool raw)
165 TemplateVariant::TemplateVariant(TemplateStructIntf *s)
173 TemplateVariant::TemplateVariant(TemplateListIntf *l)
181 TemplateVariant::TemplateVariant(const TemplateVariant::Delegate &delegate)
185 p->delegate = delegate;
188 TemplateVariant::~TemplateVariant()
190 if (p->type==Struct) p->strukt->release();
191 else if (p->type==List) p->list->release();
195 TemplateVariant::TemplateVariant(const TemplateVariant &v)
203 case Bool: p->boolVal = v.p->boolVal; break;
204 case Integer: p->intVal = v.p->intVal; break;
205 case String: p->strVal = v.p->strVal; break;
206 case Struct: p->strukt = v.p->strukt; p->strukt->addRef(); break;
207 case List: p->list = v.p->list; p->list->addRef(); break;
208 case Function: p->delegate= v.p->delegate;break;
212 TemplateVariant &TemplateVariant::operator=(const TemplateVariant &v)
214 // assignment can change the type of the variable, so we have to be
215 // careful with reference counted content.
216 TemplateStructIntf *tmpStruct = p->type==Struct ? p->strukt : 0;
217 TemplateListIntf *tmpList = p->type==List ? p->list : 0;
218 Type tmpType = p->type;
225 case Bool: p->boolVal = v.p->boolVal; break;
226 case Integer: p->intVal = v.p->intVal; break;
227 case String: p->strVal = v.p->strVal; break;
228 case Struct: p->strukt = v.p->strukt; p->strukt->addRef(); break;
229 case List: p->list = v.p->list; p->list->addRef(); break;
230 case Function: p->delegate= v.p->delegate;break;
233 // release overwritten reference counted values
234 if (tmpType==Struct && tmpStruct) tmpStruct->release();
235 else if (tmpType==List && tmpList ) tmpList->release();
239 QCString TemplateVariant::toString() const
247 result=p->boolVal ? "true" : "false";
250 result=QCString().setNum(p->intVal);
268 bool TemplateVariant::toBool() const
279 result = p->intVal!=0;
282 result = !p->strVal.isEmpty(); // && p->strVal!="false" && p->strVal!="0";
288 result = p->list->count()!=0;
297 int TemplateVariant::toInt() const
305 result = p->boolVal ? 1 : 0;
311 result = p->strVal.toInt();
316 result = p->list->count();
325 TemplateStructIntf *TemplateVariant::toStruct() const
327 return p->type==Struct ? p->strukt : 0;
330 TemplateListIntf *TemplateVariant::toList() const
332 return p->type==List ? p->list : 0;
335 TemplateVariant TemplateVariant::call(const QValueList<TemplateVariant> &args)
337 if (p->type==Function) return p->delegate(args);
338 return TemplateVariant();
341 bool TemplateVariant::operator==(TemplateVariant &other)
347 if (p->type==TemplateVariant::List && other.p->type==TemplateVariant::List)
349 return p->list==other.p->list; // TODO: improve me
351 else if (p->type==TemplateVariant::Struct && other.p->type==TemplateVariant::Struct)
353 return p->strukt==other.p->strukt; // TODO: improve me
357 return toString()==other.toString();
361 TemplateVariant::Type TemplateVariant::type() const
366 QCString TemplateVariant::typeAsString() const
370 case None: return "none";
371 case Bool: return "bool";
372 case Integer: return "integer";
373 case String: return "string";
374 case Struct: return "struct";
375 case List: return "list";
376 case Function: return "function";
381 bool TemplateVariant::isValid() const
383 return p->type!=None;
386 void TemplateVariant::setRaw(bool b)
391 bool TemplateVariant::raw() const
396 //- Template struct implementation --------------------------------------------
399 /** @brief Private data of a template struct object */
400 class TemplateStruct::Private
403 Private() : fields(17), refCount(0)
404 { fields.setAutoDelete(TRUE); }
405 QDict<TemplateVariant> fields;
409 TemplateStruct::TemplateStruct()
414 TemplateStruct::~TemplateStruct()
419 int TemplateStruct::addRef()
421 return ++p->refCount;
424 int TemplateStruct::release()
426 int count = --p->refCount;
434 void TemplateStruct::set(const char *name,const TemplateVariant &v)
436 TemplateVariant *pv = p->fields.find(name);
437 if (pv) // change existing field
441 else // insert new field
443 p->fields.insert(name,new TemplateVariant(v));
447 TemplateVariant TemplateStruct::get(const char *name) const
449 TemplateVariant *v = p->fields.find(name);
450 return v ? *v : TemplateVariant();
453 TemplateStruct *TemplateStruct::alloc()
455 return new TemplateStruct;
458 //- Template list implementation ----------------------------------------------
461 /** @brief Private data of a template list object */
462 class TemplateList::Private
465 Private() : index(-1), refCount(0) {}
466 QValueList<TemplateVariant> elems;
472 TemplateList::TemplateList()
477 TemplateList::~TemplateList()
482 int TemplateList::addRef()
484 return ++p->refCount;
487 int TemplateList::release()
489 int count = --p->refCount;
497 int TemplateList::count() const
499 return p->elems.count();
502 void TemplateList::append(const TemplateVariant &v)
508 class TemplateListConstIterator : public TemplateListIntf::ConstIterator
511 TemplateListConstIterator(const TemplateList &l) : m_list(l) { m_index=-1; }
512 virtual ~TemplateListConstIterator() {}
513 virtual void toFirst()
515 m_it = m_list.p->elems.begin();
518 virtual void toLast()
520 m_it = m_list.p->elems.fromLast();
521 m_index=m_list.count()-1;
523 virtual void toNext()
525 if (m_it!=m_list.p->elems.end())
531 virtual void toPrev()
543 virtual bool current(TemplateVariant &v) const
545 if (m_index<0 || m_it==m_list.p->elems.end())
547 v = TemplateVariant();
557 const TemplateList &m_list;
558 QValueList<TemplateVariant>::ConstIterator m_it;
562 TemplateListIntf::ConstIterator *TemplateList::createIterator() const
564 return new TemplateListConstIterator(*this);
567 TemplateVariant TemplateList::at(int index) const
569 if (index>=0 && index<(int)p->elems.count())
571 return p->elems[index];
575 return TemplateVariant();
579 TemplateList *TemplateList::alloc()
581 return new TemplateList;
584 //- Operator types ------------------------------------------------------------
586 /** @brief Class representing operators that can appear in template expressions */
590 /* Operator precedence (low to high)
604 Or, And, Not, In, Equal, NotEqual, Less, Greater, LessEqual,
605 GreaterEqual, Plus, Minus, Multiply, Divide, Modulo, Filter, Colon, Comma,
606 LeftParen, RightParen,
610 static const char *toString(Type op)
614 case Or: return "or";
615 case And: return "and";
616 case Not: return "not";
617 case In: return "in";
618 case Equal: return "==";
619 case NotEqual: return "!=";
620 case Less: return "<";
621 case Greater: return ">";
622 case LessEqual: return "<=";
623 case GreaterEqual: return ">=";
624 case Plus: return "+";
625 case Minus: return "-";
626 case Multiply: return "*";
627 case Divide: return "/";
628 case Modulo: return "%";
629 case Filter: return "|";
630 case Colon: return ":";
631 case Comma: return ",";
632 case LeftParen: return "(";
633 case RightParen: return ")";
634 case Last: return "?";
640 //-----------------------------------------------------------------------------
642 class TemplateNodeBlock;
644 /** @brief Class holding stacks of blocks available in the context */
645 class TemplateBlockContext
648 TemplateBlockContext();
649 TemplateNodeBlock *get(const QCString &name) const;
650 TemplateNodeBlock *pop(const QCString &name) const;
651 void add(TemplateNodeBlock *block);
652 void add(TemplateBlockContext *ctx);
653 void push(TemplateNodeBlock *block);
656 QDict< QList<TemplateNodeBlock> > m_blocks;
659 /** @brief A container to store a key-value pair */
660 struct TemplateKeyValue
662 TemplateKeyValue() {}
663 TemplateKeyValue(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
665 TemplateVariant value;
668 /** @brief Internal class representing the implementation of a template
670 class TemplateContextImpl : public TemplateContext
673 TemplateContextImpl(const TemplateEngine *e);
674 virtual ~TemplateContextImpl();
676 // TemplateContext methods
679 void set(const char *name,const TemplateVariant &v);
680 TemplateVariant get(const QCString &name) const;
681 const TemplateVariant *getRef(const QCString &name) const;
682 void setOutputDirectory(const QCString &dir)
683 { m_outputDir = dir; }
684 void setEscapeIntf(const QCString &ext,TemplateEscapeIntf *intf)
686 int i=(!ext.isEmpty() && ext.at(0)=='.') ? 1 : 0;
687 m_escapeIntfDict.insert(ext.mid(i),new TemplateEscapeIntf*(intf));
689 void selectEscapeIntf(const QCString &ext)
690 { TemplateEscapeIntf **ppIntf = m_escapeIntfDict.find(ext);
691 m_activeEscapeIntf = ppIntf ? *ppIntf : 0;
693 void setActiveEscapeIntf(TemplateEscapeIntf *intf) { m_activeEscapeIntf = intf; }
694 void setSpacelessIntf(TemplateSpacelessIntf *intf) { m_spacelessIntf = intf; }
697 TemplateBlockContext *blockContext();
698 TemplateVariant getPrimary(const QCString &name) const;
699 void setLocation(const QCString &templateName,int line)
700 { m_templateName=templateName; m_line=line; }
701 QCString templateName() const { return m_templateName; }
702 int line() const { return m_line; }
703 QCString outputDirectory() const { return m_outputDir; }
704 TemplateEscapeIntf *escapeIntf() const { return m_activeEscapeIntf; }
705 TemplateSpacelessIntf *spacelessIntf() const { return m_spacelessIntf; }
706 void enableSpaceless(bool b) { if (b && !m_spacelessEnabled) m_spacelessIntf->reset();
707 m_spacelessEnabled=b;
709 bool spacelessEnabled() const { return m_spacelessEnabled && m_spacelessIntf; }
710 void warn(const char *fileName,int line,const char *fmt,...) const;
712 // index related functions
713 void openSubIndex(const QCString &indexName);
714 void closeSubIndex(const QCString &indexName);
715 void addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments);
718 const TemplateEngine *m_engine;
719 QCString m_templateName;
721 QCString m_outputDir;
722 QList< QDict<TemplateVariant> > m_contextStack;
723 TemplateBlockContext m_blockContext;
724 QDict<TemplateEscapeIntf*> m_escapeIntfDict;
725 TemplateEscapeIntf *m_activeEscapeIntf;
726 TemplateSpacelessIntf *m_spacelessIntf;
727 bool m_spacelessEnabled;
728 TemplateAutoRef<TemplateStruct> m_indices;
729 QDict< QStack<TemplateVariant> > m_indexStacks;
732 //-----------------------------------------------------------------------------
734 /** @brief The implementation of the "add" filter */
738 static int variantIntValue(const TemplateVariant &v,bool &isInt)
740 isInt = v.type()==TemplateVariant::Integer;
741 if (!isInt && v.type()==TemplateVariant::String)
743 return v.toString().toInt(&isInt);
745 return isInt ? v.toInt() : 0;
747 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
754 int lhsValue = variantIntValue(v,lhsIsInt);
756 int rhsValue = variantIntValue(arg,rhsIsInt);
757 if (lhsIsInt && rhsIsInt)
759 return lhsValue+rhsValue;
761 else if (v.type()==TemplateVariant::String && arg.type()==TemplateVariant::String)
763 return TemplateVariant(v.toString() + arg.toString());
772 //-----------------------------------------------------------------------------
774 /** @brief The implementation of the "get" filter */
778 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
780 if (v.isValid() && v.type()==TemplateVariant::Struct && arg.type()==TemplateVariant::String)
782 TemplateVariant result = v.toStruct()->get(arg.toString());
783 //printf("\nok[%s]=%d\n",arg.toString().data(),result.type());
788 //printf("\nnok[%s]\n",arg.toString().data());
795 //-----------------------------------------------------------------------------
797 /** @brief The implementation of the "append" filter */
801 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
803 if ((v.type()==TemplateVariant::String || v.type()==TemplateVariant::Integer) &&
804 arg.type()==TemplateVariant::String)
806 return TemplateVariant(v.toString() + arg.toString());
815 //-----------------------------------------------------------------------------
817 /** @brief The implementation of the "prepend" filter */
821 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
823 if ((v.type()==TemplateVariant::String || v.type()==TemplateVariant::Integer) &&
824 arg.type()==TemplateVariant::String)
826 return TemplateVariant(arg.toString() + v.toString());
835 //--------------------------------------------------------------------
837 /** @brief The implementation of the "length" filter */
841 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
845 return TemplateVariant();
847 if (v.type()==TemplateVariant::List)
849 return TemplateVariant(v.toList()->count());
851 else if (v.type()==TemplateVariant::String)
853 return TemplateVariant((int)v.toString().length());
857 return TemplateVariant();
862 //--------------------------------------------------------------------
864 /** @brief The implementation of the "default" filter */
868 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
874 else if (v.type()==TemplateVariant::String && v.toString().isEmpty())
885 //--------------------------------------------------------------------
887 /** @brief The implementation of the "flatten" filter */
891 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
893 if (!v.isValid() || v.type()!=TemplateVariant::List)
899 TemplateList *list = TemplateList::alloc();
900 flatten(v.toList(),list);
901 return TemplateVariant(list);
906 static void flatten(TemplateListIntf *tree,TemplateList *list)
908 TemplateListIntf::ConstIterator *it = tree->createIterator();
909 TemplateVariant item;
910 for (it->toFirst();(it->current(item));it->toNext())
912 TemplateStructIntf *s = item.toStruct();
916 // if s has "children" then recurse into the children
917 TemplateVariant children = s->get("children");
918 if (children.isValid() && children.type()==TemplateVariant::List)
920 flatten(children.toList(),list);
932 //--------------------------------------------------------------------
934 /** @brief The implementation of the "listsort" filter */
939 ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
941 TemplateVariant value;
943 class SortList : public QList<ListElem>
946 SortList() { setAutoDelete(TRUE); }
948 int compareValues(const ListElem *item1,const ListElem *item2) const
950 return qstrcmp(item1->key,item2->key);
954 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
956 if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
958 //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
959 TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
961 TemplateVariant item;
962 TemplateList *result = TemplateList::alloc();
964 // create list of items based on v using the data in args as a sort key
966 for (it->toFirst();(it->current(item));it->toNext())
968 TemplateStructIntf *s = item.toStruct();
971 QCString sortKey = determineSortKey(s,args.toString());
972 sortList.append(new ListElem(sortKey,item));
973 //printf("sortKey=%s\n",sortKey.data());
981 // add sorted items to the result list
982 QListIterator<ListElem> sit(sortList);
984 for (sit.toFirst();(elem=sit.current());++sit)
986 result->append(elem->value);
994 static QCString determineSortKey(TemplateStructIntf *s,const QCString &arg)
998 while ((i=arg.find("{{",p))!=-1)
1000 result+=arg.mid(p,i-p);
1001 int j=arg.find("}}",i+2);
1004 QCString var = arg.mid(i+2,j-i-2);
1005 TemplateVariant val=s->get(var);
1006 //printf("found argument %s value=%s\n",var.data(),val.toString().data());
1007 result+=val.toString();
1015 result+=arg.right(arg.length()-p);
1020 //--------------------------------------------------------------------
1022 /** @brief The implementation of the "groupBy" filter */
1027 ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
1029 TemplateVariant value;
1031 class SortList : public QList<ListElem>
1034 SortList() { setAutoDelete(TRUE); }
1036 int compareValues(const ListElem *item1,const ListElem *item2) const
1038 return qstrcmp(item1->key,item2->key);
1042 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1044 if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
1046 //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
1047 TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
1049 TemplateVariant item;
1050 TemplateList *result = TemplateList::alloc();
1052 // create list of items based on v using the data in args as a sort key
1054 for (it->toFirst();(it->current(item));it->toNext())
1056 TemplateStructIntf *s = item.toStruct();
1059 QCString sortKey = determineSortKey(s,args.toString());
1060 sortList.append(new ListElem(sortKey,item));
1061 //printf("sortKey=%s\n",sortKey.data());
1069 // add sorted items to the result list
1070 QListIterator<ListElem> sit(sortList);
1072 TemplateList *groupList=0;
1074 for (sit.toFirst();(elem=sit.current());++sit)
1076 if (groupList==0 || elem->key!=prevKey)
1078 groupList = TemplateList::alloc();
1079 result->append(groupList);
1080 prevKey = elem->key;
1082 groupList->append(elem->value);
1090 static QCString determineSortKey(TemplateStructIntf *s,const QCString &attribName)
1092 TemplateVariant v = s->get(attribName);
1093 return v.toString();
1097 //--------------------------------------------------------------------
1099 /** @brief The implementation of the "paginate" filter */
1100 class FilterPaginate
1103 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1105 if (v.isValid() && v.type()==TemplateVariant::List &&
1106 args.isValid() && args.type()==TemplateVariant::Integer)
1108 int pageSize = args.toInt();
1109 TemplateListIntf *list = v.toList();
1110 TemplateList *result = TemplateList::alloc();
1111 TemplateListIntf::ConstIterator *it = list->createIterator();
1112 TemplateVariant item;
1113 TemplateList *pageList=0;
1115 for (it->toFirst();(it->current(item));it->toNext())
1119 pageList = TemplateList::alloc();
1120 result->append(pageList);
1122 pageList->append(item);
1124 if (i==pageSize) // page is full start a new one
1133 else // wrong arguments
1140 //--------------------------------------------------------------------
1142 /** @brief The implementation of the "alphaIndex" filter */
1143 class FilterAlphaIndex
1148 ListElem(uint k,const TemplateVariant &v) : key(k), value(v) {}
1150 TemplateVariant value;
1152 class SortList : public QList<ListElem>
1155 SortList() { setAutoDelete(TRUE); }
1157 int compareValues(const ListElem *item1,const ListElem *item2) const
1159 return item1->key-item2->key;
1162 static QCString keyToLetter(uint startLetter)
1164 return QString(QChar(startLetter)).utf8();
1166 static QCString keyToLabel(uint startLetter)
1169 if (startLetter>0x20 && startLetter<=0x7f) // printable ASCII character
1171 s[0]=tolower((char)startLetter);
1176 const char hex[]="0123456789abcdef";
1180 if (startLetter>(1<<24)) // 4 byte character
1182 s[i++]=hex[(startLetter>>28)&0xf];
1183 s[i++]=hex[(startLetter>>24)&0xf];
1185 if (startLetter>(1<<16)) // 3 byte character
1187 s[i++]=hex[(startLetter>>20)&0xf];
1188 s[i++]=hex[(startLetter>>16)&0xf];
1190 if (startLetter>(1<<8)) // 2 byte character
1192 s[i++]=hex[(startLetter>>12)&0xf];
1193 s[i++]=hex[(startLetter>>8)&0xf];
1195 // one byte character
1196 s[i++]=hex[(startLetter>>4)&0xf];
1197 s[i++]=hex[(startLetter>>0)&0xf];
1202 static uint determineSortKey(TemplateStructIntf *s,const QCString &attribName)
1204 TemplateVariant v = s->get(attribName);
1205 int index = getPrefixIndex(v.toString());
1206 return getUtf8CodeToUpper(v.toString(),index);
1210 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1212 if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
1214 //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
1215 TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
1217 TemplateVariant item;
1218 TemplateList *result = TemplateList::alloc();
1220 // create list of items based on v using the data in args as a sort key
1222 for (it->toFirst();(it->current(item));it->toNext())
1224 TemplateStructIntf *s = item.toStruct();
1227 uint sortKey = determineSortKey(s,args.toString());
1228 sortList.append(new ListElem(sortKey,item));
1229 //printf("sortKey=%s\n",sortKey.data());
1237 // create an index from the sorted list
1239 QListIterator<ListElem> sit(sortList);
1241 TemplateStruct *indexNode = 0;
1242 TemplateList *indexList = 0;
1243 for (sit.toFirst();(elem=sit.current());++sit)
1245 if (letter!=elem->key || indexNode==0)
1247 // create new indexNode
1248 indexNode = TemplateStruct::alloc();
1249 indexList = TemplateList::alloc();
1250 indexNode->set("letter", keyToLetter(elem->key));
1251 indexNode->set("label", keyToLabel(elem->key));
1252 indexNode->set("items",indexList);
1253 result->append(indexNode);
1256 indexList->append(elem->value);
1264 //--------------------------------------------------------------------
1266 /** @brief The implementation of the "default" filter */
1267 class FilterStripPath
1270 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1272 if (!v.isValid() || v.type()!=TemplateVariant::String)
1276 QCString result = v.toString();
1277 int i=result.findRev('/');
1280 result=result.mid(i+1);
1282 i=result.findRev('\\');
1285 result=result.mid(i+1);
1291 //--------------------------------------------------------------------
1293 /** @brief The implementation of the "default" filter */
1297 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1299 if (!v.isValid() || v.type()!=TemplateVariant::String)
1303 QCString s = v.toString();
1304 return substitute(s," "," ");
1308 //--------------------------------------------------------------------
1310 /** @brief The implementation of the "divisibleby" filter */
1311 class FilterDivisibleBy
1314 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &n)
1316 if (!v.isValid() || !n.isValid())
1318 return TemplateVariant();
1320 if (v.type()==TemplateVariant::Integer && n.type()==TemplateVariant::Integer)
1322 return TemplateVariant((v.toInt()%n.toInt())==0);
1326 return TemplateVariant();
1332 //--------------------------------------------------------------------
1334 /** @brief Factory singleton for registering and creating filters */
1335 class TemplateFilterFactory
1338 typedef TemplateVariant (FilterFunction)(const TemplateVariant &v,const TemplateVariant &arg);
1340 static TemplateFilterFactory *instance()
1342 static TemplateFilterFactory *instance = 0;
1343 if (instance==0) instance = new TemplateFilterFactory;
1347 TemplateVariant apply(const QCString &name,const TemplateVariant &v,const TemplateVariant &arg, bool &ok)
1349 FilterFunction *func = (FilterFunction*)m_registry.find(name);
1353 return (*func)(v,arg);
1362 void registerFilter(const QCString &name,FilterFunction *func)
1364 m_registry.insert(name,(void*)func);
1367 /** @brief Helper class for registering a filter function */
1368 template<class T> class AutoRegister
1371 AutoRegister<T>(const QCString &key)
1373 TemplateFilterFactory::instance()->registerFilter(key,&T::apply);
1378 QDict<void> m_registry;
1381 // register a handlers for each filter we support
1382 static TemplateFilterFactory::AutoRegister<FilterAdd> fAdd("add");
1383 static TemplateFilterFactory::AutoRegister<FilterGet> fGet("get");
1384 static TemplateFilterFactory::AutoRegister<FilterAppend> fAppend("append");
1385 static TemplateFilterFactory::AutoRegister<FilterLength> fLength("length");
1386 static TemplateFilterFactory::AutoRegister<FilterNoWrap> fNoWrap("nowrap");
1387 static TemplateFilterFactory::AutoRegister<FilterFlatten> fFlatten("flatten");
1388 static TemplateFilterFactory::AutoRegister<FilterDefault> fDefault("default");
1389 static TemplateFilterFactory::AutoRegister<FilterPrepend> fPrepend("prepend");
1390 static TemplateFilterFactory::AutoRegister<FilterGroupBy> fGroupBy("groupBy");
1391 static TemplateFilterFactory::AutoRegister<FilterListSort> fListSort("listsort");
1392 static TemplateFilterFactory::AutoRegister<FilterPaginate> fPaginate("paginate");
1393 static TemplateFilterFactory::AutoRegister<FilterStripPath> fStripPath("stripPath");
1394 static TemplateFilterFactory::AutoRegister<FilterAlphaIndex> fAlphaIndex("alphaIndex");
1395 static TemplateFilterFactory::AutoRegister<FilterDivisibleBy> fDivisibleBy("divisibleby");
1397 //--------------------------------------------------------------------
1399 /** @brief Base class for all nodes in the abstract syntax tree of an
1405 virtual ~ExprAst() {}
1406 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(); }
1409 /** @brief Class representing a number in the AST */
1410 class ExprAstNumber : public ExprAst
1413 ExprAstNumber(int num) : m_number(num)
1414 { TRACE(("ExprAstNumber(%d)\n",num)); }
1415 int number() const { return m_number; }
1416 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_number); }
1421 /** @brief Class representing a variable in the AST */
1422 class ExprAstVariable : public ExprAst
1425 ExprAstVariable(const char *name) : m_name(name)
1426 { TRACE(("ExprAstVariable(%s)\n",name)); }
1427 const QCString &name() const { return m_name; }
1428 virtual TemplateVariant resolve(TemplateContext *c)
1430 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1431 TemplateVariant v = c->get(m_name);
1434 ci->warn(ci->templateName(),ci->line(),"undefined variable '%s' in expression",m_name.data());
1442 class ExprAstFunctionVariable : public ExprAst
1445 ExprAstFunctionVariable(ExprAst *var,const QList<ExprAst> &args)
1446 : m_var(var), m_args(args)
1447 { TRACE(("ExprAstFunctionVariable()\n"));
1448 m_args.setAutoDelete(TRUE);
1450 virtual TemplateVariant resolve(TemplateContext *c)
1452 QValueList<TemplateVariant> args;
1453 for (uint i=0;i<m_args.count();i++)
1455 TemplateVariant v = m_args.at(i)->resolve(c);
1458 TemplateVariant v = m_var->resolve(c);
1459 if (v.type()==TemplateVariant::Function)
1467 QList<ExprAst> m_args;
1470 /** @brief Class representing a filter in the AST */
1471 class ExprAstFilter : public ExprAst
1474 ExprAstFilter(const char *name,ExprAst *arg) : m_name(name), m_arg(arg)
1475 { TRACE(("ExprAstFilter(%s)\n",name)); }
1476 ~ExprAstFilter() { delete m_arg; }
1477 const QCString &name() const { return m_name; }
1478 TemplateVariant apply(const TemplateVariant &v,TemplateContext *c)
1480 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1481 TRACE(("Applying filter '%s' to '%s' (type=%d)\n",m_name.data(),v.toString().data(),v.type()));
1482 TemplateVariant arg;
1483 if (m_arg) arg = m_arg->resolve(c);
1485 TemplateVariant result = TemplateFilterFactory::instance()->apply(m_name,v,arg,ok);
1488 ci->warn(ci->templateName(),ci->line(),"unknown filter '%s'",m_name.data());
1497 /** @brief Class representing a filter applied to an expression in the AST */
1498 class ExprAstFilterAppl : public ExprAst
1501 ExprAstFilterAppl(ExprAst *expr,ExprAstFilter *filter)
1502 : m_expr(expr), m_filter(filter)
1503 { TRACE(("ExprAstFilterAppl\n")); }
1504 ~ExprAstFilterAppl() { delete m_expr; delete m_filter; }
1505 virtual TemplateVariant resolve(TemplateContext *c)
1507 return m_filter->apply(m_expr->resolve(c),c);
1511 ExprAstFilter *m_filter;
1514 /** @brief Class representing a string literal in the AST */
1515 class ExprAstLiteral : public ExprAst
1518 ExprAstLiteral(const char *lit) : m_literal(lit)
1519 { TRACE(("ExprAstLiteral(%s)\n",lit)); }
1520 const QCString &literal() const { return m_literal; }
1521 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_literal); }
1526 /** @brief Class representing a negation (not) operator in the AST */
1527 class ExprAstNegate : public ExprAst
1530 ExprAstNegate(ExprAst *expr) : m_expr(expr)
1531 { TRACE(("ExprAstNegate\n")); }
1532 ~ExprAstNegate() { delete m_expr; }
1533 virtual TemplateVariant resolve(TemplateContext *c)
1534 { return TemplateVariant(!m_expr->resolve(c).toBool()); }
1539 class ExprAstUnary : public ExprAst
1542 ExprAstUnary(Operator::Type op,ExprAst *exp) : m_operator(op), m_exp(exp)
1543 { TRACE(("ExprAstUnary %s\n",Operator::toString(op))); }
1544 ~ExprAstUnary() { delete m_exp; }
1545 virtual TemplateVariant resolve(TemplateContext *c)
1547 TemplateVariant exp = m_exp->resolve(c);
1550 case Operator::Minus:
1551 return -exp.toInt();
1553 return TemplateVariant();
1557 Operator::Type m_operator;
1561 /** @brief Class representing a binary operator in the AST */
1562 class ExprAstBinary : public ExprAst
1565 ExprAstBinary(Operator::Type op,ExprAst *lhs,ExprAst *rhs)
1566 : m_operator(op), m_lhs(lhs), m_rhs(rhs)
1567 { TRACE(("ExprAstBinary %s\n",Operator::toString(op))); }
1568 ~ExprAstBinary() { delete m_lhs; delete m_rhs; }
1569 virtual TemplateVariant resolve(TemplateContext *c)
1571 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1572 TemplateVariant lhs = m_lhs->resolve(c);
1573 TemplateVariant rhs = m_rhs ? m_rhs->resolve(c) : TemplateVariant();
1577 return TemplateVariant(lhs.toBool() || rhs.toBool());
1579 return TemplateVariant(lhs.toBool() && rhs.toBool());
1580 case Operator::Equal:
1581 return TemplateVariant(lhs == rhs);
1582 case Operator::NotEqual:
1583 return TemplateVariant(!(lhs == rhs));
1584 case Operator::Less:
1585 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1587 return lhs.toString()<rhs.toString();
1591 return lhs.toInt()<rhs.toInt();
1593 case Operator::Greater:
1594 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1596 return !(lhs.toString()<rhs.toString());
1600 return lhs.toInt()>rhs.toInt();
1602 case Operator::LessEqual:
1603 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1605 return lhs.toString()==rhs.toString() || lhs.toString()<rhs.toString();
1609 return lhs.toInt()<=rhs.toInt();
1611 case Operator::GreaterEqual:
1612 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1614 return lhs.toString()==rhs.toString() || !(lhs.toString()<rhs.toString());
1618 return lhs.toInt()>=rhs.toInt();
1620 case Operator::Plus:
1622 return TemplateVariant(lhs.toInt() + rhs.toInt());
1624 case Operator::Minus:
1626 return TemplateVariant(lhs.toInt() - rhs.toInt());
1628 case Operator::Multiply:
1630 return TemplateVariant(lhs.toInt() * rhs.toInt());
1632 case Operator::Divide:
1634 int denom = rhs.toInt();
1637 return TemplateVariant(lhs.toInt() / denom);
1639 else // divide by zero
1641 ci->warn(ci->templateName(),ci->line(),"division by zero while evaluating expression is undefined");
1645 case Operator::Modulo:
1647 int denom = rhs.toInt();
1650 return TemplateVariant(lhs.toInt() % denom);
1654 ci->warn(ci->templateName(),ci->line(),"modulo zero while evaluating expression is undefined");
1659 return TemplateVariant();
1663 Operator::Type m_operator;
1668 //----------------------------------------------------------
1670 /** @brief Base class of all nodes in a template's AST */
1674 TemplateNode(TemplateNode *parent) : m_parent(parent) {}
1675 virtual ~TemplateNode() {}
1677 virtual void render(FTextStream &ts, TemplateContext *c) = 0;
1679 TemplateNode *parent() { return m_parent; }
1682 TemplateNode *m_parent;
1685 //----------------------------------------------------------
1687 /** @brief Parser for templates */
1688 class TemplateParser
1691 TemplateParser(const TemplateEngine *engine,
1692 const QCString &templateName,QList<TemplateToken> &tokens);
1693 void parse(TemplateNode *parent,int line,const QStrList &stopAt,
1694 QList<TemplateNode> &nodes);
1695 bool hasNextToken() const;
1696 TemplateToken *takeNextToken();
1697 void removeNextToken();
1698 void prependToken(const TemplateToken *token);
1699 const TemplateToken *currentToken() const;
1700 QCString templateName() const { return m_templateName; }
1701 void warn(const char *fileName,int line,const char *fmt,...) const;
1703 const TemplateEngine *m_engine;
1704 QCString m_templateName;
1705 QList<TemplateToken> &m_tokens;
1708 //--------------------------------------------------------------------
1710 /** @brief Recursive decent parser for Django style template expressions.
1712 class ExpressionParser
1715 ExpressionParser(const TemplateParser *parser,int line)
1716 : m_parser(parser), m_line(line), m_tokenStream(0)
1719 virtual ~ExpressionParser()
1723 ExprAst *parse(const char *expr)
1725 if (expr==0) return 0;
1726 m_tokenStream = expr;
1728 return parseExpression();
1733 /** @brief Class representing a token within an expression. */
1737 ExprToken() : type(Unknown), num(-1), op(Operator::Or)
1742 Unknown, Operator, Number, Identifier, Literal
1751 ExprAst *parseExpression()
1753 TRACE(("{parseExpression(%s)\n",m_tokenStream));
1754 ExprAst *result = parseOrExpression();
1755 TRACE(("}parseExpression(%s)\n",m_tokenStream));
1759 ExprAst *parseOrExpression()
1761 TRACE(("{parseOrExpression(%s)\n",m_tokenStream));
1762 ExprAst *lhs = parseAndExpression();
1765 while (m_curToken.type==ExprToken::Operator &&
1766 m_curToken.op==Operator::Or)
1769 ExprAst *rhs = parseAndExpression();
1770 lhs = new ExprAstBinary(Operator::Or,lhs,rhs);
1773 TRACE(("}parseOrExpression(%s)\n",m_tokenStream));
1777 ExprAst *parseAndExpression()
1779 TRACE(("{parseAndExpression(%s)\n",m_tokenStream));
1780 ExprAst *lhs = parseNotExpression();
1783 while (m_curToken.type==ExprToken::Operator &&
1784 m_curToken.op==Operator::And)
1787 ExprAst *rhs = parseNotExpression();
1788 lhs = new ExprAstBinary(Operator::And,lhs,rhs);
1791 TRACE(("}parseAndExpression(%s)\n",m_tokenStream));
1795 ExprAst *parseNotExpression()
1797 TRACE(("{parseNotExpression(%s)\n",m_tokenStream));
1799 if (m_curToken.type==ExprToken::Operator &&
1800 m_curToken.op==Operator::Not)
1803 ExprAst *expr = parseCompareExpression();
1806 warn(m_parser->templateName(),m_line,"argument missing for not operator");
1809 result = new ExprAstNegate(expr);
1813 result = parseCompareExpression();
1815 TRACE(("}parseNotExpression(%s)\n",m_tokenStream));
1819 ExprAst *parseCompareExpression()
1821 TRACE(("{parseCompareExpression(%s)\n",m_tokenStream));
1822 ExprAst *lhs = parseAdditiveExpression();
1825 Operator::Type op = m_curToken.op;
1826 if (m_curToken.type==ExprToken::Operator &&
1827 (op==Operator::Less ||
1828 op==Operator::Greater ||
1829 op==Operator::Equal ||
1830 op==Operator::NotEqual ||
1831 op==Operator::LessEqual ||
1832 op==Operator::GreaterEqual
1837 ExprAst *rhs = parseNotExpression();
1838 lhs = new ExprAstBinary(op,lhs,rhs);
1841 TRACE(("}parseCompareExpression(%s)\n",m_tokenStream));
1845 ExprAst *parseAdditiveExpression()
1847 TRACE(("{parseAdditiveExpression(%s)\n",m_tokenStream));
1848 ExprAst *lhs = parseMultiplicativeExpression();
1851 while (m_curToken.type==ExprToken::Operator &&
1852 (m_curToken.op==Operator::Plus || m_curToken.op==Operator::Minus))
1854 Operator::Type op = m_curToken.op;
1856 ExprAst *rhs = parseMultiplicativeExpression();
1857 lhs = new ExprAstBinary(op,lhs,rhs);
1860 TRACE(("}parseAdditiveExpression(%s)\n",m_tokenStream));
1864 ExprAst *parseMultiplicativeExpression()
1866 TRACE(("{parseMultiplicativeExpression(%s)\n",m_tokenStream));
1867 ExprAst *lhs = parseUnaryExpression();
1870 while (m_curToken.type==ExprToken::Operator &&
1871 (m_curToken.op==Operator::Multiply || m_curToken.op==Operator::Divide || m_curToken.op==Operator::Modulo))
1873 Operator::Type op = m_curToken.op;
1875 ExprAst *rhs = parseUnaryExpression();
1876 lhs = new ExprAstBinary(op,lhs,rhs);
1879 TRACE(("}parseMultiplicativeExpression(%s)\n",m_tokenStream));
1883 ExprAst *parseUnaryExpression()
1885 TRACE(("{parseUnaryExpression(%s)\n",m_tokenStream));
1887 if (m_curToken.type==ExprToken::Operator)
1889 if (m_curToken.op==Operator::Plus)
1892 result = parsePrimaryExpression();
1894 else if (m_curToken.op==Operator::Minus)
1897 ExprAst *rhs = parsePrimaryExpression();
1898 result = new ExprAstUnary(m_curToken.op,rhs);
1902 result = parsePrimaryExpression();
1907 result = parsePrimaryExpression();
1909 TRACE(("}parseUnaryExpression(%s)\n",m_tokenStream));
1913 ExprAst *parsePrimaryExpression()
1915 TRACE(("{parsePrimary(%s)\n",m_tokenStream));
1917 switch (m_curToken.type)
1919 case ExprToken::Number:
1920 result = parseNumber();
1922 case ExprToken::Identifier:
1923 result = parseFilteredVariable();
1925 case ExprToken::Literal:
1926 result = parseLiteral();
1928 case ExprToken::Operator:
1929 if (m_curToken.op==Operator::LeftParen)
1931 getNextToken(); // skip over opening bracket
1932 result = parseExpression();
1933 if (m_curToken.type!=ExprToken::Operator ||
1934 m_curToken.op!=Operator::RightParen)
1936 warn(m_parser->templateName(),m_line,"missing closing parenthesis");
1940 getNextToken(); // skip over closing bracket
1945 warn(m_parser->templateName(),m_line,"unexpected operator '%s' in expression",
1946 Operator::toString(m_curToken.op));
1950 warn(m_parser->templateName(),m_line,"unexpected token in expression");
1952 TRACE(("}parsePrimary(%s)\n",m_tokenStream));
1956 ExprAst *parseNumber()
1958 TRACE(("{parseNumber(%d)\n",m_curToken.num));
1959 ExprAst *num = new ExprAstNumber(m_curToken.num);
1961 TRACE(("}parseNumber()\n"));
1965 ExprAst *parseIdentifier()
1967 TRACE(("{parseIdentifier(%s)\n",m_curToken.id.data()));
1968 ExprAst *id = new ExprAstVariable(m_curToken.id);
1970 TRACE(("}parseIdentifier()\n"));
1974 ExprAst *parseLiteral()
1976 TRACE(("{parseLiteral(%s)\n",m_curToken.id.data()));
1977 ExprAst *lit = new ExprAstLiteral(m_curToken.id);
1979 TRACE(("}parseLiteral()\n"));
1983 ExprAst *parseIdentifierOptionalArgs()
1985 TRACE(("{parseIdentifierOptionalArgs(%s)\n",m_curToken.id.data()));
1986 ExprAst *expr = parseIdentifier();
1989 if (m_curToken.type==ExprToken::Operator &&
1990 m_curToken.op==Operator::Colon)
1993 ExprAst *argExpr = parsePrimaryExpression();
1994 QList<ExprAst> args;
1995 args.append(argExpr);
1996 while (m_curToken.type==ExprToken::Operator &&
1997 m_curToken.op==Operator::Comma)
2000 argExpr = parsePrimaryExpression();
2001 args.append(argExpr);
2003 expr = new ExprAstFunctionVariable(expr,args);
2006 TRACE(("}parseIdentifierOptionalArgs()\n"));
2010 ExprAst *parseFilteredVariable()
2012 TRACE(("{parseFilteredVariable()\n"));
2013 ExprAst *expr = parseIdentifierOptionalArgs();
2016 while (m_curToken.type==ExprToken::Operator &&
2017 m_curToken.op==Operator::Filter)
2020 ExprAstFilter *filter = parseFilter();
2022 expr = new ExprAstFilterAppl(expr,filter);
2025 TRACE(("}parseFilteredVariable()\n"));
2029 ExprAstFilter *parseFilter()
2031 TRACE(("{parseFilter(%s)\n",m_curToken.id.data()));
2032 QCString filterName = m_curToken.id;
2035 if (m_curToken.type==ExprToken::Operator &&
2036 m_curToken.op==Operator::Colon)
2039 argExpr = parsePrimaryExpression();
2041 ExprAstFilter *filter = new ExprAstFilter(filterName,argExpr);
2042 TRACE(("}parseFilter()\n"));
2049 const char *p = m_tokenStream;
2052 if (p==0 || *p=='\0') return FALSE;
2053 while (*p==' ') p++; // skip over spaces
2059 if (c=='=' && *(p+1)=='=') // equal
2061 m_curToken.op = Operator::Equal;
2066 if (c=='!' && *(p+1)=='=') // not equal
2068 m_curToken.op = Operator::NotEqual;
2073 if (c=='<' && *(p+1)=='=') // less or equal
2075 m_curToken.op = Operator::LessEqual;
2080 m_curToken.op = Operator::Less;
2085 if (c=='>' && *(p+1)=='=') // greater or equal
2087 m_curToken.op = Operator::GreaterEqual;
2092 m_curToken.op = Operator::Greater;
2097 m_curToken.op = Operator::LeftParen;
2101 m_curToken.op = Operator::RightParen;
2105 m_curToken.op = Operator::Filter;
2109 m_curToken.op = Operator::Plus;
2113 m_curToken.op = Operator::Minus;
2117 m_curToken.op = Operator::Multiply;
2121 m_curToken.op = Operator::Divide;
2125 m_curToken.op = Operator::Modulo;
2129 m_curToken.op = Operator::Colon;
2133 m_curToken.op = Operator::Comma;
2137 if (strncmp(p,"not ",4)==0)
2139 m_curToken.op = Operator::Not;
2144 if (strncmp(p,"and ",4)==0)
2146 m_curToken.op = Operator::And;
2151 if (strncmp(p,"or ",3)==0)
2153 m_curToken.op = Operator::Or;
2160 if (p!=q) // found an operator
2162 m_curToken.type = ExprToken::Operator;
2164 else // no token found yet
2166 if (c>='0' && c<='9') // number?
2168 m_curToken.type = ExprToken::Number;
2171 while (*np>='0' && *np<='9')
2174 m_curToken.num+=*np-'0';
2179 else if (c=='_' || (c>='a' && c<='z') || (c>='A' && c<='Z')) // identifier?
2181 m_curToken.type = ExprToken::Identifier;
2186 (c=='_' || c=='.' ||
2187 (c>='a' && c<='z') ||
2188 (c>='A' && c<='Z') ||
2196 if (m_curToken.id=="True") // treat true literal as numerical 1
2198 m_curToken.type = ExprToken::Number;
2201 else if (m_curToken.id=="False") // treat false literal as numerical 0
2203 m_curToken.type = ExprToken::Number;
2207 else if (c=='"' || c=='\'') // string literal
2209 m_curToken.type = ExprToken::Literal;
2210 m_curToken.id.resize(0);
2214 while ((c=*p) && (c!=tokenChar || (c==tokenChar && cp=='\\')))
2217 if (c!='\\' || cp=='\\') // don't add escapes
2224 if (*p==tokenChar) p++;
2227 if (p==q) // still no valid token found -> error
2229 m_curToken.type = ExprToken::Unknown;
2233 warn(m_parser->templateName(),m_line,"Found unknown token %s while parsing %s",s,m_tokenStream);
2237 //TRACE(("token type=%d op=%d num=%d id=%s\n",
2238 // m_curToken.type,m_curToken.op,m_curToken.num,m_curToken.id.data()));
2244 const TemplateParser *m_parser;
2245 ExprToken m_curToken;
2247 const char *m_tokenStream;
2250 //----------------------------------------------------------
2252 /** @brief Class representing a lexical token in a template */
2256 enum Type { Text, Variable, Block };
2257 TemplateToken(Type t,const char *d,int l) : type(t), data(d), line(l) {}
2263 //----------------------------------------------------------
2265 /** @brief Class representing a list of AST nodes in a template */
2266 class TemplateNodeList : public QList<TemplateNode>
2271 setAutoDelete(TRUE);
2273 void render(FTextStream &ts,TemplateContext *c)
2275 TRACE(("{TemplateNodeList::render\n"));
2276 QListIterator<TemplateNode> it(*this);
2278 for (it.toFirst();(tn=it.current());++it)
2282 TRACE(("}TemplateNodeList::render\n"));
2286 //----------------------------------------------------------
2288 /** @brief Internal class representing the implementation of a template */
2289 class TemplateImpl : public TemplateNode, public Template
2292 TemplateImpl(TemplateEngine *e,const QCString &name,const QCString &data);
2293 void render(FTextStream &ts, TemplateContext *c);
2295 TemplateEngine *engine() const { return m_engine; }
2296 TemplateBlockContext *blockContext() { return &m_blockContext; }
2299 TemplateEngine *m_engine;
2301 TemplateNodeList m_nodes;
2302 TemplateBlockContext m_blockContext;
2305 //----------------------------------------------------------
2308 TemplateContextImpl::TemplateContextImpl(const TemplateEngine *e)
2309 : m_engine(e), m_templateName("<unknown>"), m_line(1), m_activeEscapeIntf(0),
2310 m_spacelessIntf(0), m_spacelessEnabled(FALSE), m_indices(TemplateStruct::alloc())
2312 m_indexStacks.setAutoDelete(TRUE);
2313 m_contextStack.setAutoDelete(TRUE);
2314 m_escapeIntfDict.setAutoDelete(TRUE);
2316 set("index",m_indices.get());
2319 TemplateContextImpl::~TemplateContextImpl()
2324 void TemplateContextImpl::set(const char *name,const TemplateVariant &v)
2326 TemplateVariant *pv = m_contextStack.getFirst()->find(name);
2329 m_contextStack.getFirst()->remove(name);
2331 m_contextStack.getFirst()->insert(name,new TemplateVariant(v));
2334 TemplateVariant TemplateContextImpl::get(const QCString &name) const
2336 int i=name.find('.');
2337 if (i==-1) // simple name
2339 return getPrimary(name);
2344 QCString objName = name.left(i);
2345 v = getPrimary(objName);
2346 QCString propName = name.mid(i+1);
2347 while (!propName.isEmpty())
2349 //printf("getPrimary(%s) type=%d:%s\n",objName.data(),v.type(),v.toString().data());
2350 if (v.type()==TemplateVariant::Struct)
2352 i = propName.find(".");
2353 int l = i==-1 ? propName.length() : i;
2354 v = v.toStruct()->get(propName.left(l));
2357 warn(m_templateName,m_line,"requesting non-existing property '%s' for object '%s'",propName.left(l).data(),objName.data());
2361 objName = propName.left(i);
2362 propName = propName.mid(i+1);
2369 else if (v.type()==TemplateVariant::List)
2371 i = propName.find(".");
2372 int l = i==-1 ? propName.length() : i;
2374 int index = propName.left(l).toInt(&b);
2377 v = v.toList()->at(index);
2381 warn(m_templateName,m_line,"list index '%s' is not valid",propName.data());
2386 propName = propName.mid(i+1);
2395 warn(m_templateName,m_line,"using . on an object '%s' is not an struct or list",objName.data());
2396 return TemplateVariant();
2403 const TemplateVariant *TemplateContextImpl::getRef(const QCString &name) const
2405 QListIterator< QDict<TemplateVariant> > it(m_contextStack);
2406 QDict<TemplateVariant> *dict;
2407 for (it.toFirst();(dict=it.current());++it)
2409 TemplateVariant *v = dict->find(name);
2412 return 0; // not found
2415 TemplateVariant TemplateContextImpl::getPrimary(const QCString &name) const
2417 const TemplateVariant *v = getRef(name);
2418 return v ? *v : TemplateVariant();
2421 void TemplateContextImpl::push()
2423 QDict<TemplateVariant> *dict = new QDict<TemplateVariant>;
2424 dict->setAutoDelete(TRUE);
2425 m_contextStack.prepend(dict);
2428 void TemplateContextImpl::pop()
2430 if (!m_contextStack.removeFirst())
2432 warn(m_templateName,m_line,"pop() called on empty context stack!\n");
2436 TemplateBlockContext *TemplateContextImpl::blockContext()
2438 return &m_blockContext;
2441 void TemplateContextImpl::warn(const char *fileName,int line,const char *fmt,...) const
2445 va_warn(fileName,line,fmt,args);
2447 m_engine->printIncludeContext(fileName,line);
2450 void TemplateContextImpl::openSubIndex(const QCString &indexName)
2452 //printf("TemplateContextImpl::openSubIndex(%s)\n",indexName.data());
2453 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2454 if (!stack || stack->isEmpty() || stack->top()->type()==TemplateVariant::List) // error: no stack yet or no entry
2456 warn(m_templateName,m_line,"opensubindex for index %s without preceding indexentry",indexName.data());
2459 // get the parent entry to add the list to
2460 TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2463 // add new list to the stack
2464 TemplateList *list = TemplateList::alloc();
2465 stack->push(new TemplateVariant(list));
2466 entry->set("children",list);
2467 entry->set("is_leaf_node",false);
2471 void TemplateContextImpl::closeSubIndex(const QCString &indexName)
2473 //printf("TemplateContextImpl::closeSubIndex(%s)\n",indexName.data());
2474 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2475 if (!stack || stack->count()<3)
2477 warn(m_templateName,m_line,"closesubindex for index %s without matching open",indexName.data());
2479 else // stack->count()>=2
2481 if (stack->top()->type()==TemplateVariant::Struct)
2483 delete stack->pop(); // pop struct
2484 delete stack->pop(); // pop list
2486 else // empty list! correct "is_left_node" attribute of the parent entry
2488 delete stack->pop(); // pop list
2489 TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2492 entry->set("is_leaf_node",true);
2498 static void getPathListFunc(TemplateStructIntf *entry,TemplateList *list)
2500 TemplateVariant parent = entry->get("parent");
2501 if (parent.type()==TemplateVariant::Struct)
2503 getPathListFunc(parent.toStruct(),list);
2505 list->append(entry);
2508 static TemplateVariant getPathFunc(const void *ctx, const QValueList<TemplateVariant> &)
2510 TemplateStruct *entry = (TemplateStruct*)ctx;
2511 TemplateList *result = TemplateList::alloc();
2512 getPathListFunc(entry,result);
2516 void TemplateContextImpl::addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments)
2518 QValueListConstIterator<TemplateKeyValue> it = arguments.begin();
2519 //printf("TemplateContextImpl::addIndexEntry(%s)\n",indexName.data());
2520 //while (it!=arguments.end())
2522 // printf(" key=%s value=%s\n",(*it).key.data(),(*it).value.toString().data());
2525 TemplateVariant parent(FALSE);
2526 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2527 if (!stack) // no stack yet, create it!
2529 stack = new QStack<TemplateVariant>;
2530 stack->setAutoDelete(TRUE);
2531 m_indexStacks.insert(indexName,stack);
2533 TemplateList *list = 0;
2534 if (stack->isEmpty()) // first item, create empty list and add it to the index
2536 list = TemplateList::alloc();
2537 stack->push(new TemplateVariant(list));
2538 m_indices->set(indexName,list); // make list available under index
2540 else // stack not empty
2542 if (stack->top()->type()==TemplateVariant::Struct) // already an entry in the list
2544 // remove current entry from the stack
2545 delete stack->pop();
2547 else // first entry after opensubindex
2549 ASSERT(stack->top()->type()==TemplateVariant::List);
2551 if (stack->count()>1)
2553 TemplateVariant *tmp = stack->pop();
2554 parent = *stack->top();
2556 ASSERT(parent.type()==TemplateVariant::Struct);
2558 // get list to add new item
2559 list = dynamic_cast<TemplateList*>(stack->top()->toList());
2561 TemplateStruct *entry = TemplateStruct::alloc();
2562 // add user specified fields to the entry
2563 for (it=arguments.begin();it!=arguments.end();++it)
2565 entry->set((*it).key,(*it).value);
2567 if (list->count()>0)
2569 TemplateStruct *lastEntry = dynamic_cast<TemplateStruct*>(list->at(list->count()-1).toStruct());
2570 lastEntry->set("last",false);
2572 entry->set("is_leaf_node",true);
2573 entry->set("first",list->count()==0);
2574 entry->set("index",list->count());
2575 entry->set("parent",parent);
2576 entry->set("path",TemplateVariant::Delegate::fromFunction(entry,getPathFunc));
2577 entry->set("last",true);
2578 stack->push(new TemplateVariant(entry));
2579 list->append(entry);
2582 //----------------------------------------------------------
2584 /** @brief Class representing a piece of plain text in a template */
2585 class TemplateNodeText : public TemplateNode
2588 TemplateNodeText(TemplateParser *,TemplateNode *parent,int,const QCString &data)
2589 : TemplateNode(parent), m_data(data)
2591 TRACE(("TemplateNodeText('%s')\n",replace(data,'\n',' ').data()));
2594 void render(FTextStream &ts, TemplateContext *c)
2596 //printf("TemplateNodeText::render(%s)\n",m_data.data());
2597 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2598 if (ci->spacelessEnabled())
2600 ts << ci->spacelessIntf()->remove(m_data);
2611 //----------------------------------------------------------
2613 /** @brief Class representing a variable in a template */
2614 class TemplateNodeVariable : public TemplateNode
2617 TemplateNodeVariable(TemplateParser *parser,TemplateNode *parent,int line,const QCString &var)
2618 : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line)
2620 TRACE(("TemplateNodeVariable(%s)\n",var.data()));
2621 ExpressionParser expParser(parser,line);
2622 m_var = expParser.parse(var);
2625 parser->warn(m_templateName,line,"invalid expression '%s' for variable",var.data());
2628 ~TemplateNodeVariable()
2633 void render(FTextStream &ts, TemplateContext *c)
2635 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2636 ci->setLocation(m_templateName,m_line);
2639 TemplateVariant v = m_var->resolve(c);
2640 if (v.type()==TemplateVariant::Function)
2642 v = v.call(QValueList<TemplateVariant>());
2644 //printf("TemplateNodeVariable::render(%s) raw=%d\n",value.data(),v.raw());
2645 if (ci->escapeIntf() && !v.raw())
2647 ts << ci->escapeIntf()->escape(v.toString());
2657 QCString m_templateName;
2660 QList<ExprAst> m_args;
2663 //----------------------------------------------------------
2665 /** @brief Helper class for creating template AST tag nodes and returning
2666 * the template for a given node.
2668 template<class T> class TemplateNodeCreator : public TemplateNode
2671 TemplateNodeCreator(TemplateParser *parser,TemplateNode *parent,int line)
2672 : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line) {}
2673 static TemplateNode *createInstance(TemplateParser *parser,
2674 TemplateNode *parent,
2676 const QCString &data)
2678 return new T(parser,parent,line,data);
2680 TemplateImpl *getTemplate()
2682 TemplateNode *root = this;
2683 while (root && root->parent())
2685 root = root->parent();
2687 return dynamic_cast<TemplateImpl*>(root);
2690 QCString m_templateName;
2694 //----------------------------------------------------------
2696 /** @brief Class representing an 'if' tag in a template */
2697 class TemplateNodeIf : public TemplateNodeCreator<TemplateNodeIf>
2700 TemplateNodeIf(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) :
2701 TemplateNodeCreator<TemplateNodeIf>(parser,parent,line)
2703 m_ifGuardedNodes.setAutoDelete(TRUE);
2704 TRACE(("{TemplateNodeIf(%s)\n",data.data()));
2707 parser->warn(m_templateName,line,"missing argument for if tag");
2710 stopAt.append("endif");
2711 stopAt.append("elif");
2712 stopAt.append("else");
2715 GuardedNodes *guardedNodes = new GuardedNodes;
2716 ExpressionParser ex(parser,line);
2717 guardedNodes->line = line;
2718 guardedNodes->guardAst = ex.parse(data);
2719 parser->parse(this,line,stopAt,guardedNodes->trueNodes);
2720 m_ifGuardedNodes.append(guardedNodes);
2721 TemplateToken *tok = parser->takeNextToken();
2724 while (tok && tok->data.left(5)=="elif ")
2726 ExpressionParser ex(parser,line);
2727 guardedNodes = new GuardedNodes;
2728 guardedNodes->line = tok->line;
2729 guardedNodes->guardAst = ex.parse(tok->data.mid(5));
2730 parser->parse(this,tok->line,stopAt,guardedNodes->trueNodes);
2731 m_ifGuardedNodes.append(guardedNodes);
2732 // proceed to the next token
2734 tok = parser->takeNextToken();
2738 if (tok && tok->data=="else")
2740 stopAt.removeLast(); // remove "else"
2741 stopAt.removeLast(); // remove "elif"
2742 parser->parse(this,line,stopAt,m_falseNodes);
2743 parser->removeNextToken(); // skip over endif
2746 TRACE(("}TemplateNodeIf(%s)\n",data.data()));
2752 void render(FTextStream &ts, TemplateContext *c)
2754 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2755 ci->setLocation(m_templateName,m_line);
2756 //printf("TemplateNodeIf::render #trueNodes=%d #falseNodes=%d\n",m_trueNodes.count(),m_falseNodes.count());
2757 bool processed=FALSE;
2758 QListIterator<GuardedNodes> li(m_ifGuardedNodes);
2759 GuardedNodes *nodes;
2760 for (li.toFirst();(nodes=li.current()) && !processed;++li)
2762 if (nodes->guardAst)
2764 TemplateVariant guardValue = nodes->guardAst->resolve(c);
2765 if (guardValue.toBool()) // render nodes for the first guard that evaluated to 'true'
2767 nodes->trueNodes.render(ts,c);
2773 ci->warn(m_templateName,nodes->line,"invalid expression for if/elif");
2778 // all guards are false, render 'else' nodes
2779 m_falseNodes.render(ts,c);
2785 GuardedNodes() : guardAst(0) {}
2786 ~GuardedNodes() { delete guardAst; }
2789 TemplateNodeList trueNodes;
2791 QList<GuardedNodes> m_ifGuardedNodes;
2792 TemplateNodeList m_falseNodes;
2795 //----------------------------------------------------------
2796 /** @brief Class representing a 'for' tag in a template */
2797 class TemplateNodeRepeat : public TemplateNodeCreator<TemplateNodeRepeat>
2800 TemplateNodeRepeat(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2801 : TemplateNodeCreator<TemplateNodeRepeat>(parser,parent,line)
2803 TRACE(("{TemplateNodeRepeat(%s)\n",data.data()));
2804 ExpressionParser expParser(parser,line);
2805 m_expr = expParser.parse(data);
2807 stopAt.append("endrepeat");
2808 parser->parse(this,line,stopAt,m_repeatNodes);
2809 parser->removeNextToken(); // skip over endrepeat
2810 TRACE(("}TemplateNodeRepeat(%s)\n",data.data()));
2812 ~TemplateNodeRepeat()
2816 void render(FTextStream &ts, TemplateContext *c)
2818 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2819 ci->setLocation(m_templateName,m_line);
2821 if (m_expr && (v=m_expr->resolve(c)).type()==TemplateVariant::Integer)
2823 int i, n = v.toInt();
2826 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2827 s->set("counter0", (int)i);
2828 s->set("counter", (int)(i+1));
2829 s->set("revcounter", (int)(n-i));
2830 s->set("revcounter0", (int)(n-i-1));
2831 s->set("first",i==0);
2832 s->set("last", i==n-1);
2833 c->set("repeatloop",s.get());
2834 // render all items for this iteration of the loop
2835 m_repeatNodes.render(ts,c);
2838 else // simple type...
2840 ci->warn(m_templateName,m_line,"for requires a variable of list type!");
2844 TemplateNodeList m_repeatNodes;
2848 //----------------------------------------------------------
2850 /** @brief Class representing a 'range' tag in a template */
2851 class TemplateNodeRange : public TemplateNodeCreator<TemplateNodeRange>
2854 TemplateNodeRange(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2855 : TemplateNodeCreator<TemplateNodeRange>(parser,parent,line)
2857 TRACE(("{TemplateNodeRange(%s)\n",data.data()));
2859 int i1 = data.find(" from ");
2860 int i2 = data.find(" to ");
2861 int i3 = data.find(" downto ");
2864 if (data.right(5)==" from")
2866 parser->warn(m_templateName,line,"range missing after 'from' keyword");
2868 else if (data=="from")
2870 parser->warn(m_templateName,line,"range needs an iterator variable and a range");
2874 parser->warn(m_templateName,line,"range is missing 'from' keyword");
2877 else if (i2==-1 && i3==-1)
2879 if (data.right(3)==" to")
2881 parser->warn(m_templateName,line,"range is missing end value after 'to' keyword");
2883 else if (data.right(7)==" downto")
2885 parser->warn(m_templateName,line,"range is missing end value after 'downto' keyword");
2889 parser->warn(m_templateName,line,"range is missing 'to' or 'downto' keyword");
2894 m_var = data.left(i1).stripWhiteSpace();
2895 if (m_var.isEmpty())
2897 parser->warn(m_templateName,line,"range needs an iterator variable");
2899 start = data.mid(i1+6,i2-i1-6).stripWhiteSpace();
2902 end = data.right(data.length()-i2-4).stripWhiteSpace();
2907 end = data.right(data.length()-i3-8).stripWhiteSpace();
2911 ExpressionParser expParser(parser,line);
2912 m_startExpr = expParser.parse(start);
2913 m_endExpr = expParser.parse(end);
2916 stopAt.append("endrange");
2917 parser->parse(this,line,stopAt,m_loopNodes);
2918 parser->removeNextToken(); // skip over endrange
2919 TRACE(("}TemplateNodeRange(%s)\n",data.data()));
2922 ~TemplateNodeRange()
2928 void render(FTextStream &ts, TemplateContext *c)
2930 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2931 ci->setLocation(m_templateName,m_line);
2932 //printf("TemplateNodeRange::render #loopNodes=%d\n",
2933 // m_loopNodes.count());
2934 if (m_startExpr && m_endExpr)
2936 TemplateVariant vs = m_startExpr->resolve(c);
2937 TemplateVariant ve = m_endExpr->resolve(c);
2938 if (vs.type()==TemplateVariant::Integer && ve.type()==TemplateVariant::Integer)
2942 int l = m_down ? s-e+1 : e-s+1;
2946 //int index = m_reversed ? list.count() : 0;
2948 const TemplateVariant *parentLoop = c->getRef("forloop");
2950 int i = m_down ? e : s;
2954 // set the forloop meta-data variable
2955 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2956 s->set("counter0", (int)index);
2957 s->set("counter", (int)(index+1));
2958 s->set("revcounter", (int)(l-index));
2959 s->set("revcounter0", (int)(l-index-1));
2960 s->set("first",index==0);
2961 s->set("last", (int)index==l-1);
2962 s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
2963 c->set("forloop",s.get());
2965 // set the iterator variable
2968 // render all items for this iteration of the loop
2969 m_loopNodes.render(ts,c);
2987 ci->warn(m_templateName,m_line,"range %d %s %d is empty!",
2988 s,m_down?"downto":"to",e);
2991 else if (vs.type()!=TemplateVariant::Integer)
2993 ci->warn(m_templateName,m_line,"range requires a start value of integer type!");
2995 else if (ve.type()!=TemplateVariant::Integer)
2997 ci->warn(m_templateName,m_line,"range requires an end value of integer type!");
3000 else if (!m_startExpr)
3002 ci->warn(m_templateName,m_line,"range has empty start value");
3004 else if (!m_endExpr)
3006 ci->warn(m_templateName,m_line,"range has empty end value");
3012 ExprAst *m_startExpr;
3015 TemplateNodeList m_loopNodes;
3018 //----------------------------------------------------------
3020 /** @brief Class representing a 'for' tag in a template */
3021 class TemplateNodeFor : public TemplateNodeCreator<TemplateNodeFor>
3024 TemplateNodeFor(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3025 : TemplateNodeCreator<TemplateNodeFor>(parser,parent,line)
3027 TRACE(("{TemplateNodeFor(%s)\n",data.data()));
3029 int i = data.find(" in ");
3032 if (data.right(3)==" in")
3034 parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
3036 else if (data=="in")
3038 parser->warn(m_templateName,line,"for needs at least one iterator variable");
3042 parser->warn(m_templateName,line,"for is missing 'in' keyword");
3047 m_vars = split(data.left(i),",");
3048 if (m_vars.count()==0)
3050 parser->warn(m_templateName,line,"for needs at least one iterator variable");
3053 int j = data.find(" reversed",i);
3054 m_reversed = (j!=-1);
3056 if (j==-1) j=data.length();
3059 exprStr = data.mid(i+4,j-i-4); // skip over " in " part
3061 if (exprStr.isEmpty())
3063 parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
3066 ExpressionParser expParser(parser,line);
3067 m_expr = expParser.parse(exprStr);
3070 stopAt.append("endfor");
3071 stopAt.append("empty");
3072 parser->parse(this,line,stopAt,m_loopNodes);
3073 TemplateToken *tok = parser->takeNextToken();
3074 if (tok && tok->data=="empty")
3076 stopAt.removeLast();
3077 parser->parse(this,line,stopAt,m_emptyNodes);
3078 parser->removeNextToken(); // skip over endfor
3081 TRACE(("}TemplateNodeFor(%s)\n",data.data()));
3089 void render(FTextStream &ts, TemplateContext *c)
3091 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3092 ci->setLocation(m_templateName,m_line);
3093 //printf("TemplateNodeFor::render #loopNodes=%d #emptyNodes=%d\n",
3094 // m_loopNodes.count(),m_emptyNodes.count());
3097 TemplateVariant v = m_expr->resolve(c);
3098 if (v.type()==TemplateVariant::Function)
3100 v = v.call(QValueList<TemplateVariant>());
3102 const TemplateListIntf *list = v.toList();
3105 uint listSize = list->count();
3106 if (listSize==0) // empty for loop
3108 m_emptyNodes.render(ts,c);
3112 //int index = m_reversed ? list.count() : 0;
3114 const TemplateVariant *parentLoop = c->getRef("forloop");
3115 uint index = m_reversed ? listSize-1 : 0;
3116 TemplateListIntf::ConstIterator *it = list->createIterator();
3117 for (m_reversed ? it->toLast() : it->toFirst();
3119 m_reversed ? it->toPrev() : it->toNext())
3121 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
3122 s->set("counter0", (int)index);
3123 s->set("counter", (int)(index+1));
3124 s->set("revcounter", (int)(listSize-index));
3125 s->set("revcounter0", (int)(listSize-index-1));
3126 s->set("first",index==0);
3127 s->set("last", index==listSize-1);
3128 s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
3129 c->set("forloop",s.get());
3131 // add variables for this loop to the context
3132 //obj->addVariableToContext(index,m_vars,c);
3134 if (m_vars.count()==1) // loop variable represents an item
3136 c->set(m_vars[vi++],v);
3138 else if (m_vars.count()>1 && v.type()==TemplateVariant::Struct)
3139 // loop variables represent elements in a list item
3141 for (uint i=0;i<m_vars.count();i++,vi++)
3143 c->set(m_vars[vi],v.toStruct()->get(m_vars[vi]));
3146 for (;vi<m_vars.count();vi++)
3148 c->set(m_vars[vi],TemplateVariant());
3151 // render all items for this iteration of the loop
3152 m_loopNodes.render(ts,c);
3154 if (m_reversed) index--; else index++;
3159 else // simple type...
3161 ci->warn(m_templateName,m_line,"for requires a variable of list type, got type '%s'!",v.typeAsString().data());
3169 QValueList<QCString> m_vars;
3170 TemplateNodeList m_loopNodes;
3171 TemplateNodeList m_emptyNodes;
3174 //----------------------------------------------------------
3176 /** @brief Class representing an 'markers' tag in a template */
3177 class TemplateNodeMsg : public TemplateNodeCreator<TemplateNodeMsg>
3180 TemplateNodeMsg(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
3181 : TemplateNodeCreator<TemplateNodeMsg>(parser,parent,line)
3183 TRACE(("{TemplateNodeMsg()\n"));
3185 stopAt.append("endmsg");
3186 parser->parse(this,line,stopAt,m_nodes);
3187 parser->removeNextToken(); // skip over endmarkers
3188 TRACE(("}TemplateNodeMsg()\n"));
3190 void render(FTextStream &, TemplateContext *c)
3192 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3193 ci->setLocation(m_templateName,m_line);
3194 TemplateEscapeIntf *escIntf = ci->escapeIntf();
3195 ci->setActiveEscapeIntf(0); // avoid escaping things we send to standard out
3196 bool enable = ci->spacelessEnabled();
3197 ci->enableSpaceless(FALSE);
3198 FTextStream ts(stdout);
3199 m_nodes.render(ts,c);
3201 ci->setActiveEscapeIntf(escIntf);
3202 ci->enableSpaceless(enable);
3205 TemplateNodeList m_nodes;
3209 //----------------------------------------------------------
3211 /** @brief Class representing a 'block' tag in a template */
3212 class TemplateNodeBlock : public TemplateNodeCreator<TemplateNodeBlock>
3215 TemplateNodeBlock(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3216 : TemplateNodeCreator<TemplateNodeBlock>(parser,parent,line)
3218 TRACE(("{TemplateNodeBlock(%s)\n",data.data()));
3220 if (m_blockName.isEmpty())
3222 parser->warn(parser->templateName(),line,"block tag without name");
3225 stopAt.append("endblock");
3226 parser->parse(this,line,stopAt,m_nodes);
3227 parser->removeNextToken(); // skip over endblock
3228 TRACE(("}TemplateNodeBlock(%s)\n",data.data()));
3231 void render(FTextStream &ts, TemplateContext *c)
3233 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3234 ci->setLocation(m_templateName,m_line);
3235 TemplateImpl *t = getTemplate();
3238 // remove block from the context, so block.super can work
3239 TemplateNodeBlock *nb = ci->blockContext()->pop(m_blockName);
3240 if (nb) // block is overruled
3244 FTextStream ss(&super);
3245 // get super block of block nb
3246 TemplateNodeBlock *sb = ci->blockContext()->get(m_blockName);
3247 if (sb && sb!=nb && sb!=this) // nb and sb both overrule this block
3249 sb->render(ss,c); // render parent of nb to string
3251 else if (nb!=this) // only nb overrules this block
3253 m_nodes.render(ss,c); // render parent of nb to string
3255 // add 'block.super' variable to allow access to parent block content
3256 TemplateAutoRef<TemplateStruct> superBlock(TemplateStruct::alloc());
3257 superBlock->set("super",TemplateVariant(super.data(),TRUE));
3258 ci->set("block",superBlock.get());
3259 // render the overruled block contents
3260 t->engine()->enterBlock(nb->m_templateName,nb->m_blockName,nb->m_line);
3261 nb->m_nodes.render(ts,c);
3262 t->engine()->leaveBlock();
3264 // re-add block to the context
3265 ci->blockContext()->push(nb);
3267 else // block has no overrule
3269 t->engine()->enterBlock(m_templateName,m_blockName,m_line);
3270 m_nodes.render(ts,c);
3271 t->engine()->leaveBlock();
3276 QCString name() const
3282 QCString m_blockName;
3283 TemplateNodeList m_nodes;
3286 //----------------------------------------------------------
3288 /** @brief Class representing a 'extend' tag in a template */
3289 class TemplateNodeExtend : public TemplateNodeCreator<TemplateNodeExtend>
3292 TemplateNodeExtend(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3293 : TemplateNodeCreator<TemplateNodeExtend>(parser,parent,line)
3295 TRACE(("{TemplateNodeExtend(%s)\n",data.data()));
3296 ExpressionParser ep(parser,line);
3299 parser->warn(m_templateName,line,"extend tag is missing template file argument");
3301 m_extendExpr = ep.parse(data);
3303 parser->parse(this,line,stopAt,m_nodes);
3304 TRACE(("}TemplateNodeExtend(%s)\n",data.data()));
3306 ~TemplateNodeExtend()
3308 delete m_extendExpr;
3311 void render(FTextStream &ts, TemplateContext *c)
3313 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3314 ci->setLocation(m_templateName,m_line);
3315 if (m_extendExpr==0) return;
3317 QCString extendFile = m_extendExpr->resolve(c).toString();
3318 if (extendFile.isEmpty())
3320 ci->warn(m_templateName,m_line,"invalid parameter for extend command");
3323 // goto root of tree (template node)
3324 TemplateImpl *t = getTemplate();
3327 Template *bt = t->engine()->loadByName(extendFile,m_line);
3328 TemplateImpl *baseTemplate = bt ? dynamic_cast<TemplateImpl*>(bt) : 0;
3331 // fill block context
3332 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3333 TemplateBlockContext *bc = ci->blockContext();
3335 // add overruling blocks to the context
3336 QListIterator<TemplateNode> li(m_nodes);
3338 for (li.toFirst();(n=li.current());++li)
3340 TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
3345 TemplateNodeMsg *msg = dynamic_cast<TemplateNodeMsg*>(n);
3352 // render the base template with the given context
3353 baseTemplate->render(ts,c);
3357 t->engine()->unload(t);
3361 ci->warn(m_templateName,m_line,"failed to load template %s for extend",extendFile.data());
3367 ExprAst *m_extendExpr;
3368 TemplateNodeList m_nodes;
3371 /** @brief Class representing an 'include' tag in a template */
3372 class TemplateNodeInclude : public TemplateNodeCreator<TemplateNodeInclude>
3375 TemplateNodeInclude(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3376 : TemplateNodeCreator<TemplateNodeInclude>(parser,parent,line)
3378 TRACE(("TemplateNodeInclude(%s)\n",data.data()));
3379 ExpressionParser ep(parser,line);
3382 parser->warn(m_templateName,line,"include tag is missing template file argument");
3384 m_includeExpr = ep.parse(data);
3386 ~TemplateNodeInclude()
3388 delete m_includeExpr;
3390 void render(FTextStream &ts, TemplateContext *c)
3392 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3393 ci->setLocation(m_templateName,m_line);
3396 QCString includeFile = m_includeExpr->resolve(c).toString();
3397 if (includeFile.isEmpty())
3399 ci->warn(m_templateName,m_line,"invalid parameter for include command\n");
3403 TemplateImpl *t = getTemplate();
3406 Template *it = t->engine()->loadByName(includeFile,m_line);
3407 TemplateImpl *incTemplate = it ? dynamic_cast<TemplateImpl*>(it) : 0;
3410 incTemplate->render(ts,c);
3411 t->engine()->unload(t);
3415 ci->warn(m_templateName,m_line,"failed to load template '%s' for include",includeFile.data()?includeFile.data():"");
3423 ExprAst *m_includeExpr;
3426 //----------------------------------------------------------
3428 /** @brief Class representing an 'create' tag in a template */
3429 class TemplateNodeCreate : public TemplateNodeCreator<TemplateNodeCreate>
3432 TemplateNodeCreate(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3433 : TemplateNodeCreator<TemplateNodeCreate>(parser,parent,line)
3435 TRACE(("TemplateNodeCreate(%s)\n",data.data()));
3436 ExpressionParser ep(parser,line);
3439 parser->warn(m_templateName,line,"create tag is missing arguments");
3441 int i = data.find(" from ");
3444 if (data.right(3)==" from")
3446 parser->warn(m_templateName,line,"create is missing template name after 'from' keyword");
3448 else if (data=="from")
3450 parser->warn(m_templateName,line,"create needs a file name and a template name");
3454 parser->warn(m_templateName,line,"create is missing 'from' keyword");
3459 ExpressionParser ep(parser,line);
3460 m_fileExpr = ep.parse(data.left(i).stripWhiteSpace());
3461 m_templateExpr = ep.parse(data.mid(i+6).stripWhiteSpace());
3464 ~TemplateNodeCreate()
3466 delete m_templateExpr;
3469 void mkpath(TemplateContextImpl *ci,const QCString &fileName)
3471 int i=fileName.find('/');
3472 QCString outputDir = ci->outputDirectory();
3475 while (i!=-1) // fileName contains path part
3479 bool ok = d.mkdir(fileName.mid(j,i-j));
3481 QCString dirName = outputDir+'/'+fileName.left(i);
3485 i=fileName.find('/',i+1);
3488 void render(FTextStream &, TemplateContext *c)
3490 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3491 ci->setLocation(m_templateName,m_line);
3492 if (m_templateExpr && m_fileExpr)
3494 QCString templateFile = m_templateExpr->resolve(c).toString();
3495 QCString outputFile = m_fileExpr->resolve(c).toString();
3496 if (templateFile.isEmpty())
3498 ci->warn(m_templateName,m_line,"empty template name parameter for create command\n");
3500 else if (outputFile.isEmpty())
3502 ci->warn(m_templateName,m_line,"empty file name parameter for create command\n");
3506 TemplateImpl *t = getTemplate();
3509 Template *ct = t->engine()->loadByName(templateFile,m_line);
3510 TemplateImpl *createTemplate = ct ? dynamic_cast<TemplateImpl*>(ct) : 0;
3513 //mkpath(ci,outputFile);
3514 QCString extension=outputFile;
3515 int i=extension.findRev('.');
3518 extension=extension.right(extension.length()-i-1);
3520 if (!ci->outputDirectory().isEmpty())
3522 outputFile.prepend(ci->outputDirectory()+"/");
3524 QFile f(outputFile);
3525 if (f.open(IO_WriteOnly))
3527 TemplateEscapeIntf *escIntf = ci->escapeIntf();
3528 ci->selectEscapeIntf(extension);
3530 createTemplate->render(ts,c);
3531 t->engine()->unload(t);
3532 ci->setActiveEscapeIntf(escIntf);
3536 ci->warn(m_templateName,m_line,"failed to open output file '%s' for create command",outputFile.data());
3541 ci->warn(m_templateName,m_line,"failed to load template '%s' for include",templateFile.data());
3549 ExprAst *m_templateExpr;
3550 ExprAst *m_fileExpr;
3553 //----------------------------------------------------------
3555 /** @brief Class representing an 'tree' tag in a template */
3556 class TemplateNodeTree : public TemplateNodeCreator<TemplateNodeTree>
3560 TreeContext(TemplateNodeTree *o,const TemplateListIntf *l,TemplateContext *c)
3561 : object(o), list(l), templateCtx(c) {}
3562 TemplateNodeTree *object;
3563 const TemplateListIntf *list;
3564 TemplateContext *templateCtx;
3567 TemplateNodeTree(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3568 : TemplateNodeCreator<TemplateNodeTree>(parser,parent,line)
3570 TRACE(("{TemplateNodeTree(%s)\n",data.data()));
3571 ExpressionParser ep(parser,line);
3574 parser->warn(m_templateName,line,"recursetree tag is missing data argument");
3576 m_treeExpr = ep.parse(data);
3578 stopAt.append("endrecursetree");
3579 parser->parse(this,line,stopAt,m_treeNodes);
3580 parser->removeNextToken(); // skip over endrecursetree
3581 TRACE(("}TemplateNodeTree(%s)\n",data.data()));
3587 static TemplateVariant renderChildrenStub(const void *ctx, const QValueList<TemplateVariant> &)
3589 return TemplateVariant(((TreeContext*)ctx)->object->
3590 renderChildren((const TreeContext*)ctx),TRUE);
3592 QCString renderChildren(const TreeContext *ctx)
3594 //printf("TemplateNodeTree::renderChildren(%d)\n",ctx->list->count());
3595 // render all children of node to a string and return it
3597 FTextStream ss(&result);
3598 TemplateContext *c = ctx->templateCtx;
3600 TemplateVariant node;
3601 TemplateListIntf::ConstIterator *it = ctx->list->createIterator();
3602 for (it->toFirst();(it->current(node));it->toNext())
3604 c->set("node",node);
3605 bool hasChildren=FALSE;
3606 const TemplateStructIntf *ns = node.toStruct();
3607 if (ns) // node is a struct
3609 TemplateVariant v = ns->get("children");
3610 if (v.isValid()) // with a field 'children'
3612 const TemplateListIntf *list = v.toList();
3613 if (list && list->count()>0) // non-empty list
3615 TreeContext childCtx(this,list,ctx->templateCtx);
3616 // TemplateVariant children(&childCtx,renderChildrenStub);
3617 TemplateVariant children(TemplateVariant::Delegate::fromFunction(&childCtx,renderChildrenStub));
3618 children.setRaw(TRUE);
3619 c->set("children",children);
3620 m_treeNodes.render(ss,c);
3627 c->set("children",TemplateVariant("")); // provide default
3628 m_treeNodes.render(ss,c);
3632 return result.data();
3634 void render(FTextStream &ts, TemplateContext *c)
3636 //printf("TemplateNodeTree::render()\n");
3637 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3638 ci->setLocation(m_templateName,m_line);
3639 TemplateVariant v = m_treeExpr->resolve(c);
3640 const TemplateListIntf *list = v.toList();
3643 TreeContext ctx(this,list,c);
3644 ts << renderChildren(&ctx);
3648 ci->warn(m_templateName,m_line,"recursetree's argument should be a list type");
3653 ExprAst *m_treeExpr;
3654 TemplateNodeList m_treeNodes;
3657 //----------------------------------------------------------
3659 /** @brief Class representing an 'indexentry' tag in a template */
3660 class TemplateNodeIndexEntry : public TemplateNodeCreator<TemplateNodeIndexEntry>
3664 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3665 ~Mapping() { delete value; }
3670 TemplateNodeIndexEntry(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3671 : TemplateNodeCreator<TemplateNodeIndexEntry>(parser,parent,line)
3673 TRACE(("{TemplateNodeIndexEntry(%s)\n",data.data()));
3674 m_args.setAutoDelete(TRUE);
3675 ExpressionParser expParser(parser,line);
3676 QValueList<QCString> args = split(data," ");
3677 QValueListIterator<QCString> it = args.begin();
3678 if (it==args.end() || (*it).find('=')!=-1)
3680 parser->warn(parser->templateName(),line,"Missing name for indexentry tag");
3686 while (it!=args.end())
3689 int j=arg.find('=');
3692 ExprAst *expr = expParser.parse(arg.mid(j+1));
3695 m_args.append(new Mapping(arg.left(j),expr));
3700 parser->warn(parser->templateName(),line,"invalid argument '%s' for indexentry tag",arg.data());
3705 TRACE(("}TemplateNodeIndexEntry(%s)\n",data.data()));
3707 void render(FTextStream &, TemplateContext *c)
3709 if (!m_name.isEmpty())
3711 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3712 ci->setLocation(m_templateName,m_line);
3713 QListIterator<Mapping> it(m_args);
3715 QValueList<TemplateKeyValue> list;
3716 for (it.toFirst();(mapping=it.current());++it)
3718 list.append(TemplateKeyValue(mapping->name,mapping->value->resolve(c)));
3720 ci->addIndexEntry(m_name,list);
3725 QList<Mapping> m_args;
3728 //----------------------------------------------------------
3730 /** @brief Class representing an 'opensubindex' tag in a template */
3731 class TemplateNodeOpenSubIndex : public TemplateNodeCreator<TemplateNodeOpenSubIndex>
3734 TemplateNodeOpenSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3735 : TemplateNodeCreator<TemplateNodeOpenSubIndex>(parser,parent,line)
3737 TRACE(("{TemplateNodeOpenSubIndex(%s)\n",data.data()));
3738 m_name = data.stripWhiteSpace();
3739 if (m_name.isEmpty())
3741 parser->warn(parser->templateName(),line,"Missing argument for opensubindex tag");
3743 else if (m_name.find(' ')!=-1)
3745 parser->warn(parser->templateName(),line,"Expected single argument for opensubindex tag got '%s'",data.data());
3748 TRACE(("}TemplateNodeOpenSubIndex(%s)\n",data.data()));
3750 void render(FTextStream &, TemplateContext *c)
3752 if (!m_name.isEmpty())
3754 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3755 ci->setLocation(m_templateName,m_line);
3756 ci->openSubIndex(m_name);
3763 //----------------------------------------------------------
3765 /** @brief Class representing an 'closesubindex' tag in a template */
3766 class TemplateNodeCloseSubIndex : public TemplateNodeCreator<TemplateNodeCloseSubIndex>
3769 TemplateNodeCloseSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3770 : TemplateNodeCreator<TemplateNodeCloseSubIndex>(parser,parent,line)
3772 TRACE(("{TemplateNodeCloseSubIndex(%s)\n",data.data()));
3773 m_name = data.stripWhiteSpace();
3774 if (m_name.isEmpty())
3776 parser->warn(parser->templateName(),line,"Missing argument for closesubindex tag");
3778 else if (m_name.find(' ')!=-1 || m_name.isEmpty())
3780 parser->warn(parser->templateName(),line,"Expected single argument for closesubindex tag got '%s'",data.data());
3783 TRACE(("}TemplateNodeCloseSubIndex(%s)\n",data.data()));
3785 void render(FTextStream &, TemplateContext *c)
3787 if (!m_name.isEmpty())
3789 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3790 ci->setLocation(m_templateName,m_line);
3791 ci->closeSubIndex(m_name);
3799 //----------------------------------------------------------
3801 /** @brief Class representing an 'with' tag in a template */
3802 class TemplateNodeWith : public TemplateNodeCreator<TemplateNodeWith>
3806 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3807 ~Mapping() { delete value; }
3812 TemplateNodeWith(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3813 : TemplateNodeCreator<TemplateNodeWith>(parser,parent,line)
3815 TRACE(("{TemplateNodeWith(%s)\n",data.data()));
3816 m_args.setAutoDelete(TRUE);
3817 ExpressionParser expParser(parser,line);
3818 QValueList<QCString> args = split(data," ");
3819 QValueListIterator<QCString> it = args.begin();
3820 while (it!=args.end())
3823 int j=arg.find('=');
3826 ExprAst *expr = expParser.parse(arg.mid(j+1));
3829 m_args.append(new Mapping(arg.left(j),expr));
3834 parser->warn(parser->templateName(),line,"invalid argument '%s' for 'with' tag",arg.data());
3839 stopAt.append("endwith");
3840 parser->parse(this,line,stopAt,m_nodes);
3841 parser->removeNextToken(); // skip over endwith
3842 TRACE(("}TemplateNodeWith(%s)\n",data.data()));
3847 void render(FTextStream &ts, TemplateContext *c)
3849 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3850 ci->setLocation(m_templateName,m_line);
3852 QListIterator<Mapping> it(m_args);
3854 for (it.toFirst();(mapping=it.current());++it)
3856 TemplateVariant value = mapping->value->resolve(c);
3857 ci->set(mapping->name,value);
3859 m_nodes.render(ts,c);
3863 TemplateNodeList m_nodes;
3864 QList<Mapping> m_args;
3867 //----------------------------------------------------------
3869 /** @brief Class representing an 'set' tag in a template */
3870 class TemplateNodeCycle : public TemplateNodeCreator<TemplateNodeCycle>
3873 TemplateNodeCycle(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3874 : TemplateNodeCreator<TemplateNodeCycle>(parser,parent,line)
3876 TRACE(("{TemplateNodeCycle(%s)\n",data.data()));
3877 m_args.setAutoDelete(TRUE);
3879 ExpressionParser expParser(parser,line);
3880 QValueList<QCString> args = split(data," ");
3881 QValueListIterator<QCString> it = args.begin();
3882 while (it!=args.end())
3884 ExprAst *expr = expParser.parse(*it);
3887 m_args.append(expr);
3891 if (m_args.count()<2)
3893 parser->warn(parser->templateName(),line,"expected at least two arguments for cycle command, got %d",m_args.count());
3895 TRACE(("}TemplateNodeCycle(%s)\n",data.data()));
3897 void render(FTextStream &ts, TemplateContext *c)
3899 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3900 ci->setLocation(m_templateName,m_line);
3901 if (m_index<m_args.count())
3903 TemplateVariant v = m_args.at(m_index)->resolve(c);
3904 if (v.type()==TemplateVariant::Function)
3906 v = v.call(QValueList<TemplateVariant>());
3908 if (ci->escapeIntf() && !v.raw())
3910 ts << ci->escapeIntf()->escape(v.toString());
3917 if (++m_index==m_args.count()) // wrap around
3924 QList<ExprAst> m_args;
3927 //----------------------------------------------------------
3929 /** @brief Class representing an 'set' tag in a template */
3930 class TemplateNodeSet : public TemplateNodeCreator<TemplateNodeSet>
3934 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3935 ~Mapping() { delete value; }
3940 TemplateNodeSet(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3941 : TemplateNodeCreator<TemplateNodeSet>(parser,parent,line), m_mapping(0)
3943 TRACE(("{TemplateNodeSet(%s)\n",data.data()));
3944 ExpressionParser expParser(parser,line);
3945 // data format: name=expression
3946 int j=data.find('=');
3948 if (j>0 && (expr = expParser.parse(data.mid(j+1))))
3950 m_mapping = new Mapping(data.left(j),expr);
3952 TRACE(("}TemplateNodeSet(%s)\n",data.data()));
3958 void render(FTextStream &, TemplateContext *c)
3960 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3961 ci->setLocation(m_templateName,m_line);
3964 TemplateVariant value = m_mapping->value->resolve(c);
3965 ci->set(m_mapping->name,value);
3972 //----------------------------------------------------------
3974 /** @brief Class representing an 'spaceless' tag in a template */
3975 class TemplateNodeSpaceless : public TemplateNodeCreator<TemplateNodeSpaceless>
3978 TemplateNodeSpaceless(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
3979 : TemplateNodeCreator<TemplateNodeSpaceless>(parser,parent,line)
3981 TRACE(("{TemplateNodeSpaceless()\n"));
3983 stopAt.append("endspaceless");
3984 parser->parse(this,line,stopAt,m_nodes);
3985 parser->removeNextToken(); // skip over endwith
3986 TRACE(("}TemplateNodeSpaceless()\n"));
3988 void render(FTextStream &ts, TemplateContext *c)
3990 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3991 ci->setLocation(m_templateName,m_line);
3992 bool wasSpaceless = ci->spacelessEnabled();
3993 ci->enableSpaceless(TRUE);
3994 m_nodes.render(ts,c);
3995 ci->enableSpaceless(wasSpaceless);
3998 TemplateNodeList m_nodes;
4001 //----------------------------------------------------------
4003 /** @brief Class representing an 'markers' tag in a template */
4004 class TemplateNodeMarkers : public TemplateNodeCreator<TemplateNodeMarkers>
4007 TemplateNodeMarkers(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4008 : TemplateNodeCreator<TemplateNodeMarkers>(parser,parent,line)
4010 TRACE(("{TemplateNodeMarkers(%s)\n",data.data()));
4011 int i = data.find(" in ");
4012 int w = data.find(" with ");
4013 if (i==-1 || w==-1 || w<i)
4015 parser->warn(m_templateName,line,"markers tag as wrong format. Expected: markers <var> in <list> with <string_with_markers>");
4019 ExpressionParser expParser(parser,line);
4020 m_var = data.left(i);
4021 m_listExpr = expParser.parse(data.mid(i+4,w-i-4));
4022 m_patternExpr = expParser.parse(data.right(data.length()-w-6));
4025 stopAt.append("endmarkers");
4026 parser->parse(this,line,stopAt,m_nodes);
4027 parser->removeNextToken(); // skip over endmarkers
4028 TRACE(("}TemplateNodeMarkers(%s)\n",data.data()));
4030 void render(FTextStream &ts, TemplateContext *c)
4032 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4033 ci->setLocation(m_templateName,m_line);
4034 if (!m_var.isEmpty() && m_listExpr && m_patternExpr)
4036 TemplateVariant v = m_listExpr->resolve(c);
4037 const TemplateListIntf *list = v.toList();
4038 TemplateVariant patternStr = m_patternExpr->resolve(c);
4041 if (patternStr.type()==TemplateVariant::String)
4043 TemplateListIntf::ConstIterator *it = list->createIterator();
4045 QCString str = patternStr.toString();
4046 QRegExp marker("@[0-9]+"); // pattern for a marker, i.e. @0, @1 ... @12, etc
4047 int index=0,newIndex,matchLen;
4048 while ((newIndex=marker.match(str,index,&matchLen))!=-1)
4050 ts << str.mid(index,newIndex-index); // write text before marker
4052 uint entryIndex = str.mid(newIndex+1,matchLen-1).toUInt(&ok); // get marker id
4053 TemplateVariant var;
4055 // search for list element at position id
4056 for (it->toFirst(); (it->current(var)) && i<entryIndex; it->toNext(),i++) {}
4057 if (ok && i==entryIndex) // found element
4059 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
4060 s->set("id",(int)i);
4061 c->set("markers",s.get());
4062 c->set(m_var,var); // define local variable to hold element of list type
4063 bool wasSpaceless = ci->spacelessEnabled();
4064 ci->enableSpaceless(TRUE);
4065 m_nodes.render(ts,c);
4066 ci->enableSpaceless(wasSpaceless);
4070 ci->warn(m_templateName,m_line,"markers pattern string has invalid markers '%s'",str.data());
4072 else if (i<entryIndex)
4074 ci->warn(m_templateName,m_line,"markers list does not an element for marker position %d",i);
4076 index=newIndex+matchLen; // set index just after marker
4078 ts << str.right(str.length()-index); // write text after last marker
4083 ci->warn(m_templateName,m_line,"markers requires a parameter of string type after 'with'!");
4088 ci->warn(m_templateName,m_line,"markers requires a parameter of list type after 'in'!");
4093 TemplateNodeList m_nodes;
4095 ExprAst *m_listExpr;
4096 ExprAst *m_patternExpr;
4099 //----------------------------------------------------------
4101 /** @brief Factory class for creating tag AST nodes found in a template */
4102 class TemplateNodeFactory
4105 typedef TemplateNode *(*CreateFunc)(TemplateParser *parser,
4106 TemplateNode *parent,
4108 const QCString &data);
4110 static TemplateNodeFactory *instance()
4112 static TemplateNodeFactory *instance = 0;
4113 if (instance==0) instance = new TemplateNodeFactory;
4117 TemplateNode *create(const QCString &name,
4118 TemplateParser *parser,
4119 TemplateNode *parent,
4121 const QCString &data)
4123 if (m_registry.find(name)==0) return 0;
4124 return ((CreateFunc)m_registry[name])(parser,parent,line,data);
4127 void registerTemplateNode(const QCString &name,CreateFunc func)
4129 m_registry.insert(name,(void*)func);
4132 /** @brief Helper class for registering a template AST node */
4133 template<class T> class AutoRegister
4136 AutoRegister<T>(const QCString &key)
4138 TemplateNodeFactory::instance()->registerTemplateNode(key,T::createInstance);
4143 QDict<void> m_registry;
4146 // register a handler for each start tag we support
4147 static TemplateNodeFactory::AutoRegister<TemplateNodeIf> autoRefIf("if");
4148 static TemplateNodeFactory::AutoRegister<TemplateNodeFor> autoRefFor("for");
4149 static TemplateNodeFactory::AutoRegister<TemplateNodeMsg> autoRefMsg("msg");
4150 static TemplateNodeFactory::AutoRegister<TemplateNodeSet> autoRefSet("set");
4151 static TemplateNodeFactory::AutoRegister<TemplateNodeTree> autoRefTree("recursetree");
4152 static TemplateNodeFactory::AutoRegister<TemplateNodeWith> autoRefWith("with");
4153 static TemplateNodeFactory::AutoRegister<TemplateNodeBlock> autoRefBlock("block");
4154 static TemplateNodeFactory::AutoRegister<TemplateNodeCycle> autoRefCycle("cycle");
4155 static TemplateNodeFactory::AutoRegister<TemplateNodeRange> autoRefRange("range");
4156 static TemplateNodeFactory::AutoRegister<TemplateNodeExtend> autoRefExtend("extend");
4157 static TemplateNodeFactory::AutoRegister<TemplateNodeCreate> autoRefCreate("create");
4158 static TemplateNodeFactory::AutoRegister<TemplateNodeRepeat> autoRefRepeat("repeat");
4159 static TemplateNodeFactory::AutoRegister<TemplateNodeInclude> autoRefInclude("include");
4160 static TemplateNodeFactory::AutoRegister<TemplateNodeMarkers> autoRefMarkers("markers");
4161 static TemplateNodeFactory::AutoRegister<TemplateNodeSpaceless> autoRefSpaceless("spaceless");
4162 static TemplateNodeFactory::AutoRegister<TemplateNodeIndexEntry> autoRefIndexEntry("indexentry");
4163 static TemplateNodeFactory::AutoRegister<TemplateNodeOpenSubIndex> autoRefOpenSubIndex("opensubindex");
4164 static TemplateNodeFactory::AutoRegister<TemplateNodeCloseSubIndex> autoRefCloseSubIndex("closesubindex");
4166 //----------------------------------------------------------
4168 TemplateBlockContext::TemplateBlockContext() : m_blocks(257)
4170 m_blocks.setAutoDelete(TRUE);
4173 TemplateNodeBlock *TemplateBlockContext::get(const QCString &name) const
4175 QList<TemplateNodeBlock> *list = m_blocks.find(name);
4176 if (list==0 || list->count()==0)
4182 return list->getLast();
4186 TemplateNodeBlock *TemplateBlockContext::pop(const QCString &name) const
4188 QList<TemplateNodeBlock> *list = m_blocks.find(name);
4189 if (list==0 || list->count()==0)
4195 return list->take(list->count()-1);
4199 void TemplateBlockContext::add(TemplateNodeBlock *block)
4201 QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
4204 list = new QList<TemplateNodeBlock>;
4205 m_blocks.insert(block->name(),list);
4207 list->prepend(block);
4210 void TemplateBlockContext::add(TemplateBlockContext *ctx)
4212 QDictIterator< QList<TemplateNodeBlock> > di(ctx->m_blocks);
4213 QList<TemplateNodeBlock> *list;
4214 for (di.toFirst();(list=di.current());++di)
4216 QListIterator<TemplateNodeBlock> li(*list);
4217 TemplateNodeBlock *nb;
4218 for (li.toFirst();(nb=li.current());++li)
4225 void TemplateBlockContext::clear()
4230 void TemplateBlockContext::push(TemplateNodeBlock *block)
4232 QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
4235 list = new QList<TemplateNodeBlock>;
4236 m_blocks.insert(block->name(),list);
4238 list->append(block);
4242 //----------------------------------------------------------
4244 /** @brief Lexer class for turning a template into a list of tokens */
4248 TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data);
4249 void tokenize(QList<TemplateToken> &tokens);
4251 void addToken(QList<TemplateToken> &tokens,
4252 const char *data,int line,int startPos,int endPos,
4253 TemplateToken::Type type);
4255 const TemplateEngine *m_engine;
4256 QCString m_fileName;
4260 TemplateLexer::TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data) :
4261 m_engine(engine), m_fileName(fileName), m_data(data)
4265 void TemplateLexer::tokenize(QList<TemplateToken> &tokens)
4280 const char *p=m_data.data();
4281 int state=StateText;
4285 bool emptyOutputLine=TRUE;
4288 int markStartPos=-1;
4289 for (;(c=*p);p++,pos++)
4294 if (c=='{') // {{ or {% or {# or something else
4296 state=StateBeginTemplate;
4298 else if (c!=' ' && c!='\t' && c!='\n') // non-whitepace text
4300 emptyOutputLine=FALSE;
4303 case StateBeginTemplate:
4315 state=StateMaybeVar;
4320 emptyOutputLine=FALSE;
4327 warn(m_fileName,line,"unexpected new line inside {%%...%%} block");
4328 m_engine->printIncludeContext(m_fileName,line);
4330 else if (c=='%') // %} or something else
4340 addToken(tokens,m_data.data(),line,lastTokenPos,
4341 emptyOutputLine ? startLinePos : markStartPos,
4342 TemplateToken::Text);
4343 addToken(tokens,m_data.data(),line,markStartPos+2,
4344 pos-1,TemplateToken::Block);
4345 lastTokenPos = pos+1;
4347 else // something else
4351 warn(m_fileName,line,"unexpected new line inside {%%...%%} block");
4352 m_engine->printIncludeContext(m_fileName,line);
4360 warn(m_fileName,line,"unexpected new line inside {#...#} block");
4361 m_engine->printIncludeContext(m_fileName,line);
4363 else if (c=='#') // #} or something else
4365 state=StateEndComment;
4368 case StateEndComment:
4371 // found comment tag!
4373 addToken(tokens,m_data.data(),line,lastTokenPos,
4374 emptyOutputLine ? startLinePos : markStartPos,
4375 TemplateToken::Text);
4376 lastTokenPos = pos+1;
4378 else // something else
4382 warn(m_fileName,line,"unexpected new line inside {#...#} block");
4383 m_engine->printIncludeContext(m_fileName,line);
4400 state=StateVariable;
4407 warn(m_fileName,line,"unexpected new line inside {{...}} block");
4408 m_engine->printIncludeContext(m_fileName,line);
4410 else if (c=='}') // }} or something else
4412 state=StateEndVariable;
4415 case StateEndVariable:
4418 // found variable tag!
4420 addToken(tokens,m_data.data(),line,lastTokenPos,
4421 emptyOutputLine ? startLinePos : markStartPos,
4422 TemplateToken::Text);
4423 addToken(tokens,m_data.data(),line,markStartPos+2,
4424 pos-1,TemplateToken::Variable);
4425 lastTokenPos = pos+1;
4427 else // something else
4431 warn(m_fileName,line,"unexpected new line inside {{...}} block");
4432 m_engine->printIncludeContext(m_fileName,line);
4434 state=StateVariable;
4438 if (c=='\n') // new line
4442 // if the current line only contain commands and whitespace,
4443 // then skip it in the output by moving lastTokenPos
4444 if (markStartPos!=-1 && emptyOutputLine) lastTokenPos = startLinePos;
4448 emptyOutputLine=TRUE;
4451 if (lastTokenPos<pos)
4453 addToken(tokens,m_data.data(),line,
4455 TemplateToken::Text);
4459 void TemplateLexer::addToken(QList<TemplateToken> &tokens,
4460 const char *data,int line,
4461 int startPos,int endPos,
4462 TemplateToken::Type type)
4464 if (startPos<endPos)
4466 int len = endPos-startPos+1;
4467 QCString text(len+1);
4468 qstrncpy(text.data(),data+startPos,len);
4470 if (type!=TemplateToken::Text) text = text.stripWhiteSpace();
4471 tokens.append(new TemplateToken(type,text,line));
4475 //----------------------------------------------------------
4477 TemplateParser::TemplateParser(const TemplateEngine *engine,
4478 const QCString &templateName,
4479 QList<TemplateToken> &tokens) :
4480 m_engine(engine), m_templateName(templateName), m_tokens(tokens)
4484 void TemplateParser::parse(
4485 TemplateNode *parent,int line,const QStrList &stopAt,
4486 QList<TemplateNode> &nodes)
4488 TRACE(("{TemplateParser::parse\n"));
4489 // process the tokens. Build node list
4490 while (hasNextToken())
4492 TemplateToken *tok = takeNextToken();
4493 //printf("%p:Token type=%d data='%s' line=%d\n",
4494 // parent,tok->type,tok->data.data(),tok->line);
4497 case TemplateToken::Text:
4498 nodes.append(new TemplateNodeText(this,parent,tok->line,tok->data));
4500 case TemplateToken::Variable: // {{ var }}
4501 nodes.append(new TemplateNodeVariable(this,parent,tok->line,tok->data));
4503 case TemplateToken::Block: // {% tag %}
4505 QCString command = tok->data;
4506 int sep = command.find(' ');
4509 command=command.left(sep);
4511 if (stopAt.contains(command))
4514 TRACE(("}TemplateParser::parse: stop\n"));
4520 arg = tok->data.mid(sep+1);
4522 TemplateNode *node = TemplateNodeFactory::instance()->
4523 create(command,this,parent,tok->line,arg);
4528 else if (command=="empty" || command=="else" ||
4529 command=="endif" || command=="endfor" ||
4530 command=="endblock" || command=="endwith" ||
4531 command=="endrecursetree" || command=="endspaceless" ||
4532 command=="endmarkers" || command=="endmsg" ||
4533 command=="endrepeat" || command=="elif" ||
4534 command=="endrange")
4536 warn(m_templateName,tok->line,"Found tag '%s' without matching start tag",command.data());
4540 warn(m_templateName,tok->line,"Unknown tag '%s'",command.data());
4547 if (!stopAt.isEmpty())
4549 QStrListIterator it(stopAt);
4552 for (it.toFirst();(s=it.current());++it)
4554 if (!options.isEmpty()) options+=", ";
4557 warn(m_templateName,line,"Unclosed tag in template, expected one of: %s",
4560 TRACE(("}TemplateParser::parse: last token\n"));
4563 bool TemplateParser::hasNextToken() const
4565 return !m_tokens.isEmpty();
4568 TemplateToken *TemplateParser::takeNextToken()
4570 return m_tokens.take(0);
4573 const TemplateToken *TemplateParser::currentToken() const
4575 return m_tokens.getFirst();
4578 void TemplateParser::removeNextToken()
4580 m_tokens.removeFirst();
4583 void TemplateParser::prependToken(const TemplateToken *token)
4585 m_tokens.prepend(token);
4588 void TemplateParser::warn(const char *fileName,int line,const char *fmt,...) const
4592 va_warn(fileName,line,fmt,args);
4594 m_engine->printIncludeContext(fileName,line);
4599 //----------------------------------------------------------
4602 TemplateImpl::TemplateImpl(TemplateEngine *engine,const QCString &name,const QCString &data)
4607 TemplateLexer lexer(engine,name,data);
4608 QList<TemplateToken> tokens;
4609 tokens.setAutoDelete(TRUE);
4610 lexer.tokenize(tokens);
4611 TemplateParser parser(engine,name,tokens);
4612 parser.parse(this,1,QStrList(),m_nodes);
4615 void TemplateImpl::render(FTextStream &ts, TemplateContext *c)
4617 if (!m_nodes.isEmpty())
4619 TemplateNodeExtend *ne = dynamic_cast<TemplateNodeExtend*>(m_nodes.getFirst());
4620 if (ne==0) // normal template, add blocks to block context
4622 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4623 TemplateBlockContext *bc = ci->blockContext();
4624 QListIterator<TemplateNode> li(m_nodes);
4626 for (li.toFirst();(n=li.current());++li)
4628 TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
4635 m_nodes.render(ts,c);
4639 //----------------------------------------------------------
4641 /** @brief Private data of the template engine */
4642 class TemplateEngine::Private
4647 enum Type { Template, Block };
4648 IncludeEntry(Type type,const QCString &fileName,const QCString &blockName,int line)
4649 : m_type(type), m_fileName(fileName), m_blockName(blockName), m_line(line) {}
4650 Type type() const { return m_type; }
4651 QCString fileName() const { return m_fileName; }
4652 QCString blockName() const { return m_blockName; }
4653 int line() const { return m_line; }
4657 QCString m_fileName;
4658 QCString m_blockName;
4662 Private(TemplateEngine *engine) : m_templateCache(17) /*, m_indent(0)*/, m_engine(engine)
4664 m_templateCache.setAutoDelete(TRUE);
4665 m_includeStack.setAutoDelete(TRUE);
4667 Template *loadByName(const QCString &fileName,int line)
4669 //for (int i=0;i<m_indent;i++) printf(" ");
4671 //printf("loadByName(%s,%d) {\n",fileName.data(),line);
4672 m_includeStack.append(new IncludeEntry(IncludeEntry::Template,fileName,QCString(),line));
4673 Template *templ = m_templateCache.find(fileName);
4677 if (f.open(IO_ReadOnly))
4680 char *data = new char[size+1];
4684 if (f.readBlock(data,f.size()))
4686 templ = new TemplateImpl(m_engine,fileName,data);
4687 m_templateCache.insert(fileName,templ);
4694 err("Cound not open template file %s\n",fileName.data());
4699 void unload(Template * /*t*/)
4703 //for (int i=0;i<m_indent;i++) printf(" ");
4705 m_includeStack.removeLast();
4708 void enterBlock(const QCString &fileName,const QCString &blockName,int line)
4710 //for (int i=0;i<m_indent;i++) printf(" ");
4712 //printf("enterBlock(%s,%s,%d) {\n",fileName.data(),blockName.data(),line);
4713 m_includeStack.append(new IncludeEntry(IncludeEntry::Block,fileName,blockName,line));
4719 //for (int i=0;i<m_indent;i++) printf(" ");
4721 m_includeStack.removeLast();
4724 void printIncludeContext(const char *fileName,int line) const
4726 QListIterator<IncludeEntry> li(m_includeStack);
4728 IncludeEntry *ie=li.current();
4729 while ((ie=li.current()))
4732 IncludeEntry *next=li.current();
4733 if (ie->type()==IncludeEntry::Template)
4737 warn(fileName,line," inside template '%s' included from template '%s' at line %d",ie->fileName().data(),next->fileName().data(),ie->line());
4740 else // ie->type()==IncludeEntry::Block
4742 warn(fileName,line," included by block '%s' inside template '%s' at line %d",ie->blockName().data(),
4743 ie->fileName().data(),ie->line());
4749 QDict<Template> m_templateCache;
4750 //mutable int m_indent;
4751 TemplateEngine *m_engine;
4752 QList<IncludeEntry> m_includeStack;
4755 TemplateEngine::TemplateEngine()
4757 p = new Private(this);
4760 TemplateEngine::~TemplateEngine()
4765 TemplateContext *TemplateEngine::createContext() const
4767 return new TemplateContextImpl(this);
4770 void TemplateEngine::destroyContext(TemplateContext *ctx)
4775 Template *TemplateEngine::loadByName(const QCString &fileName,int line)
4777 return p->loadByName(fileName,line);
4780 void TemplateEngine::unload(Template *t)
4785 void TemplateEngine::enterBlock(const QCString &fileName,const QCString &blockName,int line)
4787 p->enterBlock(fileName,blockName,line);
4790 void TemplateEngine::leaveBlock()
4795 void TemplateEngine::printIncludeContext(const char *fileName,int line) const
4797 p->printIncludeContext(fileName,line);