1 /******************************************************************************
3 * Copyright (C) 1997-2015 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"
36 #include "resourcemgr.h"
39 #define ENABLE_TRACING 0
42 #define TRACE(x) printf x
49 //-------------------------------------------------------------------
51 static QValueList<QCString> split(const QCString &str,const QCString &sep,
52 bool allowEmptyEntries=FALSE,bool cleanup=TRUE)
54 QValueList<QCString> lst;
57 int i = str.find( sep, j );
61 if ( str.mid(j,i-j).length() > 0 )
65 lst.append(str.mid(j,i-j).stripWhiteSpace());
69 lst.append(str.mid(j,i-j));
72 else if (allowEmptyEntries)
80 int l = str.length() - 1;
81 if (str.mid(j,l-j+1).length()>0)
85 lst.append(str.mid(j,l-j+1).stripWhiteSpace());
89 lst.append(str.mid(j,l-j+1));
92 else if (allowEmptyEntries)
100 //----------------------------------------------------------------------------
102 /** Strips spaces surrounding `=` from string \a in, so
103 * `foo = 10 bar=5 baz= 'hello'` will become `foo=10 bar=5 baz='hello'`
105 static QCString removeSpacesAroundEquals(const char *s)
108 const char *p=result.data();
109 char *q = result.rawData();
113 if (c==' ') // found a space, see if there is a = as well
117 while (*t==' ' || *t=='=') { if (*t++=='=') found=TRUE; }
121 p=t; // move p to end of '\s*=\s*' sequence
126 if (q<p) result.resize(q-result.data()+1);
130 //----------------------------------------------------------------------------
133 static QCString replace(const char *s,char csrc,char cdst)
136 for (char *p=result.data();*p;p++)
138 if (*p==csrc) *p=cdst;
144 //- TemplateVariant implementation -------------------------------------------
147 TemplateVariant::TemplateVariant(TemplateStructIntf *s)
148 : m_type(Struct), m_strukt(s), m_raw(FALSE)
153 TemplateVariant::TemplateVariant(TemplateListIntf *l)
154 : m_type(List), m_list(l), m_raw(FALSE)
159 TemplateVariant::~TemplateVariant()
161 if (m_type==Struct) m_strukt->release();
162 else if (m_type==List) m_list->release();
165 TemplateVariant::TemplateVariant(const TemplateVariant &v)
166 : m_type(v.m_type), m_strukt(0), m_raw(FALSE)
172 case Bool: m_boolVal = v.m_boolVal; break;
173 case Integer: m_intVal = v.m_intVal; break;
174 case String: m_strVal = v.m_strVal; break;
175 case Struct: m_strukt = v.m_strukt; m_strukt->addRef(); break;
176 case List: m_list = v.m_list; m_list->addRef(); break;
177 case Function: m_delegate= v.m_delegate;break;
181 TemplateVariant &TemplateVariant::operator=(const TemplateVariant &v)
183 // assignment can change the type of the variable, so we have to be
184 // careful with reference counted content.
185 TemplateStructIntf *tmpStruct = m_type==Struct ? m_strukt : 0;
186 TemplateListIntf *tmpList = m_type==List ? m_list : 0;
187 Type tmpType = m_type;
194 case Bool: m_boolVal = v.m_boolVal; break;
195 case Integer: m_intVal = v.m_intVal; break;
196 case String: m_strVal = v.m_strVal; break;
197 case Struct: m_strukt = v.m_strukt; m_strukt->addRef(); break;
198 case List: m_list = v.m_list; m_list->addRef(); break;
199 case Function: m_delegate= v.m_delegate;break;
202 // release overwritten reference counted values
203 if (tmpType==Struct && tmpStruct) tmpStruct->release();
204 else if (tmpType==List && tmpList ) tmpList->release();
208 bool TemplateVariant::toBool() const
212 case None: return FALSE;
213 case Bool: return m_boolVal;
214 case Integer: return m_intVal!=0;
215 case String: return !m_strVal.isEmpty();
216 case Struct: return TRUE;
217 case List: return m_list->count()!=0;
218 case Function: return FALSE;
223 int TemplateVariant::toInt() const
228 case Bool: return m_boolVal ? 1 : 0;
229 case Integer: return m_intVal;
230 case String: return m_strVal.toInt();
231 case Struct: return 0;
232 case List: return m_list->count();
233 case Function: return 0;
238 //- Template struct implementation --------------------------------------------
241 /** @brief Private data of a template struct object */
242 class TemplateStruct::Private
245 Private() : fields(17), refCount(0)
246 { fields.setAutoDelete(TRUE); }
247 QDict<TemplateVariant> fields;
251 TemplateStruct::TemplateStruct()
256 TemplateStruct::~TemplateStruct()
261 int TemplateStruct::addRef()
263 return ++p->refCount;
266 int TemplateStruct::release()
268 int count = --p->refCount;
276 void TemplateStruct::set(const char *name,const TemplateVariant &v)
278 TemplateVariant *pv = p->fields.find(name);
279 if (pv) // change existing field
283 else // insert new field
285 p->fields.insert(name,new TemplateVariant(v));
289 TemplateVariant TemplateStruct::get(const char *name) const
291 TemplateVariant *v = p->fields.find(name);
292 return v ? *v : TemplateVariant();
295 TemplateStruct *TemplateStruct::alloc()
297 return new TemplateStruct;
300 //- Template list implementation ----------------------------------------------
303 /** @brief Private data of a template list object */
304 class TemplateList::Private
307 Private() : index(-1), refCount(0) {}
308 QValueList<TemplateVariant> elems;
314 TemplateList::TemplateList()
319 TemplateList::~TemplateList()
324 int TemplateList::addRef()
326 return ++p->refCount;
329 int TemplateList::release()
331 int count = --p->refCount;
339 int TemplateList::count() const
341 return p->elems.count();
344 void TemplateList::append(const TemplateVariant &v)
350 class TemplateListConstIterator : public TemplateListIntf::ConstIterator
353 TemplateListConstIterator(const TemplateList &l) : m_list(l) { m_index=-1; }
354 virtual ~TemplateListConstIterator() {}
355 virtual void toFirst()
357 m_it = m_list.p->elems.begin();
360 virtual void toLast()
362 m_it = m_list.p->elems.fromLast();
363 m_index=m_list.count()-1;
365 virtual void toNext()
367 if (m_it!=m_list.p->elems.end())
373 virtual void toPrev()
385 virtual bool current(TemplateVariant &v) const
387 if (m_index<0 || m_it==m_list.p->elems.end())
389 v = TemplateVariant();
399 const TemplateList &m_list;
400 QValueList<TemplateVariant>::ConstIterator m_it;
404 TemplateListIntf::ConstIterator *TemplateList::createIterator() const
406 return new TemplateListConstIterator(*this);
409 TemplateVariant TemplateList::at(int index) const
411 if (index>=0 && index<(int)p->elems.count())
413 return p->elems[index];
417 return TemplateVariant();
421 TemplateList *TemplateList::alloc()
423 return new TemplateList;
426 //- Operator types ------------------------------------------------------------
428 /** @brief Class representing operators that can appear in template expressions */
432 /* Operator precedence (low to high)
446 Or, And, Not, In, Equal, NotEqual, Less, Greater, LessEqual,
447 GreaterEqual, Plus, Minus, Multiply, Divide, Modulo, Filter, Colon, Comma,
448 LeftParen, RightParen,
452 static const char *toString(Type op)
456 case Or: return "or";
457 case And: return "and";
458 case Not: return "not";
459 case In: return "in";
460 case Equal: return "==";
461 case NotEqual: return "!=";
462 case Less: return "<";
463 case Greater: return ">";
464 case LessEqual: return "<=";
465 case GreaterEqual: return ">=";
466 case Plus: return "+";
467 case Minus: return "-";
468 case Multiply: return "*";
469 case Divide: return "/";
470 case Modulo: return "%";
471 case Filter: return "|";
472 case Colon: return ":";
473 case Comma: return ",";
474 case LeftParen: return "(";
475 case RightParen: return ")";
476 case Last: return "?";
482 //-----------------------------------------------------------------------------
484 class TemplateNodeBlock;
486 /** @brief Class holding stacks of blocks available in the context */
487 class TemplateBlockContext
490 TemplateBlockContext();
491 TemplateNodeBlock *get(const QCString &name) const;
492 TemplateNodeBlock *pop(const QCString &name) const;
493 void add(TemplateNodeBlock *block);
494 void add(TemplateBlockContext *ctx);
495 void push(TemplateNodeBlock *block);
498 QDict< QList<TemplateNodeBlock> > m_blocks;
501 /** @brief A container to store a key-value pair */
502 struct TemplateKeyValue
504 TemplateKeyValue() {}
505 TemplateKeyValue(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
507 TemplateVariant value;
510 /** @brief Internal class representing the implementation of a template
512 class TemplateContextImpl : public TemplateContext
515 TemplateContextImpl(const TemplateEngine *e);
516 virtual ~TemplateContextImpl();
518 // TemplateContext methods
521 void set(const char *name,const TemplateVariant &v);
522 TemplateVariant get(const QCString &name) const;
523 const TemplateVariant *getRef(const QCString &name) const;
524 void setOutputDirectory(const QCString &dir)
525 { m_outputDir = dir; }
526 void setEscapeIntf(const QCString &ext,TemplateEscapeIntf *intf)
528 int i=(!ext.isEmpty() && ext.at(0)=='.') ? 1 : 0;
529 m_escapeIntfDict.insert(ext.mid(i),new TemplateEscapeIntf*(intf));
531 void selectEscapeIntf(const QCString &ext)
532 { TemplateEscapeIntf **ppIntf = m_escapeIntfDict.find(ext);
533 m_activeEscapeIntf = ppIntf ? *ppIntf : 0;
535 void setActiveEscapeIntf(TemplateEscapeIntf *intf) { m_activeEscapeIntf = intf; }
536 void setSpacelessIntf(TemplateSpacelessIntf *intf) { m_spacelessIntf = intf; }
539 TemplateBlockContext *blockContext();
540 TemplateVariant getPrimary(const QCString &name) const;
541 void setLocation(const QCString &templateName,int line)
542 { m_templateName=templateName; m_line=line; }
543 QCString templateName() const { return m_templateName; }
544 int line() const { return m_line; }
545 QCString outputDirectory() const { return m_outputDir; }
546 TemplateEscapeIntf *escapeIntf() const { return m_activeEscapeIntf; }
547 TemplateSpacelessIntf *spacelessIntf() const { return m_spacelessIntf; }
548 void enableSpaceless(bool b) { if (b && !m_spacelessEnabled) m_spacelessIntf->reset();
549 m_spacelessEnabled=b;
551 bool spacelessEnabled() const { return m_spacelessEnabled && m_spacelessIntf; }
552 void enableTabbing(bool b) { m_tabbingEnabled=b;
553 if (m_activeEscapeIntf) m_activeEscapeIntf->enableTabbing(b);
555 bool tabbingEnabled() const { return m_tabbingEnabled; }
556 bool needsRecoding() const { return !m_encoding.isEmpty(); }
557 QCString encoding() const { return m_encoding; }
558 void setEncoding(const QCString &file,int line,const QCString &enc);
559 QCString recode(const QCString &s);
560 void warn(const char *fileName,int line,const char *fmt,...) const;
562 // index related functions
563 void openSubIndex(const QCString &indexName);
564 void closeSubIndex(const QCString &indexName);
565 void addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments);
568 const TemplateEngine *m_engine;
569 QCString m_templateName;
571 QCString m_outputDir;
572 QList< QDict<TemplateVariant> > m_contextStack;
573 TemplateBlockContext m_blockContext;
574 QDict<TemplateEscapeIntf*> m_escapeIntfDict;
575 TemplateEscapeIntf *m_activeEscapeIntf;
576 TemplateSpacelessIntf *m_spacelessIntf;
577 bool m_spacelessEnabled;
578 bool m_tabbingEnabled;
579 TemplateAutoRef<TemplateStruct> m_indices;
580 QDict< QStack<TemplateVariant> > m_indexStacks;
585 //-----------------------------------------------------------------------------
587 /** @brief The implementation of the "add" filter */
591 static int variantIntValue(const TemplateVariant &v,bool &isInt)
593 isInt = v.type()==TemplateVariant::Integer;
594 if (!isInt && v.type()==TemplateVariant::String)
596 return v.toString().toInt(&isInt);
598 return isInt ? v.toInt() : 0;
600 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
607 int lhsValue = variantIntValue(v,lhsIsInt);
609 int rhsValue = variantIntValue(arg,rhsIsInt);
610 if (lhsIsInt && rhsIsInt)
612 return lhsValue+rhsValue;
614 else if (v.type()==TemplateVariant::String && arg.type()==TemplateVariant::String)
616 return TemplateVariant(v.toString() + arg.toString());
625 //-----------------------------------------------------------------------------
627 /** @brief The implementation of the "get" filter */
631 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
633 if (v.isValid() && v.type()==TemplateVariant::Struct && arg.type()==TemplateVariant::String)
635 TemplateVariant result = v.toStruct()->get(arg.toString());
636 //printf("\nok[%s]=%d\n",arg.toString().data(),result.type());
641 //printf("\nnok[%s]\n",arg.toString().data());
647 //-----------------------------------------------------------------------------
649 /** @brief The implementation of the "raw" filter */
653 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
655 if (v.isValid() && (v.type()==TemplateVariant::String || v.type()==TemplateVariant::Integer))
657 return TemplateVariant(v.toString(),TRUE);
666 //-----------------------------------------------------------------------------
668 /** @brief The implementation of the "list" filter */
672 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
676 if (v.type()==TemplateVariant::List) // input is already a list
680 // create a list with v as the only element
681 TemplateList *list = TemplateList::alloc();
692 //-----------------------------------------------------------------------------
693 /** @brief The implementation of the "texlabel" filter */
697 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
699 if (v.isValid() && (v.type()==TemplateVariant::String))
701 return TemplateVariant(latexEscapeLabelName(v.toString()),TRUE);
710 //-----------------------------------------------------------------------------
712 /** @brief The implementation of the "texindex" filter */
716 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
718 if (v.isValid() && (v.type()==TemplateVariant::String))
720 return TemplateVariant(latexEscapeIndexChars(v.toString()),TRUE);
729 //-----------------------------------------------------------------------------
731 /** @brief The implementation of the "append" filter */
735 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
737 if ((v.type()==TemplateVariant::String || v.type()==TemplateVariant::Integer) &&
738 (arg.type()==TemplateVariant::String || arg.type()==TemplateVariant::Integer))
740 return TemplateVariant(v.toString() + arg.toString());
749 //-----------------------------------------------------------------------------
751 /** @brief The implementation of the "prepend" filter */
755 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
757 if ((v.type()==TemplateVariant::String || v.type()==TemplateVariant::Integer) &&
758 arg.type()==TemplateVariant::String)
760 return TemplateVariant(arg.toString() + v.toString());
769 //--------------------------------------------------------------------
771 /** @brief The implementation of the "length" filter */
775 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
779 return TemplateVariant();
781 if (v.type()==TemplateVariant::List)
783 return TemplateVariant(v.toList()->count());
785 else if (v.type()==TemplateVariant::String)
787 return TemplateVariant((int)v.toString().length());
791 return TemplateVariant();
796 //--------------------------------------------------------------------
798 /** @brief The implementation of the "default" filter */
802 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
808 else if (v.type()==TemplateVariant::String && v.toString().isEmpty())
819 //--------------------------------------------------------------------
821 /** @brief The implementation of the "flatten" filter */
825 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
827 if (!v.isValid() || v.type()!=TemplateVariant::List)
833 TemplateList *list = TemplateList::alloc();
834 flatten(v.toList(),list);
835 return TemplateVariant(list);
840 static void flatten(TemplateListIntf *tree,TemplateList *list)
842 TemplateListIntf::ConstIterator *it = tree->createIterator();
843 TemplateVariant item;
844 for (it->toFirst();(it->current(item));it->toNext())
846 TemplateStructIntf *s = item.toStruct();
850 // if s has "children" then recurse into the children
851 TemplateVariant children = s->get("children");
852 if (children.isValid() && children.type()==TemplateVariant::List)
854 flatten(children.toList(),list);
866 //--------------------------------------------------------------------
868 /** @brief The implementation of the "listsort" filter */
873 ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
875 TemplateVariant value;
877 class SortList : public QList<ListElem>
880 SortList() { setAutoDelete(TRUE); }
882 int compareValues(const ListElem *item1,const ListElem *item2) const
884 return qstrcmp(item1->key,item2->key);
888 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
890 if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
892 //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
893 TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
895 TemplateVariant item;
896 TemplateList *result = TemplateList::alloc();
898 // create list of items based on v using the data in args as a sort key
900 for (it->toFirst();(it->current(item));it->toNext())
902 TemplateStructIntf *s = item.toStruct();
905 QCString sortKey = determineSortKey(s,args.toString());
906 sortList.append(new ListElem(sortKey,item));
907 //printf("sortKey=%s\n",sortKey.data());
915 // add sorted items to the result list
916 QListIterator<ListElem> sit(sortList);
918 for (sit.toFirst();(elem=sit.current());++sit)
920 result->append(elem->value);
928 static QCString determineSortKey(TemplateStructIntf *s,const QCString &arg)
932 while ((i=arg.find("{{",p))!=-1)
934 result+=arg.mid(p,i-p);
935 int j=arg.find("}}",i+2);
938 QCString var = arg.mid(i+2,j-i-2);
939 TemplateVariant val=s->get(var);
940 //printf("found argument %s value=%s\n",var.data(),val.toString().data());
941 result+=val.toString();
949 result+=arg.right(arg.length()-p);
954 //--------------------------------------------------------------------
956 /** @brief The implementation of the "groupBy" filter */
961 ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
963 TemplateVariant value;
965 class SortList : public QList<ListElem>
968 SortList() { setAutoDelete(TRUE); }
970 int compareValues(const ListElem *item1,const ListElem *item2) const
972 return qstrcmp(item1->key,item2->key);
976 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
978 if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
980 //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
981 TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
983 TemplateVariant item;
984 TemplateList *result = TemplateList::alloc();
986 // create list of items based on v using the data in args as a sort key
988 for (it->toFirst();(it->current(item));it->toNext())
990 TemplateStructIntf *s = item.toStruct();
993 QCString sortKey = determineSortKey(s,args.toString());
994 sortList.append(new ListElem(sortKey,item));
995 //printf("sortKey=%s\n",sortKey.data());
1003 // add sorted items to the result list
1004 QListIterator<ListElem> sit(sortList);
1006 TemplateList *groupList=0;
1008 for (sit.toFirst();(elem=sit.current());++sit)
1010 if (groupList==0 || elem->key!=prevKey)
1012 groupList = TemplateList::alloc();
1013 result->append(groupList);
1014 prevKey = elem->key;
1016 groupList->append(elem->value);
1024 static QCString determineSortKey(TemplateStructIntf *s,const QCString &attribName)
1026 TemplateVariant v = s->get(attribName);
1027 return v.toString();
1031 //--------------------------------------------------------------------
1033 /** @brief The implementation of the "relative" filter */
1034 class FilterRelative
1037 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1039 if (v.isValid() && v.type()==TemplateVariant::String && v.toString().left(2)=="..")
1050 //--------------------------------------------------------------------
1052 /** @brief The implementation of the "paginate" filter */
1053 class FilterPaginate
1056 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1058 if (v.isValid() && v.type()==TemplateVariant::List &&
1059 args.isValid() && args.type()==TemplateVariant::Integer)
1061 int pageSize = args.toInt();
1062 TemplateListIntf *list = v.toList();
1063 TemplateList *result = TemplateList::alloc();
1064 TemplateListIntf::ConstIterator *it = list->createIterator();
1065 TemplateVariant item;
1066 TemplateList *pageList=0;
1068 for (it->toFirst();(it->current(item));it->toNext())
1072 pageList = TemplateList::alloc();
1073 result->append(pageList);
1075 pageList->append(item);
1077 if (i==pageSize) // page is full start a new one
1086 else // wrong arguments
1093 //--------------------------------------------------------------------
1095 /** @brief The implementation of the "alphaIndex" filter */
1096 class FilterAlphaIndex
1101 ListElem(uint k,const TemplateVariant &v) : key(k), value(v) {}
1103 TemplateVariant value;
1105 class SortList : public QList<ListElem>
1108 SortList() { setAutoDelete(TRUE); }
1110 int compareValues(const ListElem *item1,const ListElem *item2) const
1112 return item1->key-item2->key;
1115 static QCString keyToLetter(uint startLetter)
1117 return QString(QChar(startLetter)).utf8();
1119 static QCString keyToLabel(uint startLetter)
1121 char s[11]; // 0x12345678 + '\0'
1122 if ((startLetter>='0' && startLetter<='9') ||
1123 (startLetter>='a' && startLetter<='z') ||
1124 (startLetter>='A' && startLetter<='Z'))
1127 if (startLetter>='0' && startLetter<='9') s[i++] = 'x';
1128 s[i++]=tolower((char)startLetter);
1133 const char hex[]="0123456789abcdef";
1136 if (startLetter>(1<<24)) // 4 byte character
1138 s[i++]=hex[(startLetter>>28)&0xf];
1139 s[i++]=hex[(startLetter>>24)&0xf];
1141 if (startLetter>(1<<16)) // 3 byte character
1143 s[i++]=hex[(startLetter>>20)&0xf];
1144 s[i++]=hex[(startLetter>>16)&0xf];
1146 if (startLetter>(1<<8)) // 2 byte character
1148 s[i++]=hex[(startLetter>>12)&0xf];
1149 s[i++]=hex[(startLetter>>8)&0xf];
1151 // one byte character
1152 s[i++]=hex[(startLetter>>4)&0xf];
1153 s[i++]=hex[(startLetter>>0)&0xf];
1158 static uint determineSortKey(TemplateStructIntf *s,const QCString &attribName)
1160 TemplateVariant v = s->get(attribName);
1161 int index = getPrefixIndex(v.toString());
1162 return getUtf8CodeToUpper(v.toString(),index);
1166 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1168 if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
1170 //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
1171 TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
1173 TemplateVariant item;
1174 TemplateList *result = TemplateList::alloc();
1176 // create list of items based on v using the data in args as a sort key
1178 for (it->toFirst();(it->current(item));it->toNext())
1180 TemplateStructIntf *s = item.toStruct();
1183 uint sortKey = determineSortKey(s,args.toString());
1184 sortList.append(new ListElem(sortKey,item));
1185 //printf("sortKey=%s\n",sortKey.data());
1193 // create an index from the sorted list
1195 QListIterator<ListElem> sit(sortList);
1197 TemplateStruct *indexNode = 0;
1198 TemplateList *indexList = 0;
1199 for (sit.toFirst();(elem=sit.current());++sit)
1201 if (letter!=elem->key || indexNode==0)
1203 // create new indexNode
1204 indexNode = TemplateStruct::alloc();
1205 indexList = TemplateList::alloc();
1206 indexNode->set("letter", keyToLetter(elem->key));
1207 indexNode->set("label", keyToLabel(elem->key));
1208 indexNode->set("items",indexList);
1209 result->append(indexNode);
1212 indexList->append(elem->value);
1220 //--------------------------------------------------------------------
1222 /** @brief The implementation of the "default" filter */
1223 class FilterStripPath
1226 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1228 if (!v.isValid() || v.type()!=TemplateVariant::String)
1232 QCString result = v.toString();
1233 int i=result.findRev('/');
1236 result=result.mid(i+1);
1238 i=result.findRev('\\');
1241 result=result.mid(i+1);
1247 //--------------------------------------------------------------------
1249 /** @brief The implementation of the "default" filter */
1253 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1255 if (!v.isValid() || v.type()!=TemplateVariant::String)
1259 QCString s = v.toString();
1260 return substitute(s," "," ");
1264 //--------------------------------------------------------------------
1266 /** @brief The implementation of the "divisibleby" filter */
1267 class FilterDivisibleBy
1270 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &n)
1272 if (!v.isValid() || !n.isValid())
1274 return TemplateVariant();
1276 if (v.type()==TemplateVariant::Integer && n.type()==TemplateVariant::Integer)
1281 return TemplateVariant((v.toInt()%ni)==0);
1285 return TemplateVariant(FALSE);
1290 return TemplateVariant();
1295 //--------------------------------------------------------------------
1297 /** @brief The implementation of the "isRelativeURL" filter */
1298 class FilterIsRelativeURL
1301 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1303 if (v.isValid() && v.type()==TemplateVariant::String)
1305 QCString s = v.toString();
1306 if (!s.isEmpty() && s.at(0)=='!') return TRUE;
1312 //--------------------------------------------------------------------
1314 /** @brief The implementation of the "isRelativeURL" filter */
1315 class FilterIsAbsoluteURL
1318 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1320 if (v.isValid() && v.type()==TemplateVariant::String)
1322 QCString s = v.toString();
1323 if (!s.isEmpty() && s.at(0)=='^') return TRUE;
1329 //--------------------------------------------------------------------
1331 /** @brief The implementation of the "decodeURL" filter
1332 * The leading character is removed from the value in case it is a ^ or !.
1333 * - ^ is used to encode a absolute URL
1334 * - ! is used to encode a relative URL
1336 class FilterDecodeURL
1339 static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1341 if (v.isValid() && v.type()==TemplateVariant::String)
1343 QCString s = v.toString();
1344 if (!s.isEmpty() && (s.at(0)=='^' || s.at(0)=='!'))
1354 //--------------------------------------------------------------------
1356 /** @brief Factory singleton for registering and creating filters */
1357 class TemplateFilterFactory
1360 typedef TemplateVariant (FilterFunction)(const TemplateVariant &v,const TemplateVariant &arg);
1362 static TemplateFilterFactory *instance()
1364 static TemplateFilterFactory *instance = 0;
1365 if (instance==0) instance = new TemplateFilterFactory;
1369 TemplateVariant apply(const QCString &name,const TemplateVariant &v,const TemplateVariant &arg, bool &ok)
1371 FilterFunction *func = (FilterFunction*)m_registry.find(name);
1375 return (*func)(v,arg);
1384 void registerFilter(const QCString &name,FilterFunction *func)
1386 m_registry.insert(name,(void*)func);
1389 /** @brief Helper class for registering a filter function */
1390 template<class T> class AutoRegister
1393 AutoRegister<T>(const QCString &key)
1395 TemplateFilterFactory::instance()->registerFilter(key,&T::apply);
1400 QDict<void> m_registry;
1403 // register a handlers for each filter we support
1404 static TemplateFilterFactory::AutoRegister<FilterAdd> fAdd("add");
1405 static TemplateFilterFactory::AutoRegister<FilterGet> fGet("get");
1406 static TemplateFilterFactory::AutoRegister<FilterRaw> fRaw("raw");
1407 static TemplateFilterFactory::AutoRegister<FilterList> fList("list");
1408 static TemplateFilterFactory::AutoRegister<FilterAppend> fAppend("append");
1409 static TemplateFilterFactory::AutoRegister<FilterLength> fLength("length");
1410 static TemplateFilterFactory::AutoRegister<FilterNoWrap> fNoWrap("nowrap");
1411 static TemplateFilterFactory::AutoRegister<FilterFlatten> fFlatten("flatten");
1412 static TemplateFilterFactory::AutoRegister<FilterDefault> fDefault("default");
1413 static TemplateFilterFactory::AutoRegister<FilterPrepend> fPrepend("prepend");
1414 static TemplateFilterFactory::AutoRegister<FilterGroupBy> fGroupBy("groupBy");
1415 static TemplateFilterFactory::AutoRegister<FilterRelative> fRelative("relative");
1416 static TemplateFilterFactory::AutoRegister<FilterListSort> fListSort("listsort");
1417 static TemplateFilterFactory::AutoRegister<FilterTexLabel> fTexLabel("texLabel");
1418 static TemplateFilterFactory::AutoRegister<FilterTexIndex> fTexIndex("texIndex");
1419 static TemplateFilterFactory::AutoRegister<FilterPaginate> fPaginate("paginate");
1420 static TemplateFilterFactory::AutoRegister<FilterStripPath> fStripPath("stripPath");
1421 static TemplateFilterFactory::AutoRegister<FilterDecodeURL> fDecodeURL("decodeURL");
1422 static TemplateFilterFactory::AutoRegister<FilterAlphaIndex> fAlphaIndex("alphaIndex");
1423 static TemplateFilterFactory::AutoRegister<FilterDivisibleBy> fDivisibleBy("divisibleby");
1424 static TemplateFilterFactory::AutoRegister<FilterIsRelativeURL> fIsRelativeURL("isRelativeURL");
1425 static TemplateFilterFactory::AutoRegister<FilterIsAbsoluteURL> fIsAbsoluteURL("isAbsoluteURL");
1427 //--------------------------------------------------------------------
1429 /** @brief Base class for all nodes in the abstract syntax tree of an
1435 virtual ~ExprAst() {}
1436 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(); }
1439 /** @brief Class representing a number in the AST */
1440 class ExprAstNumber : public ExprAst
1443 ExprAstNumber(int num) : m_number(num)
1444 { TRACE(("ExprAstNumber(%d)\n",num)); }
1445 int number() const { return m_number; }
1446 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_number); }
1451 /** @brief Class representing a variable in the AST */
1452 class ExprAstVariable : public ExprAst
1455 ExprAstVariable(const char *name) : m_name(name)
1456 { TRACE(("ExprAstVariable(%s)\n",name)); }
1457 const QCString &name() const { return m_name; }
1458 virtual TemplateVariant resolve(TemplateContext *c)
1460 TemplateVariant v = c->get(m_name);
1461 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1464 if (ci) ci->warn(ci->templateName(),ci->line(),"undefined variable '%s' in expression",m_name.data());
1472 class ExprAstFunctionVariable : public ExprAst
1475 ExprAstFunctionVariable(ExprAst *var,const QList<ExprAst> &args)
1476 : m_var(var), m_args(args)
1477 { TRACE(("ExprAstFunctionVariable()\n"));
1478 m_args.setAutoDelete(TRUE);
1480 ~ExprAstFunctionVariable()
1484 virtual TemplateVariant resolve(TemplateContext *c)
1486 QValueList<TemplateVariant> args;
1487 for (uint i=0;i<m_args.count();i++)
1489 TemplateVariant v = m_args.at(i)->resolve(c);
1492 TemplateVariant v = m_var->resolve(c);
1493 if (v.type()==TemplateVariant::Function)
1501 QList<ExprAst> m_args;
1504 /** @brief Class representing a filter in the AST */
1505 class ExprAstFilter : public ExprAst
1508 ExprAstFilter(const char *name,ExprAst *arg) : m_name(name), m_arg(arg)
1509 { TRACE(("ExprAstFilter(%s)\n",name)); }
1510 ~ExprAstFilter() { delete m_arg; }
1511 const QCString &name() const { return m_name; }
1512 TemplateVariant apply(const TemplateVariant &v,TemplateContext *c)
1514 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1515 if (ci==0) return v; // should not happen
1516 TRACE(("Applying filter '%s' to '%s' (type=%d)\n",m_name.data(),v.toString().data(),v.type()));
1517 TemplateVariant arg;
1518 if (m_arg) arg = m_arg->resolve(c);
1520 TemplateVariant result = TemplateFilterFactory::instance()->apply(m_name,v,arg,ok);
1523 ci->warn(ci->templateName(),ci->line(),"unknown filter '%s'",m_name.data());
1532 /** @brief Class representing a filter applied to an expression in the AST */
1533 class ExprAstFilterAppl : public ExprAst
1536 ExprAstFilterAppl(ExprAst *expr,ExprAstFilter *filter)
1537 : m_expr(expr), m_filter(filter)
1538 { TRACE(("ExprAstFilterAppl\n")); }
1539 ~ExprAstFilterAppl() { delete m_expr; delete m_filter; }
1540 virtual TemplateVariant resolve(TemplateContext *c)
1542 return m_filter->apply(m_expr->resolve(c),c);
1546 ExprAstFilter *m_filter;
1549 /** @brief Class representing a string literal in the AST */
1550 class ExprAstLiteral : public ExprAst
1553 ExprAstLiteral(const char *lit) : m_literal(lit)
1554 { TRACE(("ExprAstLiteral(%s)\n",lit)); }
1555 const QCString &literal() const { return m_literal; }
1556 virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_literal); }
1561 /** @brief Class representing a negation (not) operator in the AST */
1562 class ExprAstNegate : public ExprAst
1565 ExprAstNegate(ExprAst *expr) : m_expr(expr)
1566 { TRACE(("ExprAstNegate\n")); }
1567 ~ExprAstNegate() { delete m_expr; }
1568 virtual TemplateVariant resolve(TemplateContext *c)
1569 { return TemplateVariant(!m_expr->resolve(c).toBool()); }
1574 class ExprAstUnary : public ExprAst
1577 ExprAstUnary(Operator::Type op,ExprAst *exp) : m_operator(op), m_exp(exp)
1578 { TRACE(("ExprAstUnary %s\n",Operator::toString(op))); }
1579 ~ExprAstUnary() { delete m_exp; }
1580 virtual TemplateVariant resolve(TemplateContext *c)
1582 TemplateVariant exp = m_exp->resolve(c);
1585 case Operator::Minus:
1586 return -exp.toInt();
1588 return TemplateVariant();
1592 Operator::Type m_operator;
1596 /** @brief Class representing a binary operator in the AST */
1597 class ExprAstBinary : public ExprAst
1600 ExprAstBinary(Operator::Type op,ExprAst *lhs,ExprAst *rhs)
1601 : m_operator(op), m_lhs(lhs), m_rhs(rhs)
1602 { TRACE(("ExprAstBinary %s\n",Operator::toString(op))); }
1603 ~ExprAstBinary() { delete m_lhs; delete m_rhs; }
1604 virtual TemplateVariant resolve(TemplateContext *c)
1606 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1607 if (ci==0) return TemplateVariant(); // should not happen
1608 TemplateVariant lhs = m_lhs->resolve(c);
1609 TemplateVariant rhs = m_rhs ? m_rhs->resolve(c) : TemplateVariant();
1613 return TemplateVariant(lhs.toBool() || rhs.toBool());
1615 return TemplateVariant(lhs.toBool() && rhs.toBool());
1616 case Operator::Equal:
1617 return TemplateVariant(lhs == rhs);
1618 case Operator::NotEqual:
1619 return TemplateVariant(!(lhs == rhs));
1620 case Operator::Less:
1621 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1623 return lhs.toString()<rhs.toString();
1627 return lhs.toInt()<rhs.toInt();
1629 case Operator::Greater:
1630 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1632 return !(lhs.toString()<rhs.toString());
1636 return lhs.toInt()>rhs.toInt();
1638 case Operator::LessEqual:
1639 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1641 return lhs.toString()==rhs.toString() || lhs.toString()<rhs.toString();
1645 return lhs.toInt()<=rhs.toInt();
1647 case Operator::GreaterEqual:
1648 if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1650 return lhs.toString()==rhs.toString() || !(lhs.toString()<rhs.toString());
1654 return lhs.toInt()>=rhs.toInt();
1656 case Operator::Plus:
1658 return TemplateVariant(lhs.toInt() + rhs.toInt());
1660 case Operator::Minus:
1662 return TemplateVariant(lhs.toInt() - rhs.toInt());
1664 case Operator::Multiply:
1666 return TemplateVariant(lhs.toInt() * rhs.toInt());
1668 case Operator::Divide:
1670 int denom = rhs.toInt();
1673 return TemplateVariant(lhs.toInt() / denom);
1675 else // divide by zero
1677 ci->warn(ci->templateName(),ci->line(),"division by zero while evaluating expression is undefined");
1681 case Operator::Modulo:
1683 int denom = rhs.toInt();
1686 return TemplateVariant(lhs.toInt() % denom);
1690 ci->warn(ci->templateName(),ci->line(),"modulo zero while evaluating expression is undefined");
1695 return TemplateVariant();
1699 Operator::Type m_operator;
1704 //----------------------------------------------------------
1706 /** @brief Base class of all nodes in a template's AST */
1710 TemplateNode(TemplateNode *parent) : m_parent(parent) {}
1711 virtual ~TemplateNode() {}
1713 virtual void render(FTextStream &ts, TemplateContext *c) = 0;
1715 TemplateNode *parent() { return m_parent; }
1718 TemplateNode *m_parent;
1721 //----------------------------------------------------------
1723 /** @brief Parser for templates */
1724 class TemplateParser
1727 TemplateParser(const TemplateEngine *engine,
1728 const QCString &templateName,QList<TemplateToken> &tokens);
1729 void parse(TemplateNode *parent,int line,const QStrList &stopAt,
1730 QList<TemplateNode> &nodes);
1731 bool hasNextToken() const;
1732 TemplateToken *takeNextToken();
1733 void removeNextToken();
1734 void prependToken(const TemplateToken *token);
1735 const TemplateToken *currentToken() const;
1736 QCString templateName() const { return m_templateName; }
1737 void warn(const char *fileName,int line,const char *fmt,...) const;
1739 const TemplateEngine *m_engine;
1740 QCString m_templateName;
1741 QList<TemplateToken> &m_tokens;
1744 //--------------------------------------------------------------------
1746 /** @brief Recursive decent parser for Django style template expressions.
1748 class ExpressionParser
1751 ExpressionParser(const TemplateParser *parser,int line)
1752 : m_parser(parser), m_line(line), m_tokenStream(0)
1755 virtual ~ExpressionParser()
1759 ExprAst *parse(const char *expr)
1761 if (expr==0) return 0;
1762 m_tokenStream = expr;
1764 return parseExpression();
1769 /** @brief Class representing a token within an expression. */
1773 ExprToken() : type(Unknown), num(-1), op(Operator::Or)
1778 Unknown, Operator, Number, Identifier, Literal
1787 ExprAst *parseExpression()
1789 TRACE(("{parseExpression(%s)\n",m_tokenStream));
1790 ExprAst *result = parseOrExpression();
1791 TRACE(("}parseExpression(%s)\n",m_tokenStream));
1795 ExprAst *parseOrExpression()
1797 TRACE(("{parseOrExpression(%s)\n",m_tokenStream));
1798 ExprAst *lhs = parseAndExpression();
1801 while (m_curToken.type==ExprToken::Operator &&
1802 m_curToken.op==Operator::Or)
1805 ExprAst *rhs = parseAndExpression();
1806 lhs = new ExprAstBinary(Operator::Or,lhs,rhs);
1809 TRACE(("}parseOrExpression(%s)\n",m_tokenStream));
1813 ExprAst *parseAndExpression()
1815 TRACE(("{parseAndExpression(%s)\n",m_tokenStream));
1816 ExprAst *lhs = parseNotExpression();
1819 while (m_curToken.type==ExprToken::Operator &&
1820 m_curToken.op==Operator::And)
1823 ExprAst *rhs = parseNotExpression();
1824 lhs = new ExprAstBinary(Operator::And,lhs,rhs);
1827 TRACE(("}parseAndExpression(%s)\n",m_tokenStream));
1831 ExprAst *parseNotExpression()
1833 TRACE(("{parseNotExpression(%s)\n",m_tokenStream));
1835 if (m_curToken.type==ExprToken::Operator &&
1836 m_curToken.op==Operator::Not)
1839 ExprAst *expr = parseCompareExpression();
1842 warn(m_parser->templateName(),m_line,"argument missing for not operator");
1845 result = new ExprAstNegate(expr);
1849 result = parseCompareExpression();
1851 TRACE(("}parseNotExpression(%s)\n",m_tokenStream));
1855 ExprAst *parseCompareExpression()
1857 TRACE(("{parseCompareExpression(%s)\n",m_tokenStream));
1858 ExprAst *lhs = parseAdditiveExpression();
1861 Operator::Type op = m_curToken.op;
1862 if (m_curToken.type==ExprToken::Operator &&
1863 (op==Operator::Less ||
1864 op==Operator::Greater ||
1865 op==Operator::Equal ||
1866 op==Operator::NotEqual ||
1867 op==Operator::LessEqual ||
1868 op==Operator::GreaterEqual
1873 ExprAst *rhs = parseNotExpression();
1874 lhs = new ExprAstBinary(op,lhs,rhs);
1877 TRACE(("}parseCompareExpression(%s)\n",m_tokenStream));
1881 ExprAst *parseAdditiveExpression()
1883 TRACE(("{parseAdditiveExpression(%s)\n",m_tokenStream));
1884 ExprAst *lhs = parseMultiplicativeExpression();
1887 while (m_curToken.type==ExprToken::Operator &&
1888 (m_curToken.op==Operator::Plus || m_curToken.op==Operator::Minus))
1890 Operator::Type op = m_curToken.op;
1892 ExprAst *rhs = parseMultiplicativeExpression();
1893 lhs = new ExprAstBinary(op,lhs,rhs);
1896 TRACE(("}parseAdditiveExpression(%s)\n",m_tokenStream));
1900 ExprAst *parseMultiplicativeExpression()
1902 TRACE(("{parseMultiplicativeExpression(%s)\n",m_tokenStream));
1903 ExprAst *lhs = parseUnaryExpression();
1906 while (m_curToken.type==ExprToken::Operator &&
1907 (m_curToken.op==Operator::Multiply || m_curToken.op==Operator::Divide || m_curToken.op==Operator::Modulo))
1909 Operator::Type op = m_curToken.op;
1911 ExprAst *rhs = parseUnaryExpression();
1912 lhs = new ExprAstBinary(op,lhs,rhs);
1915 TRACE(("}parseMultiplicativeExpression(%s)\n",m_tokenStream));
1919 ExprAst *parseUnaryExpression()
1921 TRACE(("{parseUnaryExpression(%s)\n",m_tokenStream));
1923 if (m_curToken.type==ExprToken::Operator)
1925 if (m_curToken.op==Operator::Plus)
1928 result = parsePrimaryExpression();
1930 else if (m_curToken.op==Operator::Minus)
1933 ExprAst *rhs = parsePrimaryExpression();
1934 result = new ExprAstUnary(m_curToken.op,rhs);
1938 result = parsePrimaryExpression();
1943 result = parsePrimaryExpression();
1945 TRACE(("}parseUnaryExpression(%s)\n",m_tokenStream));
1949 ExprAst *parsePrimaryExpression()
1951 TRACE(("{parsePrimary(%s)\n",m_tokenStream));
1953 switch (m_curToken.type)
1955 case ExprToken::Number:
1956 result = parseNumber();
1958 case ExprToken::Identifier:
1959 result = parseFilteredVariable();
1961 case ExprToken::Literal:
1962 result = parseLiteral();
1964 case ExprToken::Operator:
1965 if (m_curToken.op==Operator::LeftParen)
1967 getNextToken(); // skip over opening bracket
1968 result = parseExpression();
1969 if (m_curToken.type!=ExprToken::Operator ||
1970 m_curToken.op!=Operator::RightParen)
1972 warn(m_parser->templateName(),m_line,"missing closing parenthesis");
1976 getNextToken(); // skip over closing bracket
1981 warn(m_parser->templateName(),m_line,"unexpected operator '%s' in expression",
1982 Operator::toString(m_curToken.op));
1986 warn(m_parser->templateName(),m_line,"unexpected token in expression");
1988 TRACE(("}parsePrimary(%s)\n",m_tokenStream));
1992 ExprAst *parseNumber()
1994 TRACE(("{parseNumber(%d)\n",m_curToken.num));
1995 ExprAst *num = new ExprAstNumber(m_curToken.num);
1997 TRACE(("}parseNumber()\n"));
2001 ExprAst *parseIdentifier()
2003 TRACE(("{parseIdentifier(%s)\n",m_curToken.id.data()));
2004 ExprAst *id = new ExprAstVariable(m_curToken.id);
2006 TRACE(("}parseIdentifier()\n"));
2010 ExprAst *parseLiteral()
2012 TRACE(("{parseLiteral(%s)\n",m_curToken.id.data()));
2013 ExprAst *expr = new ExprAstLiteral(m_curToken.id);
2015 TRACE(("}parseLiteral()\n"));
2019 ExprAst *parseIdentifierOptionalArgs()
2021 TRACE(("{parseIdentifierOptionalArgs(%s)\n",m_curToken.id.data()));
2022 ExprAst *expr = parseIdentifier();
2025 if (m_curToken.type==ExprToken::Operator &&
2026 m_curToken.op==Operator::Colon)
2029 ExprAst *argExpr = parsePrimaryExpression();
2030 QList<ExprAst> args;
2031 args.append(argExpr);
2032 while (m_curToken.type==ExprToken::Operator &&
2033 m_curToken.op==Operator::Comma)
2036 argExpr = parsePrimaryExpression();
2037 args.append(argExpr);
2039 expr = new ExprAstFunctionVariable(expr,args);
2042 TRACE(("}parseIdentifierOptionalArgs()\n"));
2046 ExprAst *parseFilteredVariable()
2048 TRACE(("{parseFilteredVariable()\n"));
2049 ExprAst *expr = parseIdentifierOptionalArgs();
2052 while (m_curToken.type==ExprToken::Operator &&
2053 m_curToken.op==Operator::Filter)
2056 ExprAstFilter *filter = parseFilter();
2058 expr = new ExprAstFilterAppl(expr,filter);
2061 TRACE(("}parseFilteredVariable()\n"));
2065 ExprAstFilter *parseFilter()
2067 TRACE(("{parseFilter(%s)\n",m_curToken.id.data()));
2068 QCString filterName = m_curToken.id;
2071 if (m_curToken.type==ExprToken::Operator &&
2072 m_curToken.op==Operator::Colon)
2075 argExpr = parsePrimaryExpression();
2077 ExprAstFilter *filter = new ExprAstFilter(filterName,argExpr);
2078 TRACE(("}parseFilter()\n"));
2085 const char *p = m_tokenStream;
2088 if (p==0 || *p=='\0') return FALSE;
2089 while (*p==' ') p++; // skip over spaces
2091 if (*p=='\0') // only spaces...
2100 if (c=='=' && *(p+1)=='=') // equal
2102 m_curToken.op = Operator::Equal;
2107 if (c=='!' && *(p+1)=='=') // not equal
2109 m_curToken.op = Operator::NotEqual;
2114 if (c=='<' && *(p+1)=='=') // less or equal
2116 m_curToken.op = Operator::LessEqual;
2121 m_curToken.op = Operator::Less;
2126 if (c=='>' && *(p+1)=='=') // greater or equal
2128 m_curToken.op = Operator::GreaterEqual;
2133 m_curToken.op = Operator::Greater;
2138 m_curToken.op = Operator::LeftParen;
2142 m_curToken.op = Operator::RightParen;
2146 m_curToken.op = Operator::Filter;
2150 m_curToken.op = Operator::Plus;
2154 m_curToken.op = Operator::Minus;
2158 m_curToken.op = Operator::Multiply;
2162 m_curToken.op = Operator::Divide;
2166 m_curToken.op = Operator::Modulo;
2170 m_curToken.op = Operator::Colon;
2174 m_curToken.op = Operator::Comma;
2178 if (strncmp(p,"not ",4)==0)
2180 m_curToken.op = Operator::Not;
2185 if (strncmp(p,"and ",4)==0)
2187 m_curToken.op = Operator::And;
2192 if (strncmp(p,"or ",3)==0)
2194 m_curToken.op = Operator::Or;
2201 if (p!=q) // found an operator
2203 m_curToken.type = ExprToken::Operator;
2205 else // no token found yet
2207 if (c>='0' && c<='9') // number?
2209 m_curToken.type = ExprToken::Number;
2212 while (*np>='0' && *np<='9')
2215 m_curToken.num+=*np-'0';
2220 else if (c=='_' || (c>='a' && c<='z') || (c>='A' && c<='Z')) // identifier?
2222 m_curToken.type = ExprToken::Identifier;
2227 (c=='_' || c=='.' ||
2228 (c>='a' && c<='z') ||
2229 (c>='A' && c<='Z') ||
2237 if (m_curToken.id=="True") // treat true literal as numerical 1
2239 m_curToken.type = ExprToken::Number;
2242 else if (m_curToken.id=="False") // treat false literal as numerical 0
2244 m_curToken.type = ExprToken::Number;
2248 else if (c=='"' || c=='\'') // string literal
2250 m_curToken.type = ExprToken::Literal;
2251 m_curToken.id.resize(0);
2255 while ((c=*p) && (c!=tokenChar || (c==tokenChar && cp=='\\')))
2258 if (c!='\\' || cp=='\\') // don't add escapes
2265 if (*p==tokenChar) p++;
2268 if (p==q) // still no valid token found -> error
2270 m_curToken.type = ExprToken::Unknown;
2274 warn(m_parser->templateName(),m_line,"Found unknown token '%s' (%d) while parsing %s",s,c,m_tokenStream);
2278 //TRACE(("token type=%d op=%d num=%d id=%s\n",
2279 // m_curToken.type,m_curToken.op,m_curToken.num,m_curToken.id.data()));
2285 const TemplateParser *m_parser;
2286 ExprToken m_curToken;
2288 const char *m_tokenStream;
2291 //----------------------------------------------------------
2293 /** @brief Class representing a lexical token in a template */
2297 enum Type { Text, Variable, Block };
2298 TemplateToken(Type t,const char *d,int l) : type(t), data(d), line(l) {}
2304 //----------------------------------------------------------
2306 /** @brief Class representing a list of AST nodes in a template */
2307 class TemplateNodeList : public QList<TemplateNode>
2312 setAutoDelete(TRUE);
2314 void render(FTextStream &ts,TemplateContext *c)
2316 TRACE(("{TemplateNodeList::render\n"));
2317 QListIterator<TemplateNode> it(*this);
2319 for (it.toFirst();(tn=it.current());++it)
2323 TRACE(("}TemplateNodeList::render\n"));
2327 //----------------------------------------------------------
2329 /** @brief Internal class representing the implementation of a template */
2330 class TemplateImpl : public TemplateNode, public Template
2333 TemplateImpl(TemplateEngine *e,const QCString &name,const QCString &data,
2334 const QCString &extension);
2336 void render(FTextStream &ts, TemplateContext *c);
2338 TemplateEngine *engine() const { return m_engine; }
2339 TemplateBlockContext *blockContext() { return &m_blockContext; }
2342 TemplateEngine *m_engine;
2344 TemplateNodeList m_nodes;
2345 TemplateBlockContext m_blockContext;
2348 //----------------------------------------------------------
2350 /** @brief Weak reference wrapper for TemplateStructIntf that provides access to the
2351 * wrapped struct without holding a reference.
2353 class TemplateStructWeakRef : public TemplateStructIntf
2356 TemplateStructWeakRef(TemplateStructIntf *ref) : m_ref(ref), m_refCount(0) {}
2357 virtual TemplateVariant get(const char *name) const { return m_ref->get(name); }
2358 virtual int addRef() { return ++m_refCount; }
2359 virtual int release() { int count=--m_refCount; if (count<=0) { delete this; } return count; }
2361 TemplateStructIntf *m_ref;
2365 //----------------------------------------------------------
2367 TemplateContextImpl::TemplateContextImpl(const TemplateEngine *e)
2368 : m_engine(e), m_templateName("<unknown>"), m_line(1), m_activeEscapeIntf(0),
2369 m_spacelessIntf(0), m_spacelessEnabled(FALSE), m_tabbingEnabled(FALSE), m_indices(TemplateStruct::alloc())
2371 m_indexStacks.setAutoDelete(TRUE);
2372 m_contextStack.setAutoDelete(TRUE);
2373 m_escapeIntfDict.setAutoDelete(TRUE);
2374 m_fromUtf8 = (void*)(-1);
2376 set("index",m_indices.get());
2379 TemplateContextImpl::~TemplateContextImpl()
2384 void TemplateContextImpl::setEncoding(const QCString &templateName,int line,const QCString &enc)
2386 if (enc==m_encoding) return; // nothing changed
2387 if (m_fromUtf8!=(void *)(-1))
2389 portable_iconv_close(m_fromUtf8);
2390 m_fromUtf8 = (void*)(-1);
2395 m_fromUtf8 = portable_iconv_open(enc,"UTF-8");
2396 if (m_fromUtf8==(void*)(-1))
2398 warn(templateName,line,"unsupported character conversion: '%s'->'UTF-8'\n", enc.data());
2401 //printf("TemplateContextImpl::setEncoding(%s)\n",enc.data());
2404 QCString TemplateContextImpl::recode(const QCString &s)
2406 //printf("TemplateContextImpl::recode(%s)\n",s.data());
2407 int iSize = s.length();
2408 int oSize = iSize*4+1;
2409 QCString output(oSize);
2410 size_t iLeft = iSize;
2411 size_t oLeft = oSize;
2412 char *iPtr = s.rawData();
2413 char *oPtr = output.rawData();
2414 if (!portable_iconv(m_fromUtf8,&iPtr,&iLeft,&oPtr,&oLeft))
2416 oSize -= (int)oLeft;
2417 output.resize(oSize+1);
2418 output.at(oSize)='\0';
2427 void TemplateContextImpl::set(const char *name,const TemplateVariant &v)
2429 TemplateVariant *pv = m_contextStack.getFirst()->find(name);
2432 m_contextStack.getFirst()->remove(name);
2434 m_contextStack.getFirst()->insert(name,new TemplateVariant(v));
2437 TemplateVariant TemplateContextImpl::get(const QCString &name) const
2439 int i=name.find('.');
2440 if (i==-1) // simple name
2442 return getPrimary(name);
2446 QCString objName = name.left(i);
2447 TemplateVariant v = getPrimary(objName);
2448 QCString propName = name.mid(i+1);
2449 while (!propName.isEmpty())
2451 //printf("getPrimary(%s) type=%d:%s\n",objName.data(),v.type(),v.toString().data());
2452 if (v.type()==TemplateVariant::Struct)
2454 i = propName.find(".");
2455 int l = i==-1 ? propName.length() : i;
2456 v = v.toStruct()->get(propName.left(l));
2459 warn(m_templateName,m_line,"requesting non-existing property '%s' for object '%s'",propName.left(l).data(),objName.data());
2463 objName = propName.left(i);
2464 propName = propName.mid(i+1);
2471 else if (v.type()==TemplateVariant::List)
2473 i = propName.find(".");
2474 int l = i==-1 ? propName.length() : i;
2476 int index = propName.left(l).toInt(&b);
2479 v = v.toList()->at(index);
2483 warn(m_templateName,m_line,"list index '%s' is not valid",propName.data());
2488 propName = propName.mid(i+1);
2497 warn(m_templateName,m_line,"using . on an object '%s' is not an struct or list",objName.data());
2498 return TemplateVariant();
2505 const TemplateVariant *TemplateContextImpl::getRef(const QCString &name) const
2507 QListIterator< QDict<TemplateVariant> > it(m_contextStack);
2508 QDict<TemplateVariant> *dict;
2509 for (it.toFirst();(dict=it.current());++it)
2511 TemplateVariant *v = dict->find(name);
2514 return 0; // not found
2517 TemplateVariant TemplateContextImpl::getPrimary(const QCString &name) const
2519 const TemplateVariant *v = getRef(name);
2520 return v ? *v : TemplateVariant();
2523 void TemplateContextImpl::push()
2525 QDict<TemplateVariant> *dict = new QDict<TemplateVariant>;
2526 dict->setAutoDelete(TRUE);
2527 m_contextStack.prepend(dict);
2530 void TemplateContextImpl::pop()
2532 if (!m_contextStack.removeFirst())
2534 warn(m_templateName,m_line,"pop() called on empty context stack!\n");
2538 TemplateBlockContext *TemplateContextImpl::blockContext()
2540 return &m_blockContext;
2543 void TemplateContextImpl::warn(const char *fileName,int line,const char *fmt,...) const
2547 va_warn(fileName,line,fmt,args);
2549 m_engine->printIncludeContext(fileName,line);
2552 void TemplateContextImpl::openSubIndex(const QCString &indexName)
2554 //printf("TemplateContextImpl::openSubIndex(%s)\n",indexName.data());
2555 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2556 if (!stack || stack->isEmpty() || stack->top()->type()==TemplateVariant::List) // error: no stack yet or no entry
2558 warn(m_templateName,m_line,"opensubindex for index %s without preceding indexentry",indexName.data());
2561 // get the parent entry to add the list to
2562 TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2565 // add new list to the stack
2566 TemplateList *list = TemplateList::alloc();
2567 stack->push(new TemplateVariant(list));
2568 entry->set("children",list);
2569 entry->set("is_leaf_node",false);
2573 void TemplateContextImpl::closeSubIndex(const QCString &indexName)
2575 //printf("TemplateContextImpl::closeSubIndex(%s)\n",indexName.data());
2576 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2577 if (!stack || stack->count()<3)
2579 warn(m_templateName,m_line,"closesubindex for index %s without matching open",indexName.data());
2581 else // stack->count()>=2
2583 if (stack->top()->type()==TemplateVariant::Struct)
2585 delete stack->pop(); // pop struct
2586 delete stack->pop(); // pop list
2588 else // empty list! correct "is_left_node" attribute of the parent entry
2590 delete stack->pop(); // pop list
2591 TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2594 entry->set("is_leaf_node",true);
2598 //fprintf(stderr,"TemplateContextImpl::closeSubIndex(%s) end g_count=%d\n\n",indexName.data(),g_count);
2601 static void getPathListFunc(TemplateStructIntf *entry,TemplateList *list)
2603 TemplateVariant parent = entry->get("parent");
2604 if (parent.type()==TemplateVariant::Struct)
2606 getPathListFunc(parent.toStruct(),list);
2608 list->append(entry);
2611 static TemplateVariant getPathFunc(const void *ctx, const QValueList<TemplateVariant> &)
2613 TemplateStruct *entry = (TemplateStruct*)ctx;
2614 TemplateList *result = TemplateList::alloc();
2615 getPathListFunc(entry,result);
2619 void TemplateContextImpl::addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments)
2621 QValueListConstIterator<TemplateKeyValue> it = arguments.begin();
2622 //printf("TemplateContextImpl::addIndexEntry(%s)\n",indexName.data());
2623 //while (it!=arguments.end())
2625 // printf(" key=%s value=%s\n",(*it).key.data(),(*it).value.toString().data());
2628 TemplateVariant parent(FALSE);
2629 QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2630 if (!stack) // no stack yet, create it!
2632 stack = new QStack<TemplateVariant>;
2633 stack->setAutoDelete(TRUE);
2634 m_indexStacks.insert(indexName,stack);
2636 TemplateList *list = 0;
2637 if (stack->isEmpty()) // first item, create empty list and add it to the index
2639 list = TemplateList::alloc();
2640 stack->push(new TemplateVariant(list));
2641 m_indices->set(indexName,list); // make list available under index
2643 else // stack not empty
2645 if (stack->top()->type()==TemplateVariant::Struct) // already an entry in the list
2647 // remove current entry from the stack
2648 delete stack->pop();
2650 else // first entry after opensubindex
2652 ASSERT(stack->top()->type()==TemplateVariant::List);
2654 if (stack->count()>1)
2656 TemplateVariant *tmp = stack->pop();
2657 // To prevent a cyclic dependency between parent and child which causes a memory
2658 // leak, we wrap the parent into a weak reference version.
2659 parent = new TemplateStructWeakRef(stack->top()->toStruct());
2661 ASSERT(parent.type()==TemplateVariant::Struct);
2663 // get list to add new item
2664 list = dynamic_cast<TemplateList*>(stack->top()->toList());
2666 TemplateStruct *entry = TemplateStruct::alloc();
2667 // add user specified fields to the entry
2668 for (it=arguments.begin();it!=arguments.end();++it)
2670 entry->set((*it).key,(*it).value);
2672 if (list->count()>0)
2674 TemplateStruct *lastEntry = dynamic_cast<TemplateStruct*>(list->at(list->count()-1).toStruct());
2675 lastEntry->set("last",false);
2677 entry->set("is_leaf_node",true);
2678 entry->set("first",list->count()==0);
2679 entry->set("index",list->count());
2680 entry->set("parent",parent);
2681 entry->set("path",TemplateVariant::Delegate::fromFunction(entry,getPathFunc));
2682 entry->set("last",true);
2683 stack->push(new TemplateVariant(entry));
2684 list->append(entry);
2687 //----------------------------------------------------------
2689 /** @brief Class representing a piece of plain text in a template */
2690 class TemplateNodeText : public TemplateNode
2693 TemplateNodeText(TemplateParser *,TemplateNode *parent,int,const QCString &data)
2694 : TemplateNode(parent), m_data(data)
2696 TRACE(("TemplateNodeText('%s')\n",replace(data,'\n',' ').data()));
2699 void render(FTextStream &ts, TemplateContext *c)
2701 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2702 if (ci==0) return; // should not happen
2703 //printf("TemplateNodeText::render(%s) needsRecoding=%d ci=%p\n",m_data.data(),ci->needsRecoding(),ci);
2704 if (ci->spacelessEnabled())
2706 if (ci->needsRecoding())
2708 ts << ci->recode(ci->spacelessIntf()->remove(m_data));
2712 ts << ci->spacelessIntf()->remove(m_data);
2717 if (ci->needsRecoding())
2719 ts << ci->recode(m_data);
2731 //----------------------------------------------------------
2733 /** @brief Class representing a variable in a template */
2734 class TemplateNodeVariable : public TemplateNode
2737 TemplateNodeVariable(TemplateParser *parser,TemplateNode *parent,int line,const QCString &var)
2738 : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line)
2740 TRACE(("TemplateNodeVariable(%s)\n",var.data()));
2741 ExpressionParser expParser(parser,line);
2742 m_var = expParser.parse(var);
2745 parser->warn(m_templateName,line,"invalid expression '%s' for variable",var.data());
2748 ~TemplateNodeVariable()
2753 void render(FTextStream &ts, TemplateContext *c)
2755 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2756 if (ci==0) return; // should not happen
2757 ci->setLocation(m_templateName,m_line);
2760 TemplateVariant v = m_var->resolve(c);
2761 if (v.type()==TemplateVariant::Function)
2763 v = v.call(QValueList<TemplateVariant>());
2765 if (ci->escapeIntf() && !v.raw())
2767 if (ci->needsRecoding())
2769 ts << ci->recode(ci->escapeIntf()->escape(v.toString()));
2773 ts << ci->escapeIntf()->escape(v.toString());
2778 if (ci->needsRecoding())
2780 ts << ci->recode(v.toString());
2791 QCString m_templateName;
2794 QList<ExprAst> m_args;
2797 //----------------------------------------------------------
2799 /** @brief Helper class for creating template AST tag nodes and returning
2800 * the template for a given node.
2802 template<class T> class TemplateNodeCreator : public TemplateNode
2805 TemplateNodeCreator(TemplateParser *parser,TemplateNode *parent,int line)
2806 : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line) {}
2807 static TemplateNode *createInstance(TemplateParser *parser,
2808 TemplateNode *parent,
2810 const QCString &data)
2812 return new T(parser,parent,line,data);
2814 TemplateImpl *getTemplate()
2816 TemplateNode *root = this;
2817 while (root && root->parent())
2819 root = root->parent();
2821 return dynamic_cast<TemplateImpl*>(root);
2824 void mkpath(TemplateContextImpl *ci,const QCString &fileName)
2826 int i=fileName.find('/');
2827 QCString outputDir = ci->outputDirectory();
2832 rootDir.setPath(QDir::currentDirPath());
2833 if (!rootDir.mkdir(outputDir))
2835 err("tag OUTPUT_DIRECTORY: Output directory `%s' does not "
2836 "exist and cannot be created\n",outputDir.data());
2839 d.setPath(outputDir);
2842 while (i!=-1) // fileName contains path part
2846 bool ok = d.mkdir(fileName.mid(j,i-j));
2848 QCString dirName = outputDir+'/'+fileName.left(i);
2852 i=fileName.find('/',i+1);
2855 QCString m_templateName;
2859 //----------------------------------------------------------
2861 /** @brief Class representing an 'if' tag in a template */
2862 class TemplateNodeIf : public TemplateNodeCreator<TemplateNodeIf>
2865 TemplateNodeIf(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) :
2866 TemplateNodeCreator<TemplateNodeIf>(parser,parent,line)
2868 m_ifGuardedNodes.setAutoDelete(TRUE);
2869 TRACE(("{TemplateNodeIf(%s)\n",data.data()));
2872 parser->warn(m_templateName,line,"missing argument for if tag");
2875 stopAt.append("endif");
2876 stopAt.append("elif");
2877 stopAt.append("else");
2880 GuardedNodes *guardedNodes = new GuardedNodes;
2881 ExpressionParser ex(parser,line);
2882 guardedNodes->line = line;
2883 guardedNodes->guardAst = ex.parse(data);
2884 parser->parse(this,line,stopAt,guardedNodes->trueNodes);
2885 m_ifGuardedNodes.append(guardedNodes);
2886 TemplateToken *tok = parser->takeNextToken();
2889 while (tok && tok->data.left(5)=="elif ")
2891 ExpressionParser ex(parser,line);
2892 guardedNodes = new GuardedNodes;
2893 guardedNodes->line = tok->line;
2894 guardedNodes->guardAst = ex.parse(tok->data.mid(5));
2895 parser->parse(this,tok->line,stopAt,guardedNodes->trueNodes);
2896 m_ifGuardedNodes.append(guardedNodes);
2897 // proceed to the next token
2899 tok = parser->takeNextToken();
2903 if (tok && tok->data=="else")
2905 stopAt.removeLast(); // remove "else"
2906 stopAt.removeLast(); // remove "elif"
2907 parser->parse(this,line,stopAt,m_falseNodes);
2908 parser->removeNextToken(); // skip over endif
2911 TRACE(("}TemplateNodeIf(%s)\n",data.data()));
2917 void render(FTextStream &ts, TemplateContext *c)
2919 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2920 if (ci==0) return; // should not happen
2921 ci->setLocation(m_templateName,m_line);
2922 //printf("TemplateNodeIf::render #trueNodes=%d #falseNodes=%d\n",m_trueNodes.count(),m_falseNodes.count());
2923 bool processed=FALSE;
2924 QListIterator<GuardedNodes> li(m_ifGuardedNodes);
2925 GuardedNodes *nodes;
2926 for (li.toFirst();(nodes=li.current()) && !processed;++li)
2928 if (nodes->guardAst)
2930 TemplateVariant guardValue = nodes->guardAst->resolve(c);
2931 if (guardValue.toBool()) // render nodes for the first guard that evaluated to 'true'
2933 nodes->trueNodes.render(ts,c);
2939 ci->warn(m_templateName,nodes->line,"invalid expression for if/elif");
2944 // all guards are false, render 'else' nodes
2945 m_falseNodes.render(ts,c);
2951 GuardedNodes() : guardAst(0) {}
2952 ~GuardedNodes() { delete guardAst; }
2955 TemplateNodeList trueNodes;
2957 QList<GuardedNodes> m_ifGuardedNodes;
2958 TemplateNodeList m_falseNodes;
2961 //----------------------------------------------------------
2962 /** @brief Class representing a 'for' tag in a template */
2963 class TemplateNodeRepeat : public TemplateNodeCreator<TemplateNodeRepeat>
2966 TemplateNodeRepeat(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2967 : TemplateNodeCreator<TemplateNodeRepeat>(parser,parent,line)
2969 TRACE(("{TemplateNodeRepeat(%s)\n",data.data()));
2970 ExpressionParser expParser(parser,line);
2971 m_expr = expParser.parse(data);
2973 stopAt.append("endrepeat");
2974 parser->parse(this,line,stopAt,m_repeatNodes);
2975 parser->removeNextToken(); // skip over endrepeat
2976 TRACE(("}TemplateNodeRepeat(%s)\n",data.data()));
2978 ~TemplateNodeRepeat()
2982 void render(FTextStream &ts, TemplateContext *c)
2984 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2985 if (ci==0) return; // should not happen
2986 ci->setLocation(m_templateName,m_line);
2988 if (m_expr && (v=m_expr->resolve(c)).type()==TemplateVariant::Integer)
2990 int i, n = v.toInt();
2993 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2994 s->set("counter0", (int)i);
2995 s->set("counter", (int)(i+1));
2996 s->set("revcounter", (int)(n-i));
2997 s->set("revcounter0", (int)(n-i-1));
2998 s->set("first",i==0);
2999 s->set("last", i==n-1);
3000 c->set("repeatloop",s.get());
3001 // render all items for this iteration of the loop
3002 m_repeatNodes.render(ts,c);
3005 else // simple type...
3007 ci->warn(m_templateName,m_line,"for requires a variable of list type!");
3011 TemplateNodeList m_repeatNodes;
3015 //----------------------------------------------------------
3017 /** @brief Class representing a 'range' tag in a template */
3018 class TemplateNodeRange : public TemplateNodeCreator<TemplateNodeRange>
3021 TemplateNodeRange(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3022 : TemplateNodeCreator<TemplateNodeRange>(parser,parent,line), m_down(FALSE)
3024 TRACE(("{TemplateNodeRange(%s)\n",data.data()));
3026 int i1 = data.find(" from ");
3027 int i2 = data.find(" to ");
3028 int i3 = data.find(" downto ");
3031 if (data.right(5)==" from")
3033 parser->warn(m_templateName,line,"range missing after 'from' keyword");
3035 else if (data=="from")
3037 parser->warn(m_templateName,line,"range needs an iterator variable and a range");
3041 parser->warn(m_templateName,line,"range is missing 'from' keyword");
3044 else if (i2==-1 && i3==-1)
3046 if (data.right(3)==" to")
3048 parser->warn(m_templateName,line,"range is missing end value after 'to' keyword");
3050 else if (data.right(7)==" downto")
3052 parser->warn(m_templateName,line,"range is missing end value after 'downto' keyword");
3056 parser->warn(m_templateName,line,"range is missing 'to' or 'downto' keyword");
3061 m_var = data.left(i1).stripWhiteSpace();
3062 if (m_var.isEmpty())
3064 parser->warn(m_templateName,line,"range needs an iterator variable");
3066 start = data.mid(i1+6,i2-i1-6).stripWhiteSpace();
3069 end = data.right(data.length()-i2-4).stripWhiteSpace();
3074 end = data.right(data.length()-i3-8).stripWhiteSpace();
3078 ExpressionParser expParser(parser,line);
3079 m_startExpr = expParser.parse(start);
3080 m_endExpr = expParser.parse(end);
3083 stopAt.append("endrange");
3084 parser->parse(this,line,stopAt,m_loopNodes);
3085 parser->removeNextToken(); // skip over endrange
3086 TRACE(("}TemplateNodeRange(%s)\n",data.data()));
3089 ~TemplateNodeRange()
3095 void render(FTextStream &ts, TemplateContext *c)
3097 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3098 if (ci==0) return; // should not happen
3099 ci->setLocation(m_templateName,m_line);
3100 //printf("TemplateNodeRange::render #loopNodes=%d\n",
3101 // m_loopNodes.count());
3102 if (m_startExpr && m_endExpr)
3104 TemplateVariant vs = m_startExpr->resolve(c);
3105 TemplateVariant ve = m_endExpr->resolve(c);
3106 if (vs.type()==TemplateVariant::Integer && ve.type()==TemplateVariant::Integer)
3110 int l = m_down ? s-e+1 : e-s+1;
3114 //int index = m_reversed ? list.count() : 0;
3115 const TemplateVariant *parentLoop = c->getRef("forloop");
3117 int i = m_down ? e : s;
3121 // set the forloop meta-data variable
3122 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
3123 s->set("counter0", (int)index);
3124 s->set("counter", (int)(index+1));
3125 s->set("revcounter", (int)(l-index));
3126 s->set("revcounter0", (int)(l-index-1));
3127 s->set("first",index==0);
3128 s->set("last", (int)index==l-1);
3129 s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
3130 c->set("forloop",s.get());
3132 // set the iterator variable
3135 // render all items for this iteration of the loop
3136 m_loopNodes.render(ts,c);
3154 ci->warn(m_templateName,m_line,"range %d %s %d is empty!",
3155 s,m_down?"downto":"to",e);
3158 else if (vs.type()!=TemplateVariant::Integer)
3160 ci->warn(m_templateName,m_line,"range requires a start value of integer type!");
3162 else if (ve.type()!=TemplateVariant::Integer)
3164 ci->warn(m_templateName,m_line,"range requires an end value of integer type!");
3167 else if (!m_startExpr)
3169 ci->warn(m_templateName,m_line,"range has empty start value");
3171 else if (!m_endExpr)
3173 ci->warn(m_templateName,m_line,"range has empty end value");
3179 ExprAst *m_startExpr;
3182 TemplateNodeList m_loopNodes;
3185 //----------------------------------------------------------
3187 /** @brief Class representing a 'for' tag in a template */
3188 class TemplateNodeFor : public TemplateNodeCreator<TemplateNodeFor>
3191 TemplateNodeFor(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3192 : TemplateNodeCreator<TemplateNodeFor>(parser,parent,line), m_reversed(FALSE)
3194 TRACE(("{TemplateNodeFor(%s)\n",data.data()));
3196 int i = data.find(" in ");
3199 if (data.right(3)==" in")
3201 parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
3203 else if (data=="in")
3205 parser->warn(m_templateName,line,"for needs at least one iterator variable");
3209 parser->warn(m_templateName,line,"for is missing 'in' keyword");
3214 m_vars = split(data.left(i),",");
3215 if (m_vars.count()==0)
3217 parser->warn(m_templateName,line,"for needs at least one iterator variable");
3220 int j = data.find(" reversed",i);
3221 m_reversed = (j!=-1);
3223 if (j==-1) j=data.length();
3226 exprStr = data.mid(i+4,j-i-4); // skip over " in " part
3228 if (exprStr.isEmpty())
3230 parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
3233 ExpressionParser expParser(parser,line);
3234 m_expr = expParser.parse(exprStr);
3237 stopAt.append("endfor");
3238 stopAt.append("empty");
3239 parser->parse(this,line,stopAt,m_loopNodes);
3240 TemplateToken *tok = parser->takeNextToken();
3241 if (tok && tok->data=="empty")
3243 stopAt.removeLast();
3244 parser->parse(this,line,stopAt,m_emptyNodes);
3245 parser->removeNextToken(); // skip over endfor
3248 TRACE(("}TemplateNodeFor(%s)\n",data.data()));
3256 void render(FTextStream &ts, TemplateContext *c)
3258 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3259 if (ci==0) return; // should not happen
3260 ci->setLocation(m_templateName,m_line);
3261 //printf("TemplateNodeFor::render #loopNodes=%d #emptyNodes=%d\n",
3262 // m_loopNodes.count(),m_emptyNodes.count());
3265 TemplateVariant v = m_expr->resolve(c);
3266 if (v.type()==TemplateVariant::Function)
3268 v = v.call(QValueList<TemplateVariant>());
3270 const TemplateListIntf *list = v.toList();
3273 uint listSize = list->count();
3274 if (listSize==0) // empty for loop
3276 m_emptyNodes.render(ts,c);
3280 //int index = m_reversed ? list.count() : 0;
3282 const TemplateVariant *parentLoop = c->getRef("forloop");
3283 uint index = m_reversed ? listSize-1 : 0;
3284 TemplateListIntf::ConstIterator *it = list->createIterator();
3285 for (m_reversed ? it->toLast() : it->toFirst();
3287 m_reversed ? it->toPrev() : it->toNext())
3289 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
3290 s->set("counter0", (int)index);
3291 s->set("counter", (int)(index+1));
3292 s->set("revcounter", (int)(listSize-index));
3293 s->set("revcounter0", (int)(listSize-index-1));
3294 s->set("first",index==0);
3295 s->set("last", index==listSize-1);
3296 s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
3297 c->set("forloop",s.get());
3299 // add variables for this loop to the context
3300 //obj->addVariableToContext(index,m_vars,c);
3302 if (m_vars.count()==1) // loop variable represents an item
3304 c->set(m_vars[vi++],v);
3306 else if (m_vars.count()>1 && v.type()==TemplateVariant::Struct)
3307 // loop variables represent elements in a list item
3309 for (uint i=0;i<m_vars.count();i++,vi++)
3311 c->set(m_vars[vi],v.toStruct()->get(m_vars[vi]));
3314 for (;vi<m_vars.count();vi++)
3316 c->set(m_vars[vi],TemplateVariant());
3319 // render all items for this iteration of the loop
3320 m_loopNodes.render(ts,c);
3322 if (m_reversed) index--; else index++;
3327 else // simple type...
3329 ci->warn(m_templateName,m_line,"for requires a variable of list type, got type '%s'!",v.typeAsString().data());
3337 QValueList<QCString> m_vars;
3338 TemplateNodeList m_loopNodes;
3339 TemplateNodeList m_emptyNodes;
3342 //----------------------------------------------------------
3344 /** @brief Class representing an 'markers' tag in a template */
3345 class TemplateNodeMsg : public TemplateNodeCreator<TemplateNodeMsg>
3348 TemplateNodeMsg(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
3349 : TemplateNodeCreator<TemplateNodeMsg>(parser,parent,line)
3351 TRACE(("{TemplateNodeMsg()\n"));
3353 stopAt.append("endmsg");
3354 parser->parse(this,line,stopAt,m_nodes);
3355 parser->removeNextToken(); // skip over endmsg
3356 TRACE(("}TemplateNodeMsg()\n"));
3358 void render(FTextStream &, TemplateContext *c)
3360 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3361 if (ci==0) return; // should not happen
3362 ci->setLocation(m_templateName,m_line);
3363 TemplateEscapeIntf *escIntf = ci->escapeIntf();
3364 ci->setActiveEscapeIntf(0); // avoid escaping things we send to standard out
3365 bool enable = ci->spacelessEnabled();
3366 ci->enableSpaceless(FALSE);
3367 FTextStream ts(stdout);
3368 m_nodes.render(ts,c);
3370 ci->setActiveEscapeIntf(escIntf);
3371 ci->enableSpaceless(enable);
3374 TemplateNodeList m_nodes;
3378 //----------------------------------------------------------
3380 /** @brief Class representing a 'block' tag in a template */
3381 class TemplateNodeBlock : public TemplateNodeCreator<TemplateNodeBlock>
3384 TemplateNodeBlock(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3385 : TemplateNodeCreator<TemplateNodeBlock>(parser,parent,line)
3387 TRACE(("{TemplateNodeBlock(%s)\n",data.data()));
3389 if (m_blockName.isEmpty())
3391 parser->warn(parser->templateName(),line,"block tag without name");
3394 stopAt.append("endblock");
3395 parser->parse(this,line,stopAt,m_nodes);
3396 parser->removeNextToken(); // skip over endblock
3397 TRACE(("}TemplateNodeBlock(%s)\n",data.data()));
3400 void render(FTextStream &ts, TemplateContext *c)
3402 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3403 if (ci==0) return; // should not happen
3404 ci->setLocation(m_templateName,m_line);
3405 TemplateImpl *t = getTemplate();
3408 // remove block from the context, so block.super can work
3409 TemplateNodeBlock *nb = ci->blockContext()->pop(m_blockName);
3410 if (nb) // block is overruled
3414 FTextStream ss(&super);
3415 // get super block of block nb
3416 TemplateNodeBlock *sb = ci->blockContext()->get(m_blockName);
3417 if (sb && sb!=nb && sb!=this) // nb and sb both overrule this block
3419 sb->render(ss,c); // render parent of nb to string
3421 else if (nb!=this) // only nb overrules this block
3423 m_nodes.render(ss,c); // render parent of nb to string
3425 // add 'block.super' variable to allow access to parent block content
3426 TemplateAutoRef<TemplateStruct> superBlock(TemplateStruct::alloc());
3427 superBlock->set("super",TemplateVariant(super.data(),TRUE));
3428 ci->set("block",superBlock.get());
3429 // render the overruled block contents
3430 t->engine()->enterBlock(nb->m_templateName,nb->m_blockName,nb->m_line);
3431 nb->m_nodes.render(ts,c);
3432 t->engine()->leaveBlock();
3434 // re-add block to the context
3435 ci->blockContext()->push(nb);
3437 else // block has no overrule
3439 t->engine()->enterBlock(m_templateName,m_blockName,m_line);
3440 m_nodes.render(ts,c);
3441 t->engine()->leaveBlock();
3446 QCString name() const
3452 QCString m_blockName;
3453 TemplateNodeList m_nodes;
3456 //----------------------------------------------------------
3458 /** @brief Class representing a 'extend' tag in a template */
3459 class TemplateNodeExtend : public TemplateNodeCreator<TemplateNodeExtend>
3462 TemplateNodeExtend(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3463 : TemplateNodeCreator<TemplateNodeExtend>(parser,parent,line)
3465 TRACE(("{TemplateNodeExtend(%s)\n",data.data()));
3466 ExpressionParser ep(parser,line);
3469 parser->warn(m_templateName,line,"extend tag is missing template file argument");
3471 m_extendExpr = ep.parse(data);
3473 parser->parse(this,line,stopAt,m_nodes);
3474 TRACE(("}TemplateNodeExtend(%s)\n",data.data()));
3476 ~TemplateNodeExtend()
3478 delete m_extendExpr;
3481 void render(FTextStream &ts, TemplateContext *c)
3483 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3484 if (ci==0) return; // should not happen
3485 ci->setLocation(m_templateName,m_line);
3486 if (m_extendExpr==0) return;
3488 QCString extendFile = m_extendExpr->resolve(c).toString();
3489 if (extendFile.isEmpty())
3491 ci->warn(m_templateName,m_line,"invalid parameter for extend command");
3494 // goto root of tree (template node)
3495 TemplateImpl *t = getTemplate();
3498 Template *bt = t->engine()->loadByName(extendFile,m_line);
3499 TemplateImpl *baseTemplate = bt ? dynamic_cast<TemplateImpl*>(bt) : 0;
3502 // fill block context
3503 TemplateBlockContext *bc = ci->blockContext();
3505 // add overruling blocks to the context
3506 QListIterator<TemplateNode> li(m_nodes);
3508 for (li.toFirst();(n=li.current());++li)
3510 TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
3515 TemplateNodeMsg *msg = dynamic_cast<TemplateNodeMsg*>(n);
3522 // render the base template with the given context
3523 baseTemplate->render(ts,c);
3527 t->engine()->unload(t);
3531 ci->warn(m_templateName,m_line,"failed to load template %s for extend",extendFile.data());
3537 ExprAst *m_extendExpr;
3538 TemplateNodeList m_nodes;
3541 /** @brief Class representing an 'include' tag in a template */
3542 class TemplateNodeInclude : public TemplateNodeCreator<TemplateNodeInclude>
3545 TemplateNodeInclude(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3546 : TemplateNodeCreator<TemplateNodeInclude>(parser,parent,line)
3548 TRACE(("TemplateNodeInclude(%s)\n",data.data()));
3549 ExpressionParser ep(parser,line);
3552 parser->warn(m_templateName,line,"include tag is missing template file argument");
3554 m_includeExpr = ep.parse(data);
3556 ~TemplateNodeInclude()
3558 delete m_includeExpr;
3560 void render(FTextStream &ts, TemplateContext *c)
3562 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3563 if (ci==0) return; // should not happen
3564 ci->setLocation(m_templateName,m_line);
3567 QCString includeFile = m_includeExpr->resolve(c).toString();
3568 if (includeFile.isEmpty())
3570 ci->warn(m_templateName,m_line,"invalid parameter for include command\n");
3574 TemplateImpl *t = getTemplate();
3577 Template *it = t->engine()->loadByName(includeFile,m_line);
3578 TemplateImpl *incTemplate = it ? dynamic_cast<TemplateImpl*>(it) : 0;
3581 incTemplate->render(ts,c);
3582 t->engine()->unload(t);
3586 ci->warn(m_templateName,m_line,"failed to load template '%s' for include",includeFile.data()?includeFile.data():"");
3594 ExprAst *m_includeExpr;
3597 //----------------------------------------------------------
3599 static void stripLeadingWhiteSpace(QGString &s)
3601 const char *src = s.data();
3604 char *dst = s.data();
3606 bool skipSpaces=TRUE;
3609 if (c=='\n') { *dst++=c; skipSpaces=TRUE; }
3610 else if (c==' ' && skipSpaces) {}
3611 else { *dst++=c; skipSpaces=FALSE; }
3617 /** @brief Class representing an 'create' tag in a template */
3618 class TemplateNodeCreate : public TemplateNodeCreator<TemplateNodeCreate>
3621 TemplateNodeCreate(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3622 : TemplateNodeCreator<TemplateNodeCreate>(parser,parent,line), m_templateExpr(0), m_fileExpr(0)
3624 TRACE(("TemplateNodeCreate(%s)\n",data.data()));
3625 ExpressionParser ep(parser,line);
3628 parser->warn(m_templateName,line,"create tag is missing arguments");
3630 int i = data.find(" from ");
3633 if (data.right(3)==" from")
3635 parser->warn(m_templateName,line,"create is missing template name after 'from' keyword");
3637 else if (data=="from")
3639 parser->warn(m_templateName,line,"create needs a file name and a template name");
3643 parser->warn(m_templateName,line,"create is missing 'from' keyword");
3648 ExpressionParser ep(parser,line);
3649 m_fileExpr = ep.parse(data.left(i).stripWhiteSpace());
3650 m_templateExpr = ep.parse(data.mid(i+6).stripWhiteSpace());
3653 ~TemplateNodeCreate()
3655 delete m_templateExpr;
3658 void render(FTextStream &, TemplateContext *c)
3660 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3661 if (ci==0) return; // should not happen
3662 ci->setLocation(m_templateName,m_line);
3663 if (m_templateExpr && m_fileExpr)
3665 QCString templateFile = m_templateExpr->resolve(c).toString();
3666 QCString outputFile = m_fileExpr->resolve(c).toString();
3667 if (templateFile.isEmpty())
3669 ci->warn(m_templateName,m_line,"empty template name parameter for create command\n");
3671 else if (outputFile.isEmpty())
3673 ci->warn(m_templateName,m_line,"empty file name parameter for create command\n");
3677 TemplateImpl *t = getTemplate();
3680 QCString extension=outputFile;
3681 int i=extension.findRev('.');
3684 extension=extension.right(extension.length()-i-1);
3686 t->engine()->setOutputExtension(extension);
3687 Template *ct = t->engine()->loadByName(templateFile,m_line);
3688 TemplateImpl *createTemplate = ct ? dynamic_cast<TemplateImpl*>(ct) : 0;
3691 mkpath(ci,outputFile);
3692 if (!ci->outputDirectory().isEmpty())
3694 outputFile.prepend(ci->outputDirectory()+"/");
3696 //printf("NoteCreate(%s)\n",outputFile.data());
3697 QFile f(outputFile);
3698 if (f.open(IO_WriteOnly))
3700 TemplateEscapeIntf *escIntf = ci->escapeIntf();
3701 ci->selectEscapeIntf(extension);
3704 FTextStream os(&out);
3705 createTemplate->render(os,c);
3706 stripLeadingWhiteSpace(out);
3708 t->engine()->unload(t);
3709 ci->setActiveEscapeIntf(escIntf);
3713 ci->warn(m_templateName,m_line,"failed to open output file '%s' for create command",outputFile.data());
3718 ci->warn(m_templateName,m_line,"failed to load template '%s' for include",templateFile.data());
3720 t->engine()->setOutputExtension("");
3727 ExprAst *m_templateExpr;
3728 ExprAst *m_fileExpr;
3731 //----------------------------------------------------------
3733 /** @brief Class representing an 'tree' tag in a template */
3734 class TemplateNodeTree : public TemplateNodeCreator<TemplateNodeTree>
3738 TreeContext(TemplateNodeTree *o,const TemplateListIntf *l,TemplateContext *c)
3739 : object(o), list(l), templateCtx(c) {}
3740 TemplateNodeTree *object;
3741 const TemplateListIntf *list;
3742 TemplateContext *templateCtx;
3745 TemplateNodeTree(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3746 : TemplateNodeCreator<TemplateNodeTree>(parser,parent,line)
3748 TRACE(("{TemplateNodeTree(%s)\n",data.data()));
3749 ExpressionParser ep(parser,line);
3752 parser->warn(m_templateName,line,"recursetree tag is missing data argument");
3754 m_treeExpr = ep.parse(data);
3756 stopAt.append("endrecursetree");
3757 parser->parse(this,line,stopAt,m_treeNodes);
3758 parser->removeNextToken(); // skip over endrecursetree
3759 TRACE(("}TemplateNodeTree(%s)\n",data.data()));
3765 static TemplateVariant renderChildrenStub(const void *ctx, const QValueList<TemplateVariant> &)
3767 return TemplateVariant(((TreeContext*)ctx)->object->
3768 renderChildren((const TreeContext*)ctx),TRUE);
3770 QCString renderChildren(const TreeContext *ctx)
3772 //printf("TemplateNodeTree::renderChildren(%d)\n",ctx->list->count());
3773 // render all children of node to a string and return it
3774 TemplateContext *c = ctx->templateCtx;
3775 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3776 if (ci==0) return QCString(); // should not happen
3778 FTextStream ss(&result);
3780 TemplateVariant node;
3781 TemplateListIntf::ConstIterator *it = ctx->list->createIterator();
3782 for (it->toFirst();(it->current(node));it->toNext())
3784 c->set("node",node);
3785 bool hasChildren=FALSE;
3786 const TemplateStructIntf *ns = node.toStruct();
3787 if (ns) // node is a struct
3789 TemplateVariant v = ns->get("children");
3790 if (v.isValid()) // with a field 'children'
3792 const TemplateListIntf *list = v.toList();
3793 if (list && list->count()>0) // non-empty list
3795 TreeContext childCtx(this,list,ctx->templateCtx);
3796 TemplateVariant children(TemplateVariant::Delegate::fromFunction(&childCtx,renderChildrenStub));
3797 children.setRaw(TRUE);
3798 c->set("children",children);
3799 m_treeNodes.render(ss,c);
3804 ci->warn(m_templateName,m_line,"recursetree: children attribute has type '%s' instead of list\n",v.typeAsString().data());
3809 // ci->warn(m_templateName,m_line,"recursetree: children attribute is not valid");
3814 c->set("children",TemplateVariant("")); // provide default
3815 m_treeNodes.render(ss,c);
3820 return result.data();
3822 void render(FTextStream &ts, TemplateContext *c)
3824 //printf("TemplateNodeTree::render()\n");
3825 TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3826 if (ci==0) return; // should not happen
3827 ci->setLocation(m_templateName,m_line);
3828 TemplateVariant v = m_treeExpr->resolve(c);
3829 const TemplateListIntf *list = v.toList();
3832 TreeContext ctx(this,list,c);
3833 ts << renderChildren(&ctx);
3837 ci->warn(m_templateName,m_line,"recursetree's argument should be a list type");
3842 ExprAst *m_treeExpr;
3843 TemplateNodeList m_treeNodes;
3846 //----------------------------------------------------------
3848 /** @brief Class representing an 'indexentry' tag in a template */
3849 class TemplateNodeIndexEntry : public TemplateNodeCreator<TemplateNodeIndexEntry>
3853 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3854 ~Mapping() { delete value; }
3859 TemplateNodeIndexEntry(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3860 : TemplateNodeCreator<TemplateNodeIndexEntry>(parser,parent,line)
3862 TRACE(("{TemplateNodeIndexEntry(%s)\n",data.data()));
3863 m_args.setAutoDelete(TRUE);
3864 ExpressionParser expParser(parser,line);
3865 QValueList<QCString> args = split(data," ");
3866 QValueListIterator<QCString> it = args.begin();
3867 if (it==args.end() || (*it).find('=')!=-1)
3869 parser->warn(parser->templateName(),line,"Missing name for indexentry tag");
3875 while (it!=args.end())
3878 int j=arg.find('=');
3881 ExprAst *expr = expParser.parse(arg.mid(j+1));
3884 m_args.append(new Mapping(arg.left(j),expr));
3889 parser->warn(parser->templateName(),line,"invalid argument '%s' for indexentry tag",arg.data());
3894 TRACE(("}TemplateNodeIndexEntry(%s)\n",data.data()));
3896 void render(FTextStream &, TemplateContext *c)
3898 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3899 if (ci==0) return; // should not happen
3900 if (!m_name.isEmpty())
3902 ci->setLocation(m_templateName,m_line);
3903 QListIterator<Mapping> it(m_args);
3905 QValueList<TemplateKeyValue> list;
3906 for (it.toFirst();(mapping=it.current());++it)
3908 list.append(TemplateKeyValue(mapping->name,mapping->value->resolve(c)));
3910 ci->addIndexEntry(m_name,list);
3915 QList<Mapping> m_args;
3918 //----------------------------------------------------------
3920 /** @brief Class representing an 'opensubindex' tag in a template */
3921 class TemplateNodeOpenSubIndex : public TemplateNodeCreator<TemplateNodeOpenSubIndex>
3924 TemplateNodeOpenSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3925 : TemplateNodeCreator<TemplateNodeOpenSubIndex>(parser,parent,line)
3927 TRACE(("{TemplateNodeOpenSubIndex(%s)\n",data.data()));
3928 m_name = data.stripWhiteSpace();
3929 if (m_name.isEmpty())
3931 parser->warn(parser->templateName(),line,"Missing argument for opensubindex tag");
3933 else if (m_name.find(' ')!=-1)
3935 parser->warn(parser->templateName(),line,"Expected single argument for opensubindex tag got '%s'",data.data());
3938 TRACE(("}TemplateNodeOpenSubIndex(%s)\n",data.data()));
3940 void render(FTextStream &, TemplateContext *c)
3942 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3943 if (ci==0) return; // should not happen
3944 if (!m_name.isEmpty())
3946 ci->setLocation(m_templateName,m_line);
3947 ci->openSubIndex(m_name);
3954 //----------------------------------------------------------
3956 /** @brief Class representing an 'closesubindex' tag in a template */
3957 class TemplateNodeCloseSubIndex : public TemplateNodeCreator<TemplateNodeCloseSubIndex>
3960 TemplateNodeCloseSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3961 : TemplateNodeCreator<TemplateNodeCloseSubIndex>(parser,parent,line)
3963 TRACE(("{TemplateNodeCloseSubIndex(%s)\n",data.data()));
3964 m_name = data.stripWhiteSpace();
3965 if (m_name.isEmpty())
3967 parser->warn(parser->templateName(),line,"Missing argument for closesubindex tag");
3969 else if (m_name.find(' ')!=-1 || m_name.isEmpty())
3971 parser->warn(parser->templateName(),line,"Expected single argument for closesubindex tag got '%s'",data.data());
3974 TRACE(("}TemplateNodeCloseSubIndex(%s)\n",data.data()));
3976 void render(FTextStream &, TemplateContext *c)
3978 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3979 if (ci==0) return; // should not happen
3980 if (!m_name.isEmpty())
3982 ci->setLocation(m_templateName,m_line);
3983 ci->closeSubIndex(m_name);
3991 //----------------------------------------------------------
3993 /** @brief Class representing an 'with' tag in a template */
3994 class TemplateNodeWith : public TemplateNodeCreator<TemplateNodeWith>
3998 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3999 ~Mapping() { delete value; }
4004 TemplateNodeWith(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4005 : TemplateNodeCreator<TemplateNodeWith>(parser,parent,line)
4007 TRACE(("{TemplateNodeWith(%s)\n",data.data()));
4008 m_args.setAutoDelete(TRUE);
4009 ExpressionParser expParser(parser,line);
4010 QCString filteredData = removeSpacesAroundEquals(data);
4011 QValueList<QCString> args = split(filteredData," ");
4012 QValueListIterator<QCString> it = args.begin();
4013 while (it!=args.end())
4016 int j=arg.find('=');
4019 ExprAst *expr = expParser.parse(arg.mid(j+1));
4022 m_args.append(new Mapping(arg.left(j),expr));
4027 parser->warn(parser->templateName(),line,"invalid argument '%s' for 'with' tag",arg.data());
4032 stopAt.append("endwith");
4033 parser->parse(this,line,stopAt,m_nodes);
4034 parser->removeNextToken(); // skip over endwith
4035 TRACE(("}TemplateNodeWith(%s)\n",data.data()));
4040 void render(FTextStream &ts, TemplateContext *c)
4042 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4043 if (ci==0) return; // should not happen
4044 ci->setLocation(m_templateName,m_line);
4046 QListIterator<Mapping> it(m_args);
4048 for (it.toFirst();(mapping=it.current());++it)
4050 TemplateVariant value = mapping->value->resolve(c);
4051 ci->set(mapping->name,value);
4053 m_nodes.render(ts,c);
4057 TemplateNodeList m_nodes;
4058 QList<Mapping> m_args;
4061 //----------------------------------------------------------
4063 /** @brief Class representing an 'cycle' tag in a template */
4064 class TemplateNodeCycle : public TemplateNodeCreator<TemplateNodeCycle>
4067 TemplateNodeCycle(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4068 : TemplateNodeCreator<TemplateNodeCycle>(parser,parent,line)
4070 TRACE(("{TemplateNodeCycle(%s)\n",data.data()));
4071 m_args.setAutoDelete(TRUE);
4073 ExpressionParser expParser(parser,line);
4074 QValueList<QCString> args = split(data," ");
4075 QValueListIterator<QCString> it = args.begin();
4076 while (it!=args.end())
4078 ExprAst *expr = expParser.parse(*it);
4081 m_args.append(expr);
4085 if (m_args.count()<2)
4087 parser->warn(parser->templateName(),line,"expected at least two arguments for cycle command, got %d",m_args.count());
4089 TRACE(("}TemplateNodeCycle(%s)\n",data.data()));
4091 void render(FTextStream &ts, TemplateContext *c)
4093 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4094 ci->setLocation(m_templateName,m_line);
4095 if (m_index<m_args.count())
4097 TemplateVariant v = m_args.at(m_index)->resolve(c);
4098 if (v.type()==TemplateVariant::Function)
4100 v = v.call(QValueList<TemplateVariant>());
4102 if (ci->escapeIntf() && !v.raw())
4104 if (ci->needsRecoding())
4106 ts << ci->recode(ci->escapeIntf()->escape(v.toString()));
4110 ts << ci->escapeIntf()->escape(v.toString());
4115 if (ci->needsRecoding())
4117 ts << ci->recode(v.toString());
4125 if (++m_index==m_args.count()) // wrap around
4132 QList<ExprAst> m_args;
4135 //----------------------------------------------------------
4137 /** @brief Class representing an 'set' tag in a template */
4138 class TemplateNodeSet : public TemplateNodeCreator<TemplateNodeSet>
4142 Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
4143 ~Mapping() { delete value; }
4148 TemplateNodeSet(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4149 : TemplateNodeCreator<TemplateNodeSet>(parser,parent,line), m_mapping(0)
4151 TRACE(("{TemplateNodeSet(%s)\n",data.data()));
4152 ExpressionParser expParser(parser,line);
4153 // data format: name=expression
4154 int j=data.find('=');
4156 if (j>0 && (expr = expParser.parse(data.mid(j+1))))
4158 m_mapping = new Mapping(data.left(j),expr);
4160 TRACE(("}TemplateNodeSet(%s)\n",data.data()));
4166 void render(FTextStream &, TemplateContext *c)
4168 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4169 if (ci==0) return; // should not happen
4170 ci->setLocation(m_templateName,m_line);
4173 TemplateVariant value = m_mapping->value->resolve(c);
4174 ci->set(m_mapping->name,value);
4181 //----------------------------------------------------------
4183 /** @brief Class representing an 'spaceless' tag in a template */
4184 class TemplateNodeSpaceless : public TemplateNodeCreator<TemplateNodeSpaceless>
4187 TemplateNodeSpaceless(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
4188 : TemplateNodeCreator<TemplateNodeSpaceless>(parser,parent,line)
4190 TRACE(("{TemplateNodeSpaceless()\n"));
4192 stopAt.append("endspaceless");
4193 parser->parse(this,line,stopAt,m_nodes);
4194 parser->removeNextToken(); // skip over endwith
4195 TRACE(("}TemplateNodeSpaceless()\n"));
4197 void render(FTextStream &ts, TemplateContext *c)
4199 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4200 if (ci==0) return; // should not happen
4201 ci->setLocation(m_templateName,m_line);
4202 bool wasSpaceless = ci->spacelessEnabled();
4203 ci->enableSpaceless(TRUE);
4204 m_nodes.render(ts,c);
4205 ci->enableSpaceless(wasSpaceless);
4208 TemplateNodeList m_nodes;
4211 //----------------------------------------------------------
4213 /** @brief Class representing an 'markers' tag in a template */
4214 class TemplateNodeMarkers : public TemplateNodeCreator<TemplateNodeMarkers>
4217 TemplateNodeMarkers(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4218 : TemplateNodeCreator<TemplateNodeMarkers>(parser,parent,line), m_listExpr(0), m_patternExpr(0)
4220 TRACE(("{TemplateNodeMarkers(%s)\n",data.data()));
4221 int i = data.find(" in ");
4222 int w = data.find(" with ");
4223 if (i==-1 || w==-1 || w<i)
4225 parser->warn(m_templateName,line,"markers tag as wrong format. Expected: markers <var> in <list> with <string_with_markers>");
4229 ExpressionParser expParser(parser,line);
4230 m_var = data.left(i);
4231 m_listExpr = expParser.parse(data.mid(i+4,w-i-4));
4232 m_patternExpr = expParser.parse(data.right(data.length()-w-6));
4235 stopAt.append("endmarkers");
4236 parser->parse(this,line,stopAt,m_nodes);
4237 parser->removeNextToken(); // skip over endmarkers
4238 TRACE(("}TemplateNodeMarkers(%s)\n",data.data()));
4240 ~TemplateNodeMarkers()
4243 delete m_patternExpr;
4245 void render(FTextStream &ts, TemplateContext *c)
4247 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4248 if (ci==0) return; // should not happen
4249 ci->setLocation(m_templateName,m_line);
4250 if (!m_var.isEmpty() && m_listExpr && m_patternExpr)
4252 TemplateVariant v = m_listExpr->resolve(c);
4253 const TemplateListIntf *list = v.toList();
4254 TemplateVariant patternStr = m_patternExpr->resolve(c);
4257 if (patternStr.type()==TemplateVariant::String)
4259 TemplateListIntf::ConstIterator *it = list->createIterator();
4261 QCString str = patternStr.toString();
4262 QRegExp marker("@[0-9]+"); // pattern for a marker, i.e. @0, @1 ... @12, etc
4263 int index=0,newIndex,matchLen;
4264 while ((newIndex=marker.match(str,index,&matchLen))!=-1)
4266 if (ci->needsRecoding())
4268 ts << ci->recode(str.mid(index,newIndex-index)); // write text before marker
4272 ts << str.mid(index,newIndex-index); // write text before marker
4275 uint entryIndex = str.mid(newIndex+1,matchLen-1).toUInt(&ok); // get marker id
4276 TemplateVariant var;
4278 // search for list element at position id
4279 for (it->toFirst(); (it->current(var)) && i<entryIndex; it->toNext(),i++) {}
4280 if (ok && i==entryIndex) // found element
4282 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
4283 s->set("id",(int)i);
4284 c->set("markers",s.get());
4285 c->set(m_var,var); // define local variable to hold element of list type
4286 bool wasSpaceless = ci->spacelessEnabled();
4287 ci->enableSpaceless(TRUE);
4288 m_nodes.render(ts,c);
4289 ci->enableSpaceless(wasSpaceless);
4293 ci->warn(m_templateName,m_line,"markers pattern string has invalid markers '%s'",str.data());
4295 else if (i<entryIndex)
4297 ci->warn(m_templateName,m_line,"markers list does not an element for marker position %d",i);
4299 index=newIndex+matchLen; // set index just after marker
4301 if (ci->needsRecoding())
4303 ts << ci->recode(str.right(str.length()-index)); // write text after last marker
4307 ts << str.right(str.length()-index); // write text after last marker
4314 ci->warn(m_templateName,m_line,"markers requires a parameter of string type after 'with'!");
4319 ci->warn(m_templateName,m_line,"markers requires a parameter of list type after 'in'!");
4324 TemplateNodeList m_nodes;
4326 ExprAst *m_listExpr;
4327 ExprAst *m_patternExpr;
4330 //----------------------------------------------------------
4332 /** @brief Class representing an 'tabbing' tag in a template */
4333 class TemplateNodeTabbing : public TemplateNodeCreator<TemplateNodeTabbing>
4336 TemplateNodeTabbing(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
4337 : TemplateNodeCreator<TemplateNodeTabbing>(parser,parent,line)
4339 TRACE(("{TemplateNodeTabbing()\n"));
4341 stopAt.append("endtabbing");
4342 parser->parse(this,line,stopAt,m_nodes);
4343 parser->removeNextToken(); // skip over endtabbing
4344 TRACE(("}TemplateNodeTabbing()\n"));
4346 void render(FTextStream &ts, TemplateContext *c)
4348 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4349 if (ci==0) return; // should not happen
4350 ci->setLocation(m_templateName,m_line);
4351 bool wasTabbing = ci->tabbingEnabled();
4352 ci->enableTabbing(TRUE);
4353 m_nodes.render(ts,c);
4354 ci->enableTabbing(wasTabbing);
4357 TemplateNodeList m_nodes;
4360 //----------------------------------------------------------
4362 /** @brief Class representing an 'markers' tag in a template */
4363 class TemplateNodeResource : public TemplateNodeCreator<TemplateNodeResource>
4366 TemplateNodeResource(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4367 : TemplateNodeCreator<TemplateNodeResource>(parser,parent,line)
4369 TRACE(("{TemplateNodeResource(%s)\n",data.data()));
4370 ExpressionParser ep(parser,line);
4374 parser->warn(m_templateName,line,"resource tag is missing resource file argument");
4378 else if ((i=data.find(" as "))!=-1) // resource a as b
4380 m_resExpr = ep.parse(data.left(i)); // part before as
4381 m_asExpr = ep.parse(data.mid(i+4)); // part after as
4385 m_resExpr = ep.parse(data);
4388 TRACE(("}TemplateNodeResource(%s)\n",data.data()));
4390 ~TemplateNodeResource()
4395 void render(FTextStream &, TemplateContext *c)
4397 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4398 if (ci==0) return; // should not happen
4399 ci->setLocation(m_templateName,m_line);
4402 QCString resourceFile = m_resExpr->resolve(c).toString();
4403 if (resourceFile.isEmpty())
4405 ci->warn(m_templateName,m_line,"invalid parameter for resource command\n");
4409 QCString outputDirectory = ci->outputDirectory();
4412 QCString targetFile = m_asExpr->resolve(c).toString();
4413 mkpath(ci,targetFile);
4414 if (targetFile.isEmpty())
4415 { ci->warn(m_templateName,m_line,"invalid parameter at right side of 'as' for resource command\n");
4419 ResourceMgr::instance().copyResourceAs(resourceFile,outputDirectory,targetFile);
4424 ResourceMgr::instance().copyResource(resourceFile,outputDirectory);
4434 //----------------------------------------------------------
4436 /** @brief Class representing the 'encoding' tag in a template */
4437 class TemplateNodeEncoding : public TemplateNodeCreator<TemplateNodeEncoding>
4440 TemplateNodeEncoding(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4441 : TemplateNodeCreator<TemplateNodeEncoding>(parser,parent,line)
4443 TRACE(("{TemplateNodeEncoding(%s)\n",data.data()));
4444 ExpressionParser ep(parser,line);
4447 parser->warn(m_templateName,line,"encoding tag is missing encoding argument");
4452 m_encExpr = ep.parse(data);
4455 stopAt.append("endencoding");
4456 parser->parse(this,line,stopAt,m_nodes);
4457 parser->removeNextToken(); // skip over endencoding
4458 TRACE(("}TemplateNodeEncoding(%s)\n",data.data()));
4460 ~TemplateNodeEncoding()
4464 void render(FTextStream &ts, TemplateContext *c)
4466 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4467 if (ci==0) return; // should not happen
4468 ci->setLocation(m_templateName,m_line);
4472 encStr = m_encExpr->resolve(c).toString();
4474 QCString oldEncStr = ci->encoding();
4475 if (!encStr.isEmpty())
4477 ci->setEncoding(m_templateName,m_line,encStr);
4479 m_nodes.render(ts,c);
4480 ci->setEncoding(m_templateName,m_line,oldEncStr);
4484 TemplateNodeList m_nodes;
4487 //----------------------------------------------------------
4489 /** @brief Factory class for creating tag AST nodes found in a template */
4490 class TemplateNodeFactory
4493 typedef TemplateNode *(*CreateFunc)(TemplateParser *parser,
4494 TemplateNode *parent,
4496 const QCString &data);
4498 static TemplateNodeFactory *instance()
4500 static TemplateNodeFactory *instance = 0;
4501 if (instance==0) instance = new TemplateNodeFactory;
4505 TemplateNode *create(const QCString &name,
4506 TemplateParser *parser,
4507 TemplateNode *parent,
4509 const QCString &data)
4511 if (m_registry.find(name)==0) return 0;
4512 return ((CreateFunc)m_registry[name])(parser,parent,line,data);
4515 void registerTemplateNode(const QCString &name,CreateFunc func)
4517 m_registry.insert(name,(void*)func);
4520 /** @brief Helper class for registering a template AST node */
4521 template<class T> class AutoRegister
4524 AutoRegister<T>(const QCString &key)
4526 TemplateNodeFactory::instance()->registerTemplateNode(key,T::createInstance);
4531 QDict<void> m_registry;
4534 // register a handler for each start tag we support
4535 static TemplateNodeFactory::AutoRegister<TemplateNodeIf> autoRefIf("if");
4536 static TemplateNodeFactory::AutoRegister<TemplateNodeFor> autoRefFor("for");
4537 static TemplateNodeFactory::AutoRegister<TemplateNodeMsg> autoRefMsg("msg");
4538 static TemplateNodeFactory::AutoRegister<TemplateNodeSet> autoRefSet("set");
4539 static TemplateNodeFactory::AutoRegister<TemplateNodeTree> autoRefTree("recursetree");
4540 static TemplateNodeFactory::AutoRegister<TemplateNodeWith> autoRefWith("with");
4541 static TemplateNodeFactory::AutoRegister<TemplateNodeBlock> autoRefBlock("block");
4542 static TemplateNodeFactory::AutoRegister<TemplateNodeCycle> autoRefCycle("cycle");
4543 static TemplateNodeFactory::AutoRegister<TemplateNodeRange> autoRefRange("range");
4544 static TemplateNodeFactory::AutoRegister<TemplateNodeExtend> autoRefExtend("extend");
4545 static TemplateNodeFactory::AutoRegister<TemplateNodeCreate> autoRefCreate("create");
4546 static TemplateNodeFactory::AutoRegister<TemplateNodeRepeat> autoRefRepeat("repeat");
4547 static TemplateNodeFactory::AutoRegister<TemplateNodeInclude> autoRefInclude("include");
4548 static TemplateNodeFactory::AutoRegister<TemplateNodeMarkers> autoRefMarkers("markers");
4549 static TemplateNodeFactory::AutoRegister<TemplateNodeTabbing> autoRefTabbing("tabbing");
4550 static TemplateNodeFactory::AutoRegister<TemplateNodeResource> autoRefResource("resource");
4551 static TemplateNodeFactory::AutoRegister<TemplateNodeEncoding> autoRefEncoding("encoding");
4552 static TemplateNodeFactory::AutoRegister<TemplateNodeSpaceless> autoRefSpaceless("spaceless");
4553 static TemplateNodeFactory::AutoRegister<TemplateNodeIndexEntry> autoRefIndexEntry("indexentry");
4554 static TemplateNodeFactory::AutoRegister<TemplateNodeOpenSubIndex> autoRefOpenSubIndex("opensubindex");
4555 static TemplateNodeFactory::AutoRegister<TemplateNodeCloseSubIndex> autoRefCloseSubIndex("closesubindex");
4557 //----------------------------------------------------------
4559 TemplateBlockContext::TemplateBlockContext() : m_blocks(257)
4561 m_blocks.setAutoDelete(TRUE);
4564 TemplateNodeBlock *TemplateBlockContext::get(const QCString &name) const
4566 QList<TemplateNodeBlock> *list = m_blocks.find(name);
4567 if (list==0 || list->count()==0)
4573 return list->getLast();
4577 TemplateNodeBlock *TemplateBlockContext::pop(const QCString &name) const
4579 QList<TemplateNodeBlock> *list = m_blocks.find(name);
4580 if (list==0 || list->count()==0)
4586 return list->take(list->count()-1);
4590 void TemplateBlockContext::add(TemplateNodeBlock *block)
4592 QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
4595 list = new QList<TemplateNodeBlock>;
4596 m_blocks.insert(block->name(),list);
4598 list->prepend(block);
4601 void TemplateBlockContext::add(TemplateBlockContext *ctx)
4603 QDictIterator< QList<TemplateNodeBlock> > di(ctx->m_blocks);
4604 QList<TemplateNodeBlock> *list;
4605 for (di.toFirst();(list=di.current());++di)
4607 QListIterator<TemplateNodeBlock> li(*list);
4608 TemplateNodeBlock *nb;
4609 for (li.toFirst();(nb=li.current());++li)
4616 void TemplateBlockContext::clear()
4621 void TemplateBlockContext::push(TemplateNodeBlock *block)
4623 QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
4626 list = new QList<TemplateNodeBlock>;
4627 m_blocks.insert(block->name(),list);
4629 list->append(block);
4633 //----------------------------------------------------------
4635 /** @brief Lexer class for turning a template into a list of tokens */
4639 TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data);
4640 void tokenize(QList<TemplateToken> &tokens);
4641 void setOpenCloseCharacters(char openChar,char closeChar)
4642 { m_openChar=openChar; m_closeChar=closeChar; }
4644 void addToken(QList<TemplateToken> &tokens,
4645 const char *data,int line,int startPos,int endPos,
4646 TemplateToken::Type type);
4648 const TemplateEngine *m_engine;
4649 QCString m_fileName;
4655 TemplateLexer::TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data) :
4656 m_engine(engine), m_fileName(fileName), m_data(data)
4662 void TemplateLexer::tokenize(QList<TemplateToken> &tokens)
4677 const char *p=m_data.data();
4679 int state=StateText;
4683 bool emptyOutputLine=TRUE;
4686 int markStartPos=-1;
4687 for (;(c=*p);p++,pos++)
4692 if (c==m_openChar) // {{ or {% or {# or something else
4694 state=StateBeginTemplate;
4696 else if (c!=' ' && c!='\t' && c!='\n') // non-whitepace text
4698 emptyOutputLine=FALSE;
4701 case StateBeginTemplate:
4713 if (m_openChar=='{')
4715 state=StateMaybeVar;
4719 state=StateVariable;
4725 emptyOutputLine=FALSE;
4732 warn(m_fileName,line,"unexpected new line inside %c%%...%%%c block",m_openChar,m_closeChar);
4733 m_engine->printIncludeContext(m_fileName,line);
4735 else if (c=='%') // %} or something else
4741 if (c==m_closeChar) // %}
4745 addToken(tokens,m_data.data(),line,lastTokenPos,
4746 emptyOutputLine ? startLinePos : markStartPos,
4747 TemplateToken::Text);
4748 addToken(tokens,m_data.data(),line,markStartPos+2,
4749 pos-1,TemplateToken::Block);
4750 lastTokenPos = pos+1;
4752 else // something else
4756 warn(m_fileName,line,"unexpected new line inside %c%%...%%%c block",m_openChar,m_closeChar);
4757 m_engine->printIncludeContext(m_fileName,line);
4765 warn(m_fileName,line,"unexpected new line inside %c#...#%c block",m_openChar,m_closeChar);
4766 m_engine->printIncludeContext(m_fileName,line);
4768 else if (c=='#') // #} or something else
4770 state=StateEndComment;
4773 case StateEndComment:
4774 if (c==m_closeChar) // #}
4776 // found comment tag!
4778 addToken(tokens,m_data.data(),line,lastTokenPos,
4779 emptyOutputLine ? startLinePos : markStartPos,
4780 TemplateToken::Text);
4781 lastTokenPos = pos+1;
4783 else // something else
4787 warn(m_fileName,line,"unexpected new line inside %c#...#%c block",m_openChar,m_closeChar);
4788 m_engine->printIncludeContext(m_fileName,line);
4805 state=StateVariable;
4810 emptyOutputLine=FALSE; // assume a variable expands to content
4813 warn(m_fileName,line,"unexpected new line inside %c{...}%c block",m_openChar,m_closeChar);
4814 m_engine->printIncludeContext(m_fileName,line);
4816 else if (c=='}') // }} or something else
4818 state=StateEndVariable;
4821 case StateEndVariable:
4822 if (c==m_closeChar) // }}
4824 // found variable tag!
4826 addToken(tokens,m_data.data(),line,lastTokenPos,
4827 emptyOutputLine ? startLinePos : markStartPos,
4828 TemplateToken::Text);
4829 addToken(tokens,m_data.data(),line,markStartPos+2,
4830 pos-1,TemplateToken::Variable);
4831 lastTokenPos = pos+1;
4833 else // something else
4837 warn(m_fileName,line,"unexpected new line inside %c{...}%c block",m_openChar,m_closeChar);
4838 m_engine->printIncludeContext(m_fileName,line);
4840 state=StateVariable;
4844 if (c=='\n') // new line
4848 // if the current line only contain commands and whitespace,
4849 // then skip it in the output by moving lastTokenPos
4850 if (markStartPos!=-1 && emptyOutputLine) lastTokenPos = startLinePos;
4854 emptyOutputLine=TRUE;
4857 if (lastTokenPos<pos)
4859 addToken(tokens,m_data.data(),line,
4861 TemplateToken::Text);
4865 void TemplateLexer::addToken(QList<TemplateToken> &tokens,
4866 const char *data,int line,
4867 int startPos,int endPos,
4868 TemplateToken::Type type)
4870 if (startPos<endPos)
4872 int len = endPos-startPos+1;
4874 qstrncpy(text.rawData(),data+startPos,len);
4875 if (type!=TemplateToken::Text) text = text.stripWhiteSpace();
4876 tokens.append(new TemplateToken(type,text,line));
4880 //----------------------------------------------------------
4882 TemplateParser::TemplateParser(const TemplateEngine *engine,
4883 const QCString &templateName,
4884 QList<TemplateToken> &tokens) :
4885 m_engine(engine), m_templateName(templateName), m_tokens(tokens)
4889 void TemplateParser::parse(
4890 TemplateNode *parent,int line,const QStrList &stopAt,
4891 QList<TemplateNode> &nodes)
4893 TRACE(("{TemplateParser::parse\n"));
4894 // process the tokens. Build node list
4895 while (hasNextToken())
4897 TemplateToken *tok = takeNextToken();
4898 //printf("%p:Token type=%d data='%s' line=%d\n",
4899 // parent,tok->type,tok->data.data(),tok->line);
4902 case TemplateToken::Text:
4903 nodes.append(new TemplateNodeText(this,parent,tok->line,tok->data));
4905 case TemplateToken::Variable: // {{ var }}
4906 nodes.append(new TemplateNodeVariable(this,parent,tok->line,tok->data));
4908 case TemplateToken::Block: // {% tag %}
4910 QCString command = tok->data;
4911 int sep = command.find(' ');
4914 command=command.left(sep);
4916 if (stopAt.contains(command))
4919 TRACE(("}TemplateParser::parse: stop\n"));
4925 arg = tok->data.mid(sep+1);
4927 TemplateNode *node = TemplateNodeFactory::instance()->
4928 create(command,this,parent,tok->line,arg);
4933 else if (command=="empty" || command=="else" ||
4934 command=="endif" || command=="endfor" ||
4935 command=="endblock" || command=="endwith" ||
4936 command=="endrecursetree" || command=="endspaceless" ||
4937 command=="endmarkers" || command=="endmsg" ||
4938 command=="endrepeat" || command=="elif" ||
4939 command=="endrange" || command=="endtabbing" ||
4940 command=="endencoding")
4942 warn(m_templateName,tok->line,"Found tag '%s' without matching start tag",command.data());
4946 warn(m_templateName,tok->line,"Unknown tag '%s'",command.data());
4953 if (!stopAt.isEmpty())
4955 QStrListIterator it(stopAt);
4958 for (it.toFirst();(s=it.current());++it)
4960 if (!options.isEmpty()) options+=", ";
4963 warn(m_templateName,line,"Unclosed tag in template, expected one of: %s",
4966 TRACE(("}TemplateParser::parse: last token\n"));
4969 bool TemplateParser::hasNextToken() const
4971 return !m_tokens.isEmpty();
4974 TemplateToken *TemplateParser::takeNextToken()
4976 return m_tokens.take(0);
4979 const TemplateToken *TemplateParser::currentToken() const
4981 return m_tokens.getFirst();
4984 void TemplateParser::removeNextToken()
4986 m_tokens.removeFirst();
4989 void TemplateParser::prependToken(const TemplateToken *token)
4991 m_tokens.prepend(token);
4994 void TemplateParser::warn(const char *fileName,int line,const char *fmt,...) const
4998 va_warn(fileName,line,fmt,args);
5000 m_engine->printIncludeContext(fileName,line);
5005 //----------------------------------------------------------
5008 TemplateImpl::TemplateImpl(TemplateEngine *engine,const QCString &name,const QCString &data,
5009 const QCString &extension)
5014 TemplateLexer lexer(engine,name,data);
5015 if (extension=="tex")
5017 lexer.setOpenCloseCharacters('<','>');
5019 QList<TemplateToken> tokens;
5020 tokens.setAutoDelete(TRUE);
5021 lexer.tokenize(tokens);
5022 TemplateParser parser(engine,name,tokens);
5023 parser.parse(this,1,QStrList(),m_nodes);
5026 TemplateImpl::~TemplateImpl()
5028 //printf("deleting template %s\n",m_name.data());
5031 void TemplateImpl::render(FTextStream &ts, TemplateContext *c)
5033 TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
5034 if (ci==0) return; // should not happen
5035 if (!m_nodes.isEmpty())
5037 TemplateNodeExtend *ne = dynamic_cast<TemplateNodeExtend*>(m_nodes.getFirst());
5038 if (ne==0) // normal template, add blocks to block context
5040 TemplateBlockContext *bc = ci->blockContext();
5041 QListIterator<TemplateNode> li(m_nodes);
5043 for (li.toFirst();(n=li.current());++li)
5045 TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
5052 m_nodes.render(ts,c);
5056 //----------------------------------------------------------
5058 /** @brief Private data of the template engine */
5059 class TemplateEngine::Private
5064 enum Type { Template, Block };
5065 IncludeEntry(Type type,const QCString &fileName,const QCString &blockName,int line)
5066 : m_type(type), m_fileName(fileName), m_blockName(blockName), m_line(line) {}
5067 Type type() const { return m_type; }
5068 QCString fileName() const { return m_fileName; }
5069 QCString blockName() const { return m_blockName; }
5070 int line() const { return m_line; }
5074 QCString m_fileName;
5075 QCString m_blockName;
5079 Private(TemplateEngine *engine) : m_templateCache(17) /*, m_indent(0)*/, m_engine(engine)
5081 m_templateCache.setAutoDelete(TRUE);
5082 m_includeStack.setAutoDelete(TRUE);
5084 Template *loadByName(const QCString &fileName,int line)
5086 //for (int i=0;i<m_indent;i++) printf(" ");
5088 //printf("loadByName(%s,%d) {\n",fileName.data(),line);
5089 m_includeStack.append(new IncludeEntry(IncludeEntry::Template,fileName,QCString(),line));
5090 Template *templ = m_templateCache.find(fileName);
5091 if (templ==0) // first time template is referenced
5093 QCString filePath = m_templateDirName+"/"+fileName;
5095 if (f.open(IO_ReadOnly))
5097 QFileInfo fi(filePath);
5099 QCString data(size+1);
5100 if (f.readBlock(data.rawData(),size)==size)
5102 templ = new TemplateImpl(m_engine,filePath,data,m_extension);
5103 m_templateCache.insert(fileName,templ);
5107 // fallback to default built-in template
5108 const QCString data = ResourceMgr::instance().getAsString(fileName);
5109 if (!data.isEmpty())
5111 templ = new TemplateImpl(m_engine,fileName,data,m_extension);
5112 m_templateCache.insert(fileName,templ);
5116 err("Could not open template file %s\n",fileName.data());
5121 void unload(Template * /*t*/)
5125 //for (int i=0;i<m_indent;i++) printf(" ");
5127 m_includeStack.removeLast();
5130 void enterBlock(const QCString &fileName,const QCString &blockName,int line)
5132 //for (int i=0;i<m_indent;i++) printf(" ");
5134 //printf("enterBlock(%s,%s,%d) {\n",fileName.data(),blockName.data(),line);
5135 m_includeStack.append(new IncludeEntry(IncludeEntry::Block,fileName,blockName,line));
5141 //for (int i=0;i<m_indent;i++) printf(" ");
5143 m_includeStack.removeLast();
5146 void printIncludeContext(const char *fileName,int line) const
5148 QListIterator<IncludeEntry> li(m_includeStack);
5150 IncludeEntry *ie=li.current();
5151 while ((ie=li.current()))
5154 IncludeEntry *next=li.current();
5155 if (ie->type()==IncludeEntry::Template)
5159 warn(fileName,line," inside template '%s' included from template '%s' at line %d",ie->fileName().data(),next->fileName().data(),ie->line());
5162 else // ie->type()==IncludeEntry::Block
5164 warn(fileName,line," included by block '%s' inside template '%s' at line %d",ie->blockName().data(),
5165 ie->fileName().data(),ie->line());
5170 void setOutputExtension(const char *extension)
5172 m_extension = extension;
5175 QCString outputExtension() const
5180 void setTemplateDir(const char *dirName)
5182 m_templateDirName = dirName;
5186 QDict<Template> m_templateCache;
5187 //mutable int m_indent;
5188 TemplateEngine *m_engine;
5189 QList<IncludeEntry> m_includeStack;
5190 QCString m_extension;
5191 QCString m_templateDirName;
5194 TemplateEngine::TemplateEngine()
5196 p = new Private(this);
5199 TemplateEngine::~TemplateEngine()
5204 TemplateContext *TemplateEngine::createContext() const
5206 return new TemplateContextImpl(this);
5209 void TemplateEngine::destroyContext(TemplateContext *ctx)
5214 Template *TemplateEngine::loadByName(const QCString &fileName,int line)
5216 return p->loadByName(fileName,line);
5219 void TemplateEngine::unload(Template *t)
5224 void TemplateEngine::enterBlock(const QCString &fileName,const QCString &blockName,int line)
5226 p->enterBlock(fileName,blockName,line);
5229 void TemplateEngine::leaveBlock()
5234 void TemplateEngine::printIncludeContext(const char *fileName,int line) const
5236 p->printIncludeContext(fileName,line);
5239 void TemplateEngine::setOutputExtension(const char *extension)
5241 p->setOutputExtension(extension);
5244 QCString TemplateEngine::outputExtension() const
5246 return p->outputExtension();
5249 void TemplateEngine::setTemplateDir(const char *dirName)
5251 p->setTemplateDir(dirName);