Imported Upstream version 1.8.8
[platform/upstream/doxygen.git] / src / template.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2014 by Dimitri van Heesch.
4  *
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.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15
16 #include "template.h"
17
18 #include <stdio.h>
19 #include <stdarg.h>
20
21 #include <qlist.h>
22 #include <qarray.h>
23 #include <qdict.h>
24 #include <qstrlist.h>
25 #include <qvaluelist.h>
26 #include <qstack.h>
27 #include <qfile.h>
28 #include <qregexp.h>
29 #include <qcstring.h>
30 #include <qdir.h>
31
32 #include "sortdict.h"
33 #include "ftextstream.h"
34 #include "message.h"
35 #include "util.h"
36
37 #define ENABLE_TRACING 0
38
39 #if ENABLE_TRACING
40 #define TRACE(x) printf x
41 #else
42 #define TRACE(x)
43 #endif
44
45 class TemplateToken;
46
47 //-------------------------------------------------------------------
48
49 static QValueList<QCString> split(const QCString &str,const QCString &sep,
50                                   bool allowEmptyEntries=FALSE,bool cleanup=TRUE)
51 {
52   QValueList<QCString> lst;
53
54   int j = 0;
55   int i = str.find( sep, j );
56
57   while (i!=-1)
58   {
59     if ( str.mid(j,i-j).length() > 0 )
60     {
61       if (cleanup)
62       {
63         lst.append(str.mid(j,i-j).stripWhiteSpace());
64       }
65       else
66       {
67         lst.append(str.mid(j,i-j));
68       }
69     }
70     else if (allowEmptyEntries)
71     {
72       lst.append("");
73     }
74     j = i + sep.length();
75     i = str.find(sep,j);
76   }
77
78   int l = str.length() - 1;
79   if (str.mid(j,l-j+1).length()>0)
80   {
81     if (cleanup)
82     {
83       lst.append(str.mid(j,l-j+1).stripWhiteSpace());
84     }
85     else
86     {
87       lst.append(str.mid(j,l-j+1));
88     }
89   }
90   else if (allowEmptyEntries)
91   {
92     lst.append("");
93   }
94
95   return lst;
96 }
97
98 //----------------------------------------------------------------------------
99
100 #if ENABLE_TRACING
101 static QCString replace(const char *s,char csrc,char cdst)
102 {
103   QCString result = s;
104   for (char *p=result.data();*p;p++)
105   {
106     if (*p==csrc) *p=cdst;
107   }
108   return result;
109 }
110 #endif
111
112 //- TemplateVariant implementation -------------------------------------------
113
114 /** @brief Private data of a template variant object */
115 class TemplateVariant::Private
116 {
117   public:
118     Private() : raw(FALSE) {}
119     Type                type;
120     int                 intVal;
121     QCString            strVal;
122     bool                boolVal;
123     TemplateStructIntf *strukt;
124     TemplateListIntf   *list;
125     Delegate            delegate;
126     bool                raw;
127 };
128
129 TemplateVariant::TemplateVariant()
130 {
131   p = new Private;
132   p->type=None;
133 }
134
135 TemplateVariant::TemplateVariant(bool b)
136 {
137   p = new Private;
138   p->type = Bool;
139   p->boolVal = b;
140 }
141
142 TemplateVariant::TemplateVariant(int v)
143 {
144   p = new Private;
145   p->type = Integer;
146   p->intVal = v;
147 }
148
149 TemplateVariant::TemplateVariant(const char *s,bool raw)
150 {
151   p = new Private;
152   p->type = String;
153   p->strVal = s;
154   p->raw = raw;
155 }
156
157 TemplateVariant::TemplateVariant(const QCString &s,bool raw)
158 {
159   p = new Private;
160   p->type = String;
161   p->strVal = s;
162   p->raw = raw;
163 }
164
165 TemplateVariant::TemplateVariant(TemplateStructIntf *s)
166 {
167   p = new Private;
168   p->type = Struct;
169   p->strukt = s;
170   p->strukt->addRef();
171 }
172
173 TemplateVariant::TemplateVariant(TemplateListIntf *l)
174 {
175   p = new Private;
176   p->type = List;
177   p->list = l;
178   p->list->addRef();
179 }
180
181 TemplateVariant::TemplateVariant(const TemplateVariant::Delegate &delegate)
182 {
183   p = new Private;
184   p->type = Function;
185   p->delegate = delegate;
186 }
187
188 TemplateVariant::~TemplateVariant()
189 {
190   if (p->type==Struct) p->strukt->release();
191   else if (p->type==List) p->list->release();
192   delete p;
193 }
194
195 TemplateVariant::TemplateVariant(const TemplateVariant &v)
196 {
197   p = new Private;
198   p->type    = v.p->type;
199   p->raw     = v.p->raw;
200   switch (p->type)
201   {
202     case None: break;
203     case Bool:     p->boolVal = v.p->boolVal; break;
204     case Integer:  p->intVal  = v.p->intVal;  break;
205     case String:   p->strVal  = v.p->strVal;  break;
206     case Struct:   p->strukt  = v.p->strukt;  p->strukt->addRef(); break;
207     case List:     p->list    = v.p->list;    p->list->addRef();   break;
208     case Function: p->delegate= v.p->delegate;break;
209   }
210 }
211
212 TemplateVariant &TemplateVariant::operator=(const TemplateVariant &v)
213 {
214   // assignment can change the type of the variable, so we have to be
215   // careful with reference counted content.
216   TemplateStructIntf *tmpStruct = p->type==Struct ? p->strukt : 0;
217   TemplateListIntf   *tmpList   = p->type==List   ? p->list   : 0;
218   Type tmpType = p->type;
219
220   p->type    = v.p->type;
221   p->raw     = v.p->raw;
222   switch (p->type)
223   {
224     case None: break;
225     case Bool:     p->boolVal = v.p->boolVal; break;
226     case Integer:  p->intVal  = v.p->intVal;  break;
227     case String:   p->strVal  = v.p->strVal;  break;
228     case Struct:   p->strukt  = v.p->strukt;  p->strukt->addRef(); break;
229     case List:     p->list    = v.p->list;    p->list->addRef();   break;
230     case Function: p->delegate= v.p->delegate;break;
231   }
232
233   // release overwritten reference counted values
234   if      (tmpType==Struct && tmpStruct) tmpStruct->release();
235   else if (tmpType==List   && tmpList  ) tmpList->release();
236   return *this;
237 }
238
239 QCString TemplateVariant::toString() const
240 {
241   QCString result;
242   switch (p->type)
243   {
244     case None:
245       break;
246     case Bool:
247       result=p->boolVal ? "true" : "false";
248       break;
249     case Integer:
250       result=QCString().setNum(p->intVal);
251       break;
252     case String:
253       result=p->strVal;
254       break;
255     case Struct:
256       result="[struct]";
257       break;
258     case List:
259       result="[list]";
260       break;
261     case Function:
262       result="[function]";
263       break;
264   }
265   return result;
266 }
267
268 bool TemplateVariant::toBool() const
269 {
270   bool result=FALSE;
271   switch (p->type)
272   {
273     case None:
274       break;
275     case Bool:
276       result = p->boolVal;
277       break;
278     case Integer:
279       result = p->intVal!=0;
280       break;
281     case String:
282       result = !p->strVal.isEmpty(); // && p->strVal!="false" && p->strVal!="0";
283       break;
284     case Struct:
285       result = TRUE;
286       break;
287     case List:
288       result = p->list->count()!=0;
289       break;
290     case Function:
291       result = FALSE;
292       break;
293   }
294   return result;
295 }
296
297 int TemplateVariant::toInt() const
298 {
299   int result=0;
300   switch (p->type)
301   {
302     case None:
303       break;
304     case Bool:
305       result = p->boolVal ? 1 : 0;
306       break;
307     case Integer:
308       result = p->intVal;
309       break;
310     case String:
311       result = p->strVal.toInt();
312       break;
313     case Struct:
314       break;
315     case List:
316       result = p->list->count();
317       break;
318     case Function:
319       result = 0;
320       break;
321   }
322   return result;
323 }
324
325 TemplateStructIntf *TemplateVariant::toStruct() const
326 {
327   return p->type==Struct ? p->strukt : 0;
328 }
329
330 TemplateListIntf *TemplateVariant::toList() const
331 {
332   return p->type==List ? p->list : 0;
333 }
334
335 TemplateVariant TemplateVariant::call(const QValueList<TemplateVariant> &args)
336 {
337   if (p->type==Function) return p->delegate(args);
338   return TemplateVariant();
339 }
340
341 bool TemplateVariant::operator==(TemplateVariant &other)
342 {
343   if (p->type==None)
344   {
345     return FALSE;
346   }
347   if (p->type==TemplateVariant::List && other.p->type==TemplateVariant::List)
348   {
349     return p->list==other.p->list; // TODO: improve me
350   }
351   else if (p->type==TemplateVariant::Struct && other.p->type==TemplateVariant::Struct)
352   {
353     return p->strukt==other.p->strukt; // TODO: improve me
354   }
355   else
356   {
357     return toString()==other.toString();
358   }
359 }
360
361 TemplateVariant::Type TemplateVariant::type() const
362 {
363   return p->type;
364 }
365
366 QCString TemplateVariant::typeAsString() const
367 {
368   switch (p->type)
369   {
370     case None:     return "none";
371     case Bool:     return "bool";
372     case Integer:  return "integer";
373     case String:   return "string";
374     case Struct:   return "struct";
375     case List:     return "list";
376     case Function: return "function";
377   }
378   return "invalid";
379 }
380
381 bool TemplateVariant::isValid() const
382 {
383   return p->type!=None;
384 }
385
386 void TemplateVariant::setRaw(bool b)
387 {
388   p->raw = b;
389 }
390
391 bool TemplateVariant::raw() const
392 {
393   return p->raw;
394 }
395
396 //- Template struct implementation --------------------------------------------
397
398
399 /** @brief Private data of a template struct object */
400 class TemplateStruct::Private
401 {
402   public:
403     Private() : fields(17), refCount(0)
404     { fields.setAutoDelete(TRUE); }
405     QDict<TemplateVariant> fields;
406     int refCount;
407 };
408
409 TemplateStruct::TemplateStruct()
410 {
411   p = new Private;
412 }
413
414 TemplateStruct::~TemplateStruct()
415 {
416   delete p;
417 }
418
419 int TemplateStruct::addRef()
420 {
421   return ++p->refCount;
422 }
423
424 int TemplateStruct::release()
425 {
426   int count = --p->refCount;
427   if (count<=0)
428   {
429     delete this;
430   }
431   return count;
432 }
433
434 void TemplateStruct::set(const char *name,const TemplateVariant &v)
435 {
436   TemplateVariant *pv = p->fields.find(name);
437   if (pv) // change existing field
438   {
439     *pv = v;
440   }
441   else // insert new field
442   {
443     p->fields.insert(name,new TemplateVariant(v));
444   }
445 }
446
447 TemplateVariant TemplateStruct::get(const char *name) const
448 {
449   TemplateVariant *v = p->fields.find(name);
450   return v ? *v : TemplateVariant();
451 }
452
453 TemplateStruct *TemplateStruct::alloc()
454 {
455   return new TemplateStruct;
456 }
457
458 //- Template list implementation ----------------------------------------------
459
460
461 /** @brief Private data of a template list object */
462 class TemplateList::Private
463 {
464   public:
465     Private() : index(-1), refCount(0) {}
466     QValueList<TemplateVariant> elems;
467     int index;
468     int refCount;
469 };
470
471
472 TemplateList::TemplateList()
473 {
474   p = new Private;
475 }
476
477 TemplateList::~TemplateList()
478 {
479   delete p;
480 }
481
482 int TemplateList::addRef()
483 {
484   return ++p->refCount;
485 }
486
487 int TemplateList::release()
488 {
489   int count = --p->refCount;
490   if (count<=0)
491   {
492     delete this;
493   }
494   return count;
495 }
496
497 int TemplateList::count() const
498 {
499   return p->elems.count();
500 }
501
502 void TemplateList::append(const TemplateVariant &v)
503 {
504   p->elems.append(v);
505 }
506
507 // iterator support
508 class TemplateListConstIterator : public TemplateListIntf::ConstIterator
509 {
510   public:
511     TemplateListConstIterator(const TemplateList &l) : m_list(l) { m_index=-1; }
512     virtual ~TemplateListConstIterator() {}
513     virtual void toFirst()
514     {
515       m_it = m_list.p->elems.begin();
516       m_index=0;
517     }
518     virtual void toLast()
519     {
520       m_it = m_list.p->elems.fromLast();
521       m_index=m_list.count()-1;
522     }
523     virtual void toNext()
524     {
525       if (m_it!=m_list.p->elems.end())
526       {
527         ++m_it;
528         ++m_index;
529       }
530     }
531     virtual void toPrev()
532     {
533       if (m_index>0)
534       {
535         --m_it;
536         --m_index;
537       }
538       else
539       {
540         m_index=-1;
541       }
542     }
543     virtual bool current(TemplateVariant &v) const
544     {
545       if (m_index<0 || m_it==m_list.p->elems.end())
546       {
547         v = TemplateVariant();
548         return FALSE;
549       }
550       else
551       {
552         v = *m_it;
553         return TRUE;
554       }
555     }
556   private:
557     const TemplateList &m_list;
558     QValueList<TemplateVariant>::ConstIterator m_it;
559     int m_index;
560 };
561
562 TemplateListIntf::ConstIterator *TemplateList::createIterator() const
563 {
564   return new TemplateListConstIterator(*this);
565 }
566
567 TemplateVariant TemplateList::at(int index) const
568 {
569   if (index>=0 && index<(int)p->elems.count())
570   {
571     return p->elems[index];
572   }
573   else
574   {
575     return TemplateVariant();
576   }
577 }
578
579 TemplateList *TemplateList::alloc()
580 {
581   return new TemplateList;
582 }
583
584 //- Operator types ------------------------------------------------------------
585
586 /** @brief Class representing operators that can appear in template expressions */
587 class Operator
588 {
589   public:
590       /* Operator precedence (low to high)
591          or
592          and
593          not
594          in
595          ==, !=, <, >, <=, >=
596          +, -
597          *, /, %
598          |
599          :
600          ,
601        */
602     enum Type
603     {
604       Or, And, Not, In, Equal, NotEqual, Less, Greater, LessEqual,
605       GreaterEqual, Plus, Minus, Multiply, Divide, Modulo, Filter, Colon, Comma,
606       LeftParen, RightParen,
607       Last
608     };
609
610     static const char *toString(Type op)
611     {
612       switch(op)
613       {
614         case Or:           return "or";
615         case And:          return "and";
616         case Not:          return "not";
617         case In:           return "in";
618         case Equal:        return "==";
619         case NotEqual:     return "!=";
620         case Less:         return "<";
621         case Greater:      return ">";
622         case LessEqual:    return "<=";
623         case GreaterEqual: return ">=";
624         case Plus:         return "+";
625         case Minus:        return "-";
626         case Multiply:     return "*";
627         case Divide:       return "/";
628         case Modulo:       return "%";
629         case Filter:       return "|";
630         case Colon:        return ":";
631         case Comma:        return ",";
632         case LeftParen:    return "(";
633         case RightParen:   return ")";
634         case Last:         return "?";
635       }
636       return "?";
637     }
638 };
639
640 //-----------------------------------------------------------------------------
641
642 class TemplateNodeBlock;
643
644 /** @brief Class holding stacks of blocks available in the context */
645 class TemplateBlockContext
646 {
647   public:
648     TemplateBlockContext();
649     TemplateNodeBlock *get(const QCString &name) const;
650     TemplateNodeBlock *pop(const QCString &name) const;
651     void add(TemplateNodeBlock *block);
652     void add(TemplateBlockContext *ctx);
653     void push(TemplateNodeBlock *block);
654     void clear();
655   private:
656     QDict< QList<TemplateNodeBlock> > m_blocks;
657 };
658
659 /** @brief A container to store a key-value pair */
660 struct TemplateKeyValue
661 {
662   TemplateKeyValue() {}
663   TemplateKeyValue(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
664   QCString key;
665   TemplateVariant value;
666 };
667
668 /** @brief Internal class representing the implementation of a template
669  *  context */
670 class TemplateContextImpl : public TemplateContext
671 {
672   public:
673     TemplateContextImpl(const TemplateEngine *e);
674     virtual ~TemplateContextImpl();
675
676     // TemplateContext methods
677     void push();
678     void pop();
679     void set(const char *name,const TemplateVariant &v);
680     TemplateVariant get(const QCString &name) const;
681     const TemplateVariant *getRef(const QCString &name) const;
682     void setOutputDirectory(const QCString &dir)
683     { m_outputDir = dir; }
684     void setEscapeIntf(const QCString &ext,TemplateEscapeIntf *intf)
685     {
686       int i=(!ext.isEmpty() && ext.at(0)=='.') ? 1 : 0;
687       m_escapeIntfDict.insert(ext.mid(i),new TemplateEscapeIntf*(intf));
688     }
689     void selectEscapeIntf(const QCString &ext)
690     { TemplateEscapeIntf **ppIntf = m_escapeIntfDict.find(ext);
691       m_activeEscapeIntf = ppIntf ? *ppIntf : 0;
692     }
693     void setActiveEscapeIntf(TemplateEscapeIntf *intf) { m_activeEscapeIntf = intf; }
694     void setSpacelessIntf(TemplateSpacelessIntf *intf) { m_spacelessIntf = intf; }
695
696     // internal methods
697     TemplateBlockContext *blockContext();
698     TemplateVariant getPrimary(const QCString &name) const;
699     void setLocation(const QCString &templateName,int line)
700     { m_templateName=templateName; m_line=line; }
701     QCString templateName() const                { return m_templateName; }
702     int line() const                             { return m_line; }
703     QCString outputDirectory() const             { return m_outputDir; }
704     TemplateEscapeIntf *escapeIntf() const       { return m_activeEscapeIntf; }
705     TemplateSpacelessIntf *spacelessIntf() const { return m_spacelessIntf; }
706     void enableSpaceless(bool b)                 { if (b && !m_spacelessEnabled) m_spacelessIntf->reset(); 
707                                                    m_spacelessEnabled=b;
708                                                  }
709     bool spacelessEnabled() const                { return m_spacelessEnabled && m_spacelessIntf; }
710     void warn(const char *fileName,int line,const char *fmt,...) const;
711
712     // index related functions
713     void openSubIndex(const QCString &indexName);
714     void closeSubIndex(const QCString &indexName);
715     void addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments);
716
717   private:
718     const TemplateEngine *m_engine;
719     QCString m_templateName;
720     int m_line;
721     QCString m_outputDir;
722     QList< QDict<TemplateVariant> > m_contextStack;
723     TemplateBlockContext m_blockContext;
724     QDict<TemplateEscapeIntf*> m_escapeIntfDict;
725     TemplateEscapeIntf *m_activeEscapeIntf;
726     TemplateSpacelessIntf *m_spacelessIntf;
727     bool m_spacelessEnabled;
728     TemplateAutoRef<TemplateStruct> m_indices;
729     QDict< QStack<TemplateVariant> > m_indexStacks;
730 };
731
732 //-----------------------------------------------------------------------------
733
734 /** @brief The implementation of the "add" filter */
735 class FilterAdd
736 {
737   public:
738     static int variantIntValue(const TemplateVariant &v,bool &isInt)
739     {
740       isInt = v.type()==TemplateVariant::Integer;
741       if (!isInt && v.type()==TemplateVariant::String)
742       {
743         return v.toString().toInt(&isInt);
744       }
745       return isInt ? v.toInt() : 0;
746     }
747     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
748     {
749       if (!v.isValid())
750       {
751         return arg;
752       }
753       bool lhsIsInt;
754       int  lhsValue = variantIntValue(v,lhsIsInt);
755       bool rhsIsInt;
756       int  rhsValue = variantIntValue(arg,rhsIsInt);
757       if (lhsIsInt && rhsIsInt)
758       {
759         return lhsValue+rhsValue;
760       }
761       else if (v.type()==TemplateVariant::String && arg.type()==TemplateVariant::String)
762       {
763         return TemplateVariant(v.toString() + arg.toString());
764       }
765       else
766       {
767         return v;
768       }
769     }
770 };
771
772 //-----------------------------------------------------------------------------
773
774 /** @brief The implementation of the "get" filter */
775 class FilterGet
776 {
777   public:
778     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
779     {
780       if (v.isValid() && v.type()==TemplateVariant::Struct && arg.type()==TemplateVariant::String)
781       {
782         TemplateVariant result = v.toStruct()->get(arg.toString());
783         //printf("\nok[%s]=%d\n",arg.toString().data(),result.type());
784         return result;
785       }
786       else
787       {
788         //printf("\nnok[%s]\n",arg.toString().data());
789         return FALSE;
790       }
791     }
792 };
793
794
795 //-----------------------------------------------------------------------------
796
797 /** @brief The implementation of the "append" filter */
798 class FilterAppend
799 {
800   public:
801     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
802     {
803       if ((v.type()==TemplateVariant::String || v.type()==TemplateVariant::Integer) &&
804           arg.type()==TemplateVariant::String)
805       {
806         return TemplateVariant(v.toString() + arg.toString());
807       }
808       else
809       {
810         return v;
811       }
812     }
813 };
814
815 //-----------------------------------------------------------------------------
816
817 /** @brief The implementation of the "prepend" filter */
818 class FilterPrepend
819 {
820   public:
821     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
822     {
823       if ((v.type()==TemplateVariant::String || v.type()==TemplateVariant::Integer) &&
824           arg.type()==TemplateVariant::String)
825       {
826         return TemplateVariant(arg.toString() + v.toString());
827       }
828       else
829       {
830         return v;
831       }
832     }
833 };
834
835 //--------------------------------------------------------------------
836
837 /** @brief The implementation of the "length" filter */
838 class FilterLength
839 {
840   public:
841     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
842     {
843       if (!v.isValid())
844       {
845         return TemplateVariant();
846       }
847       if (v.type()==TemplateVariant::List)
848       {
849         return TemplateVariant(v.toList()->count());
850       }
851       else if (v.type()==TemplateVariant::String)
852       {
853         return TemplateVariant((int)v.toString().length());
854       }
855       else
856       {
857         return TemplateVariant();
858       }
859     }
860 };
861
862 //--------------------------------------------------------------------
863
864 /** @brief The implementation of the "default" filter */
865 class FilterDefault
866 {
867   public:
868     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &arg)
869     {
870       if (!v.isValid())
871       {
872         return arg;
873       }
874       else if (v.type()==TemplateVariant::String && v.toString().isEmpty())
875       {
876         return arg;
877       }
878       else
879       {
880         return v;
881       }
882     }
883 };
884
885 //--------------------------------------------------------------------
886
887 /** @brief The implementation of the "flatten" filter */
888 class FilterFlatten
889 {
890   public:
891     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
892     {
893       if (!v.isValid() || v.type()!=TemplateVariant::List)
894       {
895         return v;
896       }
897       else
898       {
899         TemplateList *list = TemplateList::alloc();
900         flatten(v.toList(),list);
901         return TemplateVariant(list);
902       }
903     }
904
905   private:
906     static void flatten(TemplateListIntf *tree,TemplateList *list)
907     {
908       TemplateListIntf::ConstIterator *it = tree->createIterator();
909       TemplateVariant item;
910       for (it->toFirst();(it->current(item));it->toNext())
911       {
912         TemplateStructIntf *s = item.toStruct();
913         if (s)
914         {
915           list->append(item);
916           // if s has "children" then recurse into the children
917           TemplateVariant children = s->get("children");
918           if (children.isValid() && children.type()==TemplateVariant::List)
919           {
920             flatten(children.toList(),list);
921           }
922         }
923         else
924         {
925           list->append(item);
926         }
927       }
928       delete it;
929     }
930 };
931
932 //--------------------------------------------------------------------
933
934 /** @brief The implementation of the "listsort" filter */
935 class FilterListSort
936 {
937     struct ListElem
938     {
939       ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
940       QCString key;
941       TemplateVariant value;
942     };
943     class SortList : public QList<ListElem>
944     {
945       public:
946         SortList() { setAutoDelete(TRUE); }
947       private:
948         int compareValues(const ListElem *item1,const ListElem *item2) const
949         {
950           return qstrcmp(item1->key,item2->key);
951         }
952     };
953   public:
954     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
955     {
956       if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
957       {
958         //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
959         TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
960
961         TemplateVariant item;
962         TemplateList *result = TemplateList::alloc();
963
964         // create list of items based on v using the data in args as a sort key
965         SortList sortList;
966         for (it->toFirst();(it->current(item));it->toNext())
967         {
968           TemplateStructIntf *s = item.toStruct();
969           if (s)
970           {
971             QCString sortKey = determineSortKey(s,args.toString());
972             sortList.append(new ListElem(sortKey,item));
973             //printf("sortKey=%s\n",sortKey.data());
974           }
975         }
976         delete it;
977
978         // sort the list
979         sortList.sort();
980
981         // add sorted items to the result list
982         QListIterator<ListElem> sit(sortList);
983         ListElem *elem;
984         for (sit.toFirst();(elem=sit.current());++sit)
985         {
986           result->append(elem->value);
987         }
988         return result;
989       }
990       return v;
991     }
992
993   private:
994     static QCString determineSortKey(TemplateStructIntf *s,const QCString &arg)
995     {
996       int i,p=0;
997       QCString result;
998       while ((i=arg.find("{{",p))!=-1)
999       {
1000         result+=arg.mid(p,i-p);
1001         int j=arg.find("}}",i+2);
1002         if (j!=-1)
1003         {
1004           QCString var = arg.mid(i+2,j-i-2);
1005           TemplateVariant val=s->get(var);
1006           //printf("found argument %s value=%s\n",var.data(),val.toString().data());
1007           result+=val.toString();
1008           p=j+2;
1009         }
1010         else
1011         {
1012           p=i+1;
1013         }
1014       }
1015       result+=arg.right(arg.length()-p);
1016       return result;
1017     }
1018 };
1019
1020 //--------------------------------------------------------------------
1021
1022 /** @brief The implementation of the "groupBy" filter */
1023 class FilterGroupBy
1024 {
1025     struct ListElem
1026     {
1027       ListElem(const QCString &k,const TemplateVariant &v) : key(k), value(v) {}
1028       QCString key;
1029       TemplateVariant value;
1030     };
1031     class SortList : public QList<ListElem>
1032     {
1033       public:
1034         SortList() { setAutoDelete(TRUE); }
1035       private:
1036         int compareValues(const ListElem *item1,const ListElem *item2) const
1037         {
1038           return qstrcmp(item1->key,item2->key);
1039         }
1040     };
1041   public:
1042     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1043     {
1044       if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
1045       {
1046         //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
1047         TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
1048
1049         TemplateVariant item;
1050         TemplateList *result = TemplateList::alloc();
1051
1052         // create list of items based on v using the data in args as a sort key
1053         SortList sortList;
1054         for (it->toFirst();(it->current(item));it->toNext())
1055         {
1056           TemplateStructIntf *s = item.toStruct();
1057           if (s)
1058           {
1059             QCString sortKey = determineSortKey(s,args.toString());
1060             sortList.append(new ListElem(sortKey,item));
1061             //printf("sortKey=%s\n",sortKey.data());
1062           }
1063         }
1064         delete it;
1065
1066         // sort the list
1067         sortList.sort();
1068
1069         // add sorted items to the result list
1070         QListIterator<ListElem> sit(sortList);
1071         ListElem *elem;
1072         TemplateList *groupList=0;
1073         QCString prevKey;
1074         for (sit.toFirst();(elem=sit.current());++sit)
1075         {
1076           if (groupList==0 || elem->key!=prevKey)
1077           {
1078             groupList = TemplateList::alloc();
1079             result->append(groupList);
1080             prevKey = elem->key;
1081           }
1082           groupList->append(elem->value);
1083         }
1084         return result;
1085       }
1086       return v;
1087     }
1088
1089   private:
1090     static QCString determineSortKey(TemplateStructIntf *s,const QCString &attribName)
1091     {
1092        TemplateVariant v = s->get(attribName);
1093        return v.toString();
1094     }
1095 };
1096
1097 //--------------------------------------------------------------------
1098
1099 /** @brief The implementation of the "paginate" filter */
1100 class FilterPaginate
1101 {
1102   public:
1103     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1104     {
1105       if (v.isValid() && v.type()==TemplateVariant::List &&
1106           args.isValid() && args.type()==TemplateVariant::Integer)
1107       {
1108         int pageSize = args.toInt();
1109         TemplateListIntf *list   = v.toList();
1110         TemplateList     *result = TemplateList::alloc();
1111         TemplateListIntf::ConstIterator *it = list->createIterator();
1112         TemplateVariant   item;
1113         TemplateList     *pageList=0;
1114         int i = 0;
1115         for (it->toFirst();(it->current(item));it->toNext())
1116         {
1117           if (pageList==0)
1118           {
1119             pageList = TemplateList::alloc();
1120             result->append(pageList);
1121           }
1122           pageList->append(item);
1123           i++;
1124           if (i==pageSize) // page is full start a new one
1125           {
1126             pageList=0;
1127             i=0;
1128           }
1129         }
1130         delete it;
1131         return result;
1132       }
1133       else // wrong arguments
1134       {
1135         return v;
1136       }
1137     }
1138 };
1139
1140 //--------------------------------------------------------------------
1141
1142 /** @brief The implementation of the "alphaIndex" filter */
1143 class FilterAlphaIndex
1144 {
1145   private:
1146     struct ListElem
1147     {
1148       ListElem(uint k,const TemplateVariant &v) : key(k), value(v) {}
1149       uint key;
1150       TemplateVariant value;
1151     };
1152     class SortList : public QList<ListElem>
1153     {
1154       public:
1155         SortList() { setAutoDelete(TRUE); }
1156       private:
1157         int compareValues(const ListElem *item1,const ListElem *item2) const
1158         {
1159           return item1->key-item2->key;
1160         }
1161     };
1162     static QCString keyToLetter(uint startLetter)
1163     {
1164       return QString(QChar(startLetter)).utf8();
1165     }
1166     static QCString keyToLabel(uint startLetter)
1167     {
1168       char s[10];
1169       if (startLetter>0x20 && startLetter<=0x7f) // printable ASCII character
1170       {
1171         s[0]=tolower((char)startLetter);
1172         s[1]=0;
1173       }
1174       else
1175       {
1176         const char hex[]="0123456789abcdef";
1177         int i=0;
1178         s[i++]='0';
1179         s[i++]='x';
1180         if (startLetter>(1<<24)) // 4 byte character
1181         {
1182           s[i++]=hex[(startLetter>>28)&0xf];
1183           s[i++]=hex[(startLetter>>24)&0xf];
1184         }
1185         if (startLetter>(1<<16)) // 3 byte character
1186         {
1187           s[i++]=hex[(startLetter>>20)&0xf];
1188           s[i++]=hex[(startLetter>>16)&0xf];
1189         }
1190         if (startLetter>(1<<8)) // 2 byte character
1191         {
1192           s[i++]=hex[(startLetter>>12)&0xf];
1193           s[i++]=hex[(startLetter>>8)&0xf];
1194         }
1195         // one byte character
1196         s[i++]=hex[(startLetter>>4)&0xf];
1197         s[i++]=hex[(startLetter>>0)&0xf];
1198         s[i++]=0;
1199       }
1200       return s;
1201     }
1202     static uint determineSortKey(TemplateStructIntf *s,const QCString &attribName)
1203     {
1204        TemplateVariant v = s->get(attribName);
1205        int index = getPrefixIndex(v.toString());
1206        return getUtf8CodeToUpper(v.toString(),index);
1207     }
1208
1209   public:
1210     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &args)
1211     {
1212       if (v.type()==TemplateVariant::List && args.type()==TemplateVariant::String)
1213       {
1214         //printf("FilterListSort::apply: v=%s args=%s\n",v.toString().data(),args.toString().data());
1215         TemplateListIntf::ConstIterator *it = v.toList()->createIterator();
1216
1217         TemplateVariant item;
1218         TemplateList *result = TemplateList::alloc();
1219
1220         // create list of items based on v using the data in args as a sort key
1221         SortList sortList;
1222         for (it->toFirst();(it->current(item));it->toNext())
1223         {
1224           TemplateStructIntf *s = item.toStruct();
1225           if (s)
1226           {
1227             uint sortKey = determineSortKey(s,args.toString());
1228             sortList.append(new ListElem(sortKey,item));
1229             //printf("sortKey=%s\n",sortKey.data());
1230           }
1231         }
1232         delete it;
1233
1234         // sort the list
1235         sortList.sort();
1236
1237         // create an index from the sorted list
1238         uint letter=0;
1239         QListIterator<ListElem> sit(sortList);
1240         ListElem *elem;
1241         TemplateStruct *indexNode = 0;
1242         TemplateList *indexList = 0;
1243         for (sit.toFirst();(elem=sit.current());++sit)
1244         {
1245           if (letter!=elem->key || indexNode==0)
1246           {
1247             // create new indexNode
1248             indexNode = TemplateStruct::alloc();
1249             indexList = TemplateList::alloc();
1250             indexNode->set("letter", keyToLetter(elem->key));
1251             indexNode->set("label",  keyToLabel(elem->key));
1252             indexNode->set("items",indexList);
1253             result->append(indexNode);
1254             letter=elem->key;
1255           }
1256           indexList->append(elem->value);
1257         }
1258         return result;
1259       }
1260       return v;
1261     }
1262 };
1263
1264 //--------------------------------------------------------------------
1265
1266 /** @brief The implementation of the "default" filter */
1267 class FilterStripPath
1268 {
1269   public:
1270     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1271     {
1272       if (!v.isValid() || v.type()!=TemplateVariant::String)
1273       {
1274         return v;
1275       }
1276       QCString result = v.toString();
1277       int i=result.findRev('/');
1278       if (i!=-1)
1279       {
1280         result=result.mid(i+1);
1281       }
1282       i=result.findRev('\\');
1283       if (i!=-1)
1284       {
1285         result=result.mid(i+1);
1286       }
1287       return result;
1288     }
1289 };
1290
1291 //--------------------------------------------------------------------
1292
1293 /** @brief The implementation of the "default" filter */
1294 class FilterNoWrap
1295 {
1296   public:
1297     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
1298     {
1299       if (!v.isValid() || v.type()!=TemplateVariant::String)
1300       {
1301         return v;
1302       }
1303       QCString s = v.toString();
1304       return substitute(s," ","&#160;");
1305     }
1306 };
1307
1308 //--------------------------------------------------------------------
1309
1310 /** @brief The implementation of the "divisibleby" filter */
1311 class FilterDivisibleBy
1312 {
1313   public:
1314     static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &n)
1315     {
1316       if (!v.isValid() || !n.isValid())
1317       {
1318         return TemplateVariant();
1319       }
1320       if (v.type()==TemplateVariant::Integer && n.type()==TemplateVariant::Integer)
1321       {
1322         return TemplateVariant((v.toInt()%n.toInt())==0);
1323       }
1324       else
1325       {
1326         return TemplateVariant();
1327       }
1328     }
1329 };
1330
1331
1332 //--------------------------------------------------------------------
1333
1334 /** @brief Factory singleton for registering and creating filters */
1335 class TemplateFilterFactory
1336 {
1337   public:
1338     typedef TemplateVariant (FilterFunction)(const TemplateVariant &v,const TemplateVariant &arg);
1339
1340     static TemplateFilterFactory *instance()
1341     {
1342       static TemplateFilterFactory *instance = 0;
1343       if (instance==0) instance = new TemplateFilterFactory;
1344       return instance;
1345     }
1346
1347     TemplateVariant apply(const QCString &name,const TemplateVariant &v,const TemplateVariant &arg, bool &ok)
1348     {
1349       FilterFunction *func = (FilterFunction*)m_registry.find(name);
1350       if (func)
1351       {
1352         ok=TRUE;
1353         return (*func)(v,arg);
1354       }
1355       else
1356       {
1357         ok=FALSE;
1358         return v;
1359       }
1360     }
1361
1362     void registerFilter(const QCString &name,FilterFunction *func)
1363     {
1364       m_registry.insert(name,(void*)func);
1365     }
1366
1367     /** @brief Helper class for registering a filter function */
1368     template<class T> class AutoRegister
1369     {
1370       public:
1371         AutoRegister<T>(const QCString &key)
1372         {
1373           TemplateFilterFactory::instance()->registerFilter(key,&T::apply);
1374         }
1375     };
1376
1377   private:
1378     QDict<void> m_registry;
1379 };
1380
1381 // register a handlers for each filter we support
1382 static TemplateFilterFactory::AutoRegister<FilterAdd>         fAdd("add");
1383 static TemplateFilterFactory::AutoRegister<FilterGet>         fGet("get");
1384 static TemplateFilterFactory::AutoRegister<FilterAppend>      fAppend("append");
1385 static TemplateFilterFactory::AutoRegister<FilterLength>      fLength("length");
1386 static TemplateFilterFactory::AutoRegister<FilterNoWrap>      fNoWrap("nowrap");
1387 static TemplateFilterFactory::AutoRegister<FilterFlatten>     fFlatten("flatten");
1388 static TemplateFilterFactory::AutoRegister<FilterDefault>     fDefault("default");
1389 static TemplateFilterFactory::AutoRegister<FilterPrepend>     fPrepend("prepend");
1390 static TemplateFilterFactory::AutoRegister<FilterGroupBy>     fGroupBy("groupBy");
1391 static TemplateFilterFactory::AutoRegister<FilterListSort>    fListSort("listsort");
1392 static TemplateFilterFactory::AutoRegister<FilterPaginate>    fPaginate("paginate");
1393 static TemplateFilterFactory::AutoRegister<FilterStripPath>   fStripPath("stripPath");
1394 static TemplateFilterFactory::AutoRegister<FilterAlphaIndex>  fAlphaIndex("alphaIndex");
1395 static TemplateFilterFactory::AutoRegister<FilterDivisibleBy> fDivisibleBy("divisibleby");
1396
1397 //--------------------------------------------------------------------
1398
1399 /** @brief Base class for all nodes in the abstract syntax tree of an
1400  *  expression.
1401  */
1402 class ExprAst
1403 {
1404   public:
1405     virtual ~ExprAst() {}
1406     virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(); }
1407 };
1408
1409 /** @brief Class representing a number in the AST */
1410 class ExprAstNumber : public ExprAst
1411 {
1412   public:
1413     ExprAstNumber(int num) : m_number(num)
1414     { TRACE(("ExprAstNumber(%d)\n",num)); }
1415     int number() const { return m_number; }
1416     virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_number); }
1417   private:
1418     int m_number;
1419 };
1420
1421 /** @brief Class representing a variable in the AST */
1422 class ExprAstVariable : public ExprAst
1423 {
1424   public:
1425     ExprAstVariable(const char *name) : m_name(name)
1426     { TRACE(("ExprAstVariable(%s)\n",name)); }
1427     const QCString &name() const { return m_name; }
1428     virtual TemplateVariant resolve(TemplateContext *c)
1429     {
1430       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1431       TemplateVariant v = c->get(m_name);
1432       if (!v.isValid())
1433       {
1434         ci->warn(ci->templateName(),ci->line(),"undefined variable '%s' in expression",m_name.data());
1435       }
1436       return v;
1437     }
1438   private:
1439     QCString m_name;
1440 };
1441
1442 class ExprAstFunctionVariable : public ExprAst
1443 {
1444   public:
1445     ExprAstFunctionVariable(ExprAst *var,const QList<ExprAst> &args)
1446       : m_var(var), m_args(args)
1447     { TRACE(("ExprAstFunctionVariable()\n"));
1448       m_args.setAutoDelete(TRUE);
1449     }
1450     virtual TemplateVariant resolve(TemplateContext *c)
1451     {
1452       QValueList<TemplateVariant> args;
1453       for (uint i=0;i<m_args.count();i++)
1454       {
1455         TemplateVariant v = m_args.at(i)->resolve(c);
1456         args.append(v);
1457       }
1458       TemplateVariant v = m_var->resolve(c);
1459       if (v.type()==TemplateVariant::Function)
1460       {
1461         v = v.call(args);
1462       }
1463       return v;
1464     }
1465   private:
1466     ExprAst *m_var;
1467     QList<ExprAst> m_args;
1468 };
1469
1470 /** @brief Class representing a filter in the AST */
1471 class ExprAstFilter : public ExprAst
1472 {
1473   public:
1474     ExprAstFilter(const char *name,ExprAst *arg) : m_name(name), m_arg(arg)
1475     { TRACE(("ExprAstFilter(%s)\n",name)); }
1476    ~ExprAstFilter() { delete m_arg; }
1477     const QCString &name() const { return m_name; }
1478     TemplateVariant apply(const TemplateVariant &v,TemplateContext *c)
1479     {
1480       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1481       TRACE(("Applying filter '%s' to '%s' (type=%d)\n",m_name.data(),v.toString().data(),v.type()));
1482       TemplateVariant arg;
1483       if (m_arg) arg = m_arg->resolve(c);
1484       bool ok;
1485       TemplateVariant result = TemplateFilterFactory::instance()->apply(m_name,v,arg,ok);
1486       if (!ok)
1487       {
1488         ci->warn(ci->templateName(),ci->line(),"unknown filter '%s'",m_name.data());
1489       }
1490       return result;
1491     }
1492   private:
1493     QCString m_name;
1494     ExprAst *m_arg;
1495 };
1496
1497 /** @brief Class representing a filter applied to an expression in the AST */
1498 class ExprAstFilterAppl : public ExprAst
1499 {
1500   public:
1501     ExprAstFilterAppl(ExprAst *expr,ExprAstFilter *filter)
1502       : m_expr(expr), m_filter(filter)
1503     { TRACE(("ExprAstFilterAppl\n")); }
1504    ~ExprAstFilterAppl() { delete m_expr; delete m_filter; }
1505     virtual TemplateVariant resolve(TemplateContext *c)
1506     {
1507       return m_filter->apply(m_expr->resolve(c),c);
1508     }
1509   private:
1510     ExprAst *m_expr;
1511     ExprAstFilter *m_filter;
1512 };
1513
1514 /** @brief Class representing a string literal in the AST */
1515 class ExprAstLiteral : public ExprAst
1516 {
1517   public:
1518     ExprAstLiteral(const char *lit) : m_literal(lit)
1519     { TRACE(("ExprAstLiteral(%s)\n",lit)); }
1520     const QCString &literal() const { return m_literal; }
1521     virtual TemplateVariant resolve(TemplateContext *) { return TemplateVariant(m_literal); }
1522   private:
1523     QCString m_literal;
1524 };
1525
1526 /** @brief Class representing a negation (not) operator in the AST */
1527 class ExprAstNegate : public ExprAst
1528 {
1529   public:
1530     ExprAstNegate(ExprAst *expr) : m_expr(expr)
1531     { TRACE(("ExprAstNegate\n")); }
1532    ~ExprAstNegate() { delete m_expr; }
1533     virtual TemplateVariant resolve(TemplateContext *c)
1534     { return TemplateVariant(!m_expr->resolve(c).toBool()); }
1535   private:
1536     ExprAst *m_expr;
1537 };
1538
1539 class ExprAstUnary : public ExprAst
1540 {
1541   public:
1542     ExprAstUnary(Operator::Type op,ExprAst *exp) : m_operator(op), m_exp(exp)
1543     { TRACE(("ExprAstUnary %s\n",Operator::toString(op))); }
1544    ~ExprAstUnary() { delete m_exp; }
1545     virtual TemplateVariant resolve(TemplateContext *c)
1546     {
1547       TemplateVariant exp = m_exp->resolve(c);
1548       switch (m_operator)
1549       {
1550         case Operator::Minus:
1551           return -exp.toInt();
1552         default:
1553           return TemplateVariant();
1554       }
1555     }
1556   private:
1557     Operator::Type m_operator;
1558     ExprAst *m_exp;
1559 };
1560
1561 /** @brief Class representing a binary operator in the AST */
1562 class ExprAstBinary : public ExprAst
1563 {
1564   public:
1565     ExprAstBinary(Operator::Type op,ExprAst *lhs,ExprAst *rhs)
1566       : m_operator(op), m_lhs(lhs), m_rhs(rhs)
1567     { TRACE(("ExprAstBinary %s\n",Operator::toString(op))); }
1568    ~ExprAstBinary() { delete m_lhs; delete m_rhs; }
1569     virtual TemplateVariant resolve(TemplateContext *c)
1570     {
1571       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
1572       TemplateVariant lhs = m_lhs->resolve(c);
1573       TemplateVariant rhs = m_rhs ? m_rhs->resolve(c) : TemplateVariant();
1574       switch(m_operator)
1575       {
1576         case Operator::Or:
1577           return TemplateVariant(lhs.toBool() || rhs.toBool());
1578         case Operator::And:
1579           return TemplateVariant(lhs.toBool() && rhs.toBool());
1580         case Operator::Equal:
1581           return TemplateVariant(lhs == rhs);
1582         case Operator::NotEqual:
1583           return TemplateVariant(!(lhs == rhs));
1584         case Operator::Less:
1585           if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1586           {
1587             return lhs.toString()<rhs.toString();
1588           }
1589           else
1590           {
1591             return lhs.toInt()<rhs.toInt();
1592           }
1593         case Operator::Greater:
1594           if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1595           {
1596             return !(lhs.toString()<rhs.toString());
1597           }
1598           else
1599           {
1600             return lhs.toInt()>rhs.toInt();
1601           }
1602         case Operator::LessEqual:
1603           if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1604           {
1605             return lhs.toString()==rhs.toString() || lhs.toString()<rhs.toString();
1606           }
1607           else
1608           {
1609             return lhs.toInt()<=rhs.toInt();
1610           }
1611         case Operator::GreaterEqual:
1612           if (lhs.type()==TemplateVariant::String && rhs.type()==TemplateVariant::String)
1613           {
1614             return lhs.toString()==rhs.toString() || !(lhs.toString()<rhs.toString());
1615           }
1616           else
1617           {
1618             return lhs.toInt()>=rhs.toInt();
1619           }
1620         case Operator::Plus:
1621           {
1622             return TemplateVariant(lhs.toInt() + rhs.toInt());
1623           }
1624         case Operator::Minus:
1625           {
1626             return TemplateVariant(lhs.toInt() - rhs.toInt());
1627           }
1628         case Operator::Multiply:
1629           {
1630             return TemplateVariant(lhs.toInt() * rhs.toInt());
1631           }
1632         case Operator::Divide:
1633           {
1634             int denom = rhs.toInt();
1635             if (denom!=0)
1636             {
1637               return TemplateVariant(lhs.toInt() / denom);
1638             }
1639             else // divide by zero
1640             {
1641               ci->warn(ci->templateName(),ci->line(),"division by zero while evaluating expression is undefined");
1642               return 0;
1643             }
1644           }
1645         case Operator::Modulo:
1646           {
1647             int denom = rhs.toInt();
1648             if (denom!=0)
1649             {
1650               return TemplateVariant(lhs.toInt() % denom);
1651             }
1652             else // module zero
1653             {
1654               ci->warn(ci->templateName(),ci->line(),"modulo zero while evaluating expression is undefined");
1655               return 0;
1656             }
1657           }
1658         default:
1659           return TemplateVariant();
1660       }
1661     }
1662   private:
1663     Operator::Type m_operator;
1664     ExprAst *m_lhs;
1665     ExprAst *m_rhs;
1666 };
1667
1668 //----------------------------------------------------------
1669
1670 /** @brief Base class of all nodes in a template's AST */
1671 class TemplateNode
1672 {
1673   public:
1674     TemplateNode(TemplateNode *parent) : m_parent(parent) {}
1675     virtual ~TemplateNode() {}
1676
1677     virtual void render(FTextStream &ts, TemplateContext *c) = 0;
1678
1679     TemplateNode *parent() { return m_parent; }
1680
1681   private:
1682     TemplateNode *m_parent;
1683 };
1684
1685 //----------------------------------------------------------
1686
1687 /** @brief Parser for templates */
1688 class TemplateParser
1689 {
1690   public:
1691     TemplateParser(const TemplateEngine *engine,
1692                    const QCString &templateName,QList<TemplateToken> &tokens);
1693     void parse(TemplateNode *parent,int line,const QStrList &stopAt,
1694                QList<TemplateNode> &nodes);
1695     bool hasNextToken() const;
1696     TemplateToken *takeNextToken();
1697     void removeNextToken();
1698     void prependToken(const TemplateToken *token);
1699     const TemplateToken *currentToken() const;
1700     QCString templateName() const { return m_templateName; }
1701     void warn(const char *fileName,int line,const char *fmt,...) const;
1702   private:
1703     const TemplateEngine *m_engine;
1704     QCString m_templateName;
1705     QList<TemplateToken> &m_tokens;
1706 };
1707
1708 //--------------------------------------------------------------------
1709
1710 /** @brief Recursive decent parser for Django style template expressions.
1711  */
1712 class ExpressionParser
1713 {
1714   public:
1715     ExpressionParser(const TemplateParser *parser,int line)
1716       : m_parser(parser), m_line(line), m_tokenStream(0)
1717     {
1718     }
1719     virtual ~ExpressionParser()
1720     {
1721     }
1722
1723     ExprAst *parse(const char *expr)
1724     {
1725       if (expr==0) return 0;
1726       m_tokenStream = expr;
1727       getNextToken();
1728       return parseExpression();
1729     }
1730
1731   private:
1732
1733     /** @brief Class representing a token within an expression. */
1734     class ExprToken
1735     {
1736       public:
1737         ExprToken() : type(Unknown), num(-1), op(Operator::Or)
1738         {
1739         }
1740         enum Type
1741         {
1742           Unknown, Operator, Number, Identifier, Literal
1743         };
1744
1745         Type type;
1746         int num;
1747         QCString id;
1748         Operator::Type op;
1749     };
1750
1751     ExprAst *parseExpression()
1752     {
1753       TRACE(("{parseExpression(%s)\n",m_tokenStream));
1754       ExprAst *result = parseOrExpression();
1755       TRACE(("}parseExpression(%s)\n",m_tokenStream));
1756       return result;
1757     }
1758
1759     ExprAst *parseOrExpression()
1760     {
1761       TRACE(("{parseOrExpression(%s)\n",m_tokenStream));
1762       ExprAst *lhs = parseAndExpression();
1763       if (lhs)
1764       {
1765         while (m_curToken.type==ExprToken::Operator &&
1766             m_curToken.op==Operator::Or)
1767         {
1768           getNextToken();
1769           ExprAst *rhs = parseAndExpression();
1770           lhs = new ExprAstBinary(Operator::Or,lhs,rhs);
1771         }
1772       }
1773       TRACE(("}parseOrExpression(%s)\n",m_tokenStream));
1774       return lhs;
1775     }
1776
1777     ExprAst *parseAndExpression()
1778     {
1779       TRACE(("{parseAndExpression(%s)\n",m_tokenStream));
1780       ExprAst *lhs = parseNotExpression();
1781       if (lhs)
1782       {
1783         while (m_curToken.type==ExprToken::Operator &&
1784                m_curToken.op==Operator::And)
1785         {
1786           getNextToken();
1787           ExprAst *rhs = parseNotExpression();
1788           lhs = new ExprAstBinary(Operator::And,lhs,rhs);
1789         }
1790       }
1791       TRACE(("}parseAndExpression(%s)\n",m_tokenStream));
1792       return lhs;
1793     }
1794
1795     ExprAst *parseNotExpression()
1796     {
1797       TRACE(("{parseNotExpression(%s)\n",m_tokenStream));
1798       ExprAst *result=0;
1799       if (m_curToken.type==ExprToken::Operator &&
1800           m_curToken.op==Operator::Not)
1801       {
1802         getNextToken();
1803         ExprAst *expr = parseCompareExpression();
1804         if (expr==0)
1805         {
1806           warn(m_parser->templateName(),m_line,"argument missing for not operator");
1807           return 0;
1808         }
1809         result = new ExprAstNegate(expr);
1810       }
1811       else
1812       {
1813         result = parseCompareExpression();
1814       }
1815       TRACE(("}parseNotExpression(%s)\n",m_tokenStream));
1816       return result;
1817     }
1818
1819     ExprAst *parseCompareExpression()
1820     {
1821       TRACE(("{parseCompareExpression(%s)\n",m_tokenStream));
1822       ExprAst *lhs = parseAdditiveExpression();
1823       if (lhs)
1824       {
1825         Operator::Type op = m_curToken.op;
1826         if (m_curToken.type==ExprToken::Operator &&
1827             (op==Operator::Less      ||
1828              op==Operator::Greater   ||
1829              op==Operator::Equal     ||
1830              op==Operator::NotEqual  ||
1831              op==Operator::LessEqual ||
1832              op==Operator::GreaterEqual
1833             )
1834            )
1835         {
1836           getNextToken();
1837           ExprAst *rhs = parseNotExpression();
1838           lhs = new ExprAstBinary(op,lhs,rhs);
1839         }
1840       }
1841       TRACE(("}parseCompareExpression(%s)\n",m_tokenStream));
1842       return lhs;
1843     }
1844
1845     ExprAst *parseAdditiveExpression()
1846     {
1847       TRACE(("{parseAdditiveExpression(%s)\n",m_tokenStream));
1848       ExprAst *lhs = parseMultiplicativeExpression();
1849       if (lhs)
1850       {
1851         while (m_curToken.type==ExprToken::Operator &&
1852                (m_curToken.op==Operator::Plus || m_curToken.op==Operator::Minus))
1853         {
1854           Operator::Type op = m_curToken.op;
1855           getNextToken();
1856           ExprAst *rhs = parseMultiplicativeExpression();
1857           lhs = new ExprAstBinary(op,lhs,rhs);
1858         }
1859       }
1860       TRACE(("}parseAdditiveExpression(%s)\n",m_tokenStream));
1861       return lhs;
1862     }
1863
1864     ExprAst *parseMultiplicativeExpression()
1865     {
1866       TRACE(("{parseMultiplicativeExpression(%s)\n",m_tokenStream));
1867       ExprAst *lhs = parseUnaryExpression();
1868       if (lhs)
1869       {
1870         while (m_curToken.type==ExprToken::Operator &&
1871                (m_curToken.op==Operator::Multiply || m_curToken.op==Operator::Divide || m_curToken.op==Operator::Modulo))
1872         {
1873           Operator::Type op = m_curToken.op;
1874           getNextToken();
1875           ExprAst *rhs = parseUnaryExpression();
1876           lhs = new ExprAstBinary(op,lhs,rhs);
1877         }
1878       }
1879       TRACE(("}parseMultiplicativeExpression(%s)\n",m_tokenStream));
1880       return lhs;
1881     }
1882
1883     ExprAst *parseUnaryExpression()
1884     {
1885       TRACE(("{parseUnaryExpression(%s)\n",m_tokenStream));
1886       ExprAst *result=0;
1887       if (m_curToken.type==ExprToken::Operator)
1888       {
1889         if (m_curToken.op==Operator::Plus)
1890         {
1891           getNextToken();
1892           result = parsePrimaryExpression();
1893         }
1894         else if (m_curToken.op==Operator::Minus)
1895         {
1896           getNextToken();
1897           ExprAst *rhs = parsePrimaryExpression();
1898           result = new ExprAstUnary(m_curToken.op,rhs);
1899         }
1900         else
1901         {
1902           result = parsePrimaryExpression();
1903         }
1904       }
1905       else
1906       {
1907         result = parsePrimaryExpression();
1908       }
1909       TRACE(("}parseUnaryExpression(%s)\n",m_tokenStream));
1910       return result;
1911     }
1912
1913     ExprAst *parsePrimaryExpression()
1914     {
1915       TRACE(("{parsePrimary(%s)\n",m_tokenStream));
1916       ExprAst *result=0;
1917       switch (m_curToken.type)
1918       {
1919         case ExprToken::Number:
1920           result = parseNumber();
1921           break;
1922         case ExprToken::Identifier:
1923           result = parseFilteredVariable();
1924           break;
1925         case ExprToken::Literal:
1926           result = parseLiteral();
1927           break;
1928         case ExprToken::Operator:
1929           if (m_curToken.op==Operator::LeftParen)
1930           {
1931             getNextToken(); // skip over opening bracket
1932             result = parseExpression();
1933             if (m_curToken.type!=ExprToken::Operator ||
1934                 m_curToken.op!=Operator::RightParen)
1935             {
1936               warn(m_parser->templateName(),m_line,"missing closing parenthesis");
1937             }
1938             else
1939             {
1940               getNextToken(); // skip over closing bracket
1941             }
1942           }
1943           else
1944           {
1945             warn(m_parser->templateName(),m_line,"unexpected operator '%s' in expression",
1946                 Operator::toString(m_curToken.op));
1947           }
1948           break;
1949         default:
1950           warn(m_parser->templateName(),m_line,"unexpected token in expression");
1951       }
1952       TRACE(("}parsePrimary(%s)\n",m_tokenStream));
1953       return result;
1954     }
1955
1956     ExprAst *parseNumber()
1957     {
1958       TRACE(("{parseNumber(%d)\n",m_curToken.num));
1959       ExprAst *num = new ExprAstNumber(m_curToken.num);
1960       getNextToken();
1961       TRACE(("}parseNumber()\n"));
1962       return num;
1963     }
1964
1965     ExprAst *parseIdentifier()
1966     {
1967       TRACE(("{parseIdentifier(%s)\n",m_curToken.id.data()));
1968       ExprAst *id = new ExprAstVariable(m_curToken.id);
1969       getNextToken();
1970       TRACE(("}parseIdentifier()\n"));
1971       return id;
1972     }
1973
1974     ExprAst *parseLiteral()
1975     {
1976       TRACE(("{parseLiteral(%s)\n",m_curToken.id.data()));
1977       ExprAst *lit = new ExprAstLiteral(m_curToken.id);
1978       getNextToken();
1979       TRACE(("}parseLiteral()\n"));
1980       return lit;
1981     }
1982
1983     ExprAst *parseIdentifierOptionalArgs()
1984     {
1985       TRACE(("{parseIdentifierOptionalArgs(%s)\n",m_curToken.id.data()));
1986       ExprAst *expr = parseIdentifier();
1987       if (expr)
1988       {
1989         if (m_curToken.type==ExprToken::Operator &&
1990             m_curToken.op==Operator::Colon)
1991         {
1992           getNextToken();
1993           ExprAst *argExpr = parsePrimaryExpression();
1994           QList<ExprAst> args;
1995           args.append(argExpr);
1996           while (m_curToken.type==ExprToken::Operator &&
1997                  m_curToken.op==Operator::Comma)
1998           {
1999             getNextToken();
2000             argExpr = parsePrimaryExpression();
2001             args.append(argExpr);
2002           }
2003           expr = new ExprAstFunctionVariable(expr,args);
2004         }
2005       }
2006       TRACE(("}parseIdentifierOptionalArgs()\n"));
2007       return expr;
2008     }
2009
2010     ExprAst *parseFilteredVariable()
2011     {
2012       TRACE(("{parseFilteredVariable()\n"));
2013       ExprAst *expr = parseIdentifierOptionalArgs();
2014       if (expr)
2015       {
2016         while (m_curToken.type==ExprToken::Operator &&
2017                m_curToken.op==Operator::Filter)
2018         {
2019           getNextToken();
2020           ExprAstFilter *filter = parseFilter();
2021           if (!filter) break;
2022           expr = new ExprAstFilterAppl(expr,filter);
2023         }
2024       }
2025       TRACE(("}parseFilteredVariable()\n"));
2026       return expr;
2027     }
2028
2029     ExprAstFilter *parseFilter()
2030     {
2031       TRACE(("{parseFilter(%s)\n",m_curToken.id.data()));
2032       QCString filterName = m_curToken.id;
2033       getNextToken();
2034       ExprAst *argExpr=0;
2035       if (m_curToken.type==ExprToken::Operator &&
2036           m_curToken.op==Operator::Colon)
2037       {
2038         getNextToken();
2039         argExpr = parsePrimaryExpression();
2040       }
2041       ExprAstFilter *filter = new ExprAstFilter(filterName,argExpr);
2042       TRACE(("}parseFilter()\n"));
2043       return filter;
2044     }
2045
2046
2047     bool getNextToken()
2048     {
2049       const char *p = m_tokenStream;
2050       char s[2];
2051       s[1]=0;
2052       if (p==0 || *p=='\0') return FALSE;
2053       while (*p==' ') p++; // skip over spaces
2054       char c=*p;
2055       const char *q = p;
2056       switch (c)
2057       {
2058         case '=':
2059           if (c=='=' && *(p+1)=='=') // equal
2060           {
2061             m_curToken.op = Operator::Equal;
2062             p+=2;
2063           }
2064           break;
2065         case '!':
2066           if (c=='!' && *(p+1)=='=') // not equal
2067           {
2068             m_curToken.op = Operator::NotEqual;
2069             p+=2;
2070           }
2071           break;
2072         case '<':
2073           if (c=='<' && *(p+1)=='=') // less or equal
2074           {
2075             m_curToken.op = Operator::LessEqual;
2076             p+=2;
2077           }
2078           else // less
2079           {
2080             m_curToken.op = Operator::Less;
2081             p++;
2082           }
2083           break;
2084         case '>':
2085           if (c=='>' && *(p+1)=='=') // greater or equal
2086           {
2087             m_curToken.op = Operator::GreaterEqual;
2088             p+=2;
2089           }
2090           else // greater
2091           {
2092             m_curToken.op = Operator::Greater;
2093             p++;
2094           }
2095           break;
2096         case '(':
2097           m_curToken.op = Operator::LeftParen;
2098           p++;
2099           break;
2100         case ')':
2101           m_curToken.op = Operator::RightParen;
2102           p++;
2103           break;
2104         case '|':
2105           m_curToken.op = Operator::Filter;
2106           p++;
2107           break;
2108         case '+':
2109           m_curToken.op = Operator::Plus;
2110           p++;
2111           break;
2112         case '-':
2113           m_curToken.op = Operator::Minus;
2114           p++;
2115           break;
2116         case '*':
2117           m_curToken.op = Operator::Multiply;
2118           p++;
2119           break;
2120         case '/':
2121           m_curToken.op = Operator::Divide;
2122           p++;
2123           break;
2124         case '%':
2125           m_curToken.op = Operator::Modulo;
2126           p++;
2127           break;
2128         case ':':
2129           m_curToken.op = Operator::Colon;
2130           p++;
2131           break;
2132         case ',':
2133           m_curToken.op = Operator::Comma;
2134           p++;
2135           break;
2136         case 'n':
2137           if (strncmp(p,"not ",4)==0)
2138           {
2139             m_curToken.op = Operator::Not;
2140             p+=4;
2141           }
2142           break;
2143         case 'a':
2144           if (strncmp(p,"and ",4)==0)
2145           {
2146             m_curToken.op = Operator::And;
2147             p+=4;
2148           }
2149           break;
2150         case 'o':
2151           if (strncmp(p,"or ",3)==0)
2152           {
2153             m_curToken.op = Operator::Or;
2154             p+=3;
2155           }
2156           break;
2157         default:
2158           break;
2159       }
2160       if (p!=q) // found an operator
2161       {
2162         m_curToken.type = ExprToken::Operator;
2163       }
2164       else // no token found yet
2165       {
2166         if (c>='0' && c<='9') // number?
2167         {
2168           m_curToken.type = ExprToken::Number;
2169           const char *np = p;
2170           m_curToken.num = 0;
2171           while (*np>='0' && *np<='9')
2172           {
2173             m_curToken.num*=10;
2174             m_curToken.num+=*np-'0';
2175             np++;
2176           }
2177           p=np;
2178         }
2179         else if (c=='_' || (c>='a' && c<='z') || (c>='A' && c<='Z')) // identifier?
2180         {
2181           m_curToken.type = ExprToken::Identifier;
2182           s[0]=c;
2183           m_curToken.id = s;
2184           p++;
2185           while ((c=*p) &&
2186               (c=='_' || c=='.' ||
2187                (c>='a' && c<='z') ||
2188                (c>='A' && c<='Z') ||
2189                (c>='0' && c<='9'))
2190               )
2191           {
2192             s[0]=c;
2193             m_curToken.id+=s;
2194             p++;
2195           }
2196           if (m_curToken.id=="True") // treat true literal as numerical 1
2197           {
2198             m_curToken.type = ExprToken::Number;
2199             m_curToken.num = 1;
2200           }
2201           else if (m_curToken.id=="False") // treat false literal as numerical 0
2202           {
2203             m_curToken.type = ExprToken::Number;
2204             m_curToken.num = 0;
2205           }
2206         }
2207         else if (c=='"' || c=='\'') // string literal
2208         {
2209           m_curToken.type = ExprToken::Literal;
2210           m_curToken.id.resize(0);
2211           p++;
2212           char tokenChar = c;
2213           char cp=0;
2214           while ((c=*p) && (c!=tokenChar || (c==tokenChar && cp=='\\')))
2215           {
2216             s[0]=c;
2217             if (c!='\\' || cp=='\\') // don't add escapes
2218             {
2219               m_curToken.id+=s;
2220             }
2221             cp=c;
2222             p++;
2223           }
2224           if (*p==tokenChar) p++;
2225         }
2226       }
2227       if (p==q) // still no valid token found -> error
2228       {
2229         m_curToken.type = ExprToken::Unknown;
2230         char s[2];
2231         s[0]=c;
2232         s[1]=0;
2233         warn(m_parser->templateName(),m_line,"Found unknown token %s while parsing %s",s,m_tokenStream);
2234         m_curToken.id = s;
2235         p++;
2236       }
2237       //TRACE(("token type=%d op=%d num=%d id=%s\n",
2238       //    m_curToken.type,m_curToken.op,m_curToken.num,m_curToken.id.data()));
2239
2240       m_tokenStream = p;
2241       return TRUE;
2242     }
2243
2244     const TemplateParser *m_parser;
2245     ExprToken m_curToken;
2246     int m_line;
2247     const char *m_tokenStream;
2248 };
2249
2250 //----------------------------------------------------------
2251
2252 /** @brief Class representing a lexical token in a template */
2253 class TemplateToken
2254 {
2255   public:
2256     enum Type { Text, Variable, Block };
2257     TemplateToken(Type t,const char *d,int l) : type(t), data(d), line(l) {}
2258     Type type;
2259     QCString data;
2260     int line;
2261 };
2262
2263 //----------------------------------------------------------
2264
2265 /** @brief Class representing a list of AST nodes in a template */
2266 class TemplateNodeList : public QList<TemplateNode>
2267 {
2268   public:
2269     TemplateNodeList()
2270     {
2271       setAutoDelete(TRUE);
2272     }
2273     void render(FTextStream &ts,TemplateContext *c)
2274     {
2275       TRACE(("{TemplateNodeList::render\n"));
2276       QListIterator<TemplateNode> it(*this);
2277       TemplateNode *tn=0;
2278       for (it.toFirst();(tn=it.current());++it)
2279       {
2280         tn->render(ts,c);
2281       }
2282       TRACE(("}TemplateNodeList::render\n"));
2283     }
2284 };
2285
2286 //----------------------------------------------------------
2287
2288 /** @brief Internal class representing the implementation of a template */
2289 class TemplateImpl : public TemplateNode, public Template
2290 {
2291   public:
2292     TemplateImpl(TemplateEngine *e,const QCString &name,const QCString &data);
2293     void render(FTextStream &ts, TemplateContext *c);
2294
2295     TemplateEngine *engine() const { return m_engine; }
2296     TemplateBlockContext *blockContext() { return &m_blockContext; }
2297
2298   private:
2299     TemplateEngine *m_engine;
2300     QCString m_name;
2301     TemplateNodeList m_nodes;
2302     TemplateBlockContext m_blockContext;
2303 };
2304
2305 //----------------------------------------------------------
2306
2307
2308 TemplateContextImpl::TemplateContextImpl(const TemplateEngine *e)
2309   : m_engine(e), m_templateName("<unknown>"), m_line(1), m_activeEscapeIntf(0),
2310     m_spacelessIntf(0), m_spacelessEnabled(FALSE), m_indices(TemplateStruct::alloc())
2311 {
2312   m_indexStacks.setAutoDelete(TRUE);
2313   m_contextStack.setAutoDelete(TRUE);
2314   m_escapeIntfDict.setAutoDelete(TRUE);
2315   push();
2316   set("index",m_indices.get());
2317 }
2318
2319 TemplateContextImpl::~TemplateContextImpl()
2320 {
2321   pop();
2322 }
2323
2324 void TemplateContextImpl::set(const char *name,const TemplateVariant &v)
2325 {
2326   TemplateVariant *pv = m_contextStack.getFirst()->find(name);
2327   if (pv)
2328   {
2329     m_contextStack.getFirst()->remove(name);
2330   }
2331   m_contextStack.getFirst()->insert(name,new TemplateVariant(v));
2332 }
2333
2334 TemplateVariant TemplateContextImpl::get(const QCString &name) const
2335 {
2336   int i=name.find('.');
2337   if (i==-1) // simple name
2338   {
2339     return getPrimary(name);
2340   }
2341   else // obj.prop
2342   {
2343     TemplateVariant v;
2344     QCString objName = name.left(i);
2345     v = getPrimary(objName);
2346     QCString propName = name.mid(i+1);
2347     while (!propName.isEmpty())
2348     {
2349       //printf("getPrimary(%s) type=%d:%s\n",objName.data(),v.type(),v.toString().data());
2350       if (v.type()==TemplateVariant::Struct)
2351       {
2352         i = propName.find(".");
2353         int l = i==-1 ? propName.length() : i;
2354         v = v.toStruct()->get(propName.left(l));
2355         if (!v.isValid())
2356         {
2357           warn(m_templateName,m_line,"requesting non-existing property '%s' for object '%s'",propName.left(l).data(),objName.data());
2358         }
2359         if (i!=-1)
2360         {
2361           objName = propName.left(i);
2362           propName = propName.mid(i+1);
2363         }
2364         else
2365         {
2366           propName.resize(0);
2367         }
2368       }
2369       else if (v.type()==TemplateVariant::List)
2370       {
2371         i = propName.find(".");
2372         int l = i==-1 ? propName.length() : i;
2373         bool b;
2374         int index = propName.left(l).toInt(&b);
2375         if (b)
2376         {
2377           v = v.toList()->at(index);
2378         }
2379         else
2380         {
2381           warn(m_templateName,m_line,"list index '%s' is not valid",propName.data());
2382           break;
2383         }
2384         if (i!=-1)
2385         {
2386           propName = propName.mid(i+1);
2387         }
2388         else
2389         {
2390           propName.resize(0);
2391         }
2392       }
2393       else
2394       {
2395         warn(m_templateName,m_line,"using . on an object '%s' is not an struct or list",objName.data());
2396         return TemplateVariant();
2397       }
2398     } while (i!=-1);
2399     return v;
2400   }
2401 }
2402
2403 const TemplateVariant *TemplateContextImpl::getRef(const QCString &name) const
2404 {
2405   QListIterator< QDict<TemplateVariant> > it(m_contextStack);
2406   QDict<TemplateVariant> *dict;
2407   for (it.toFirst();(dict=it.current());++it)
2408   {
2409     TemplateVariant *v = dict->find(name);
2410     if (v) return v;
2411   }
2412   return 0; // not found
2413 }
2414
2415 TemplateVariant TemplateContextImpl::getPrimary(const QCString &name) const
2416 {
2417   const TemplateVariant *v = getRef(name);
2418   return v ? *v : TemplateVariant();
2419 }
2420
2421 void TemplateContextImpl::push()
2422 {
2423   QDict<TemplateVariant> *dict = new QDict<TemplateVariant>;
2424   dict->setAutoDelete(TRUE);
2425   m_contextStack.prepend(dict);
2426 }
2427
2428 void TemplateContextImpl::pop()
2429 {
2430   if (!m_contextStack.removeFirst())
2431   {
2432     warn(m_templateName,m_line,"pop() called on empty context stack!\n");
2433   }
2434 }
2435
2436 TemplateBlockContext *TemplateContextImpl::blockContext()
2437 {
2438   return &m_blockContext;
2439 }
2440
2441 void TemplateContextImpl::warn(const char *fileName,int line,const char *fmt,...) const
2442 {
2443   va_list args;
2444   va_start(args,fmt);
2445   va_warn(fileName,line,fmt,args);
2446   va_end(args);
2447   m_engine->printIncludeContext(fileName,line);
2448 }
2449
2450 void TemplateContextImpl::openSubIndex(const QCString &indexName)
2451 {
2452   //printf("TemplateContextImpl::openSubIndex(%s)\n",indexName.data());
2453   QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2454   if (!stack || stack->isEmpty() || stack->top()->type()==TemplateVariant::List) // error: no stack yet or no entry
2455   {
2456     warn(m_templateName,m_line,"opensubindex for index %s without preceding indexentry",indexName.data());
2457     return;
2458   }
2459   // get the parent entry to add the list to
2460   TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2461   if (entry)
2462   {
2463     // add new list to the stack
2464     TemplateList *list = TemplateList::alloc();
2465     stack->push(new TemplateVariant(list));
2466     entry->set("children",list);
2467     entry->set("is_leaf_node",false);
2468   }
2469 }
2470
2471 void TemplateContextImpl::closeSubIndex(const QCString &indexName)
2472 {
2473   //printf("TemplateContextImpl::closeSubIndex(%s)\n",indexName.data());
2474   QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2475   if (!stack || stack->count()<3)
2476   {
2477     warn(m_templateName,m_line,"closesubindex for index %s without matching open",indexName.data());
2478   }
2479   else // stack->count()>=2
2480   {
2481     if (stack->top()->type()==TemplateVariant::Struct)
2482     {
2483       delete stack->pop(); // pop struct
2484       delete stack->pop(); // pop list
2485     }
2486     else // empty list! correct "is_left_node" attribute of the parent entry
2487     {
2488       delete stack->pop(); // pop list
2489       TemplateStruct *entry = dynamic_cast<TemplateStruct*>(stack->top()->toStruct());
2490       if (entry)
2491       {
2492         entry->set("is_leaf_node",true);
2493       }
2494     }
2495   }
2496 }
2497
2498 static void getPathListFunc(TemplateStructIntf *entry,TemplateList *list)
2499 {
2500   TemplateVariant parent = entry->get("parent");
2501   if (parent.type()==TemplateVariant::Struct)
2502   {
2503     getPathListFunc(parent.toStruct(),list);
2504   }
2505   list->append(entry);
2506 }
2507
2508 static TemplateVariant getPathFunc(const void *ctx, const QValueList<TemplateVariant> &)
2509 {
2510   TemplateStruct *entry = (TemplateStruct*)ctx;
2511   TemplateList *result = TemplateList::alloc();
2512   getPathListFunc(entry,result);
2513   return result;
2514 }
2515
2516 void TemplateContextImpl::addIndexEntry(const QCString &indexName,const QValueList<TemplateKeyValue> &arguments)
2517 {
2518   QValueListConstIterator<TemplateKeyValue> it = arguments.begin();
2519   //printf("TemplateContextImpl::addIndexEntry(%s)\n",indexName.data());
2520   //while (it!=arguments.end())
2521   //{
2522   //  printf("  key=%s value=%s\n",(*it).key.data(),(*it).value.toString().data());
2523   //  ++it;
2524   //}
2525   TemplateVariant parent(FALSE);
2526   QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
2527   if (!stack) // no stack yet, create it!
2528   {
2529     stack = new QStack<TemplateVariant>;
2530     stack->setAutoDelete(TRUE);
2531     m_indexStacks.insert(indexName,stack);
2532   }
2533   TemplateList *list  = 0;
2534   if (stack->isEmpty()) // first item, create empty list and add it to the index
2535   {
2536     list = TemplateList::alloc();
2537     stack->push(new TemplateVariant(list));
2538     m_indices->set(indexName,list); // make list available under index
2539   }
2540   else // stack not empty
2541   {
2542     if (stack->top()->type()==TemplateVariant::Struct) // already an entry in the list
2543     {
2544       // remove current entry from the stack
2545       delete stack->pop();
2546     }
2547     else // first entry after opensubindex
2548     {
2549       ASSERT(stack->top()->type()==TemplateVariant::List);
2550     }
2551     if (stack->count()>1)
2552     {
2553       TemplateVariant *tmp = stack->pop();
2554       parent = *stack->top();
2555       stack->push(tmp);
2556       ASSERT(parent.type()==TemplateVariant::Struct);
2557     }
2558     // get list to add new item
2559     list = dynamic_cast<TemplateList*>(stack->top()->toList());
2560   }
2561   TemplateStruct *entry = TemplateStruct::alloc();
2562   // add user specified fields to the entry
2563   for (it=arguments.begin();it!=arguments.end();++it)
2564   {
2565     entry->set((*it).key,(*it).value);
2566   }
2567   if (list->count()>0)
2568   {
2569     TemplateStruct *lastEntry = dynamic_cast<TemplateStruct*>(list->at(list->count()-1).toStruct());
2570     lastEntry->set("last",false);
2571   }
2572   entry->set("is_leaf_node",true);
2573   entry->set("first",list->count()==0);
2574   entry->set("index",list->count());
2575   entry->set("parent",parent);
2576   entry->set("path",TemplateVariant::Delegate::fromFunction(entry,getPathFunc));
2577   entry->set("last",true);
2578   stack->push(new TemplateVariant(entry));
2579   list->append(entry);
2580 }
2581
2582 //----------------------------------------------------------
2583
2584 /** @brief Class representing a piece of plain text in a template */
2585 class TemplateNodeText : public TemplateNode
2586 {
2587   public:
2588     TemplateNodeText(TemplateParser *,TemplateNode *parent,int,const QCString &data)
2589       : TemplateNode(parent), m_data(data)
2590     {
2591       TRACE(("TemplateNodeText('%s')\n",replace(data,'\n',' ').data()));
2592     }
2593
2594     void render(FTextStream &ts, TemplateContext *c)
2595     {
2596       //printf("TemplateNodeText::render(%s)\n",m_data.data());
2597       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2598       if (ci->spacelessEnabled())
2599       {
2600         ts << ci->spacelessIntf()->remove(m_data);
2601       }
2602       else
2603       {
2604         ts << m_data;
2605       }
2606     }
2607   private:
2608     QCString m_data;
2609 };
2610
2611 //----------------------------------------------------------
2612
2613 /** @brief Class representing a variable in a template */
2614 class TemplateNodeVariable : public TemplateNode
2615 {
2616   public:
2617     TemplateNodeVariable(TemplateParser *parser,TemplateNode *parent,int line,const QCString &var)
2618       : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line)
2619     {
2620       TRACE(("TemplateNodeVariable(%s)\n",var.data()));
2621       ExpressionParser expParser(parser,line);
2622       m_var = expParser.parse(var);
2623       if (m_var==0)
2624       {
2625         parser->warn(m_templateName,line,"invalid expression '%s' for variable",var.data());
2626       }
2627     }
2628     ~TemplateNodeVariable()
2629     {
2630       delete m_var;
2631     }
2632
2633     void render(FTextStream &ts, TemplateContext *c)
2634     {
2635       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2636       ci->setLocation(m_templateName,m_line);
2637       if (m_var)
2638       {
2639         TemplateVariant v = m_var->resolve(c);
2640         if (v.type()==TemplateVariant::Function)
2641         {
2642           v = v.call(QValueList<TemplateVariant>());
2643         }
2644         //printf("TemplateNodeVariable::render(%s) raw=%d\n",value.data(),v.raw());
2645         if (ci->escapeIntf() && !v.raw())
2646         {
2647           ts << ci->escapeIntf()->escape(v.toString());
2648         }
2649         else
2650         {
2651           ts << v.toString();
2652         }
2653       }
2654     }
2655
2656   private:
2657     QCString m_templateName;
2658     int m_line;
2659     ExprAst *m_var;
2660     QList<ExprAst> m_args;
2661 };
2662
2663 //----------------------------------------------------------
2664
2665 /** @brief Helper class for creating template AST tag nodes and returning
2666   * the template for a given node.
2667  */
2668 template<class T> class TemplateNodeCreator : public TemplateNode
2669 {
2670   public:
2671     TemplateNodeCreator(TemplateParser *parser,TemplateNode *parent,int line)
2672       : TemplateNode(parent), m_templateName(parser->templateName()), m_line(line) {}
2673     static TemplateNode *createInstance(TemplateParser *parser,
2674                                         TemplateNode *parent,
2675                                         int line,
2676                                         const QCString &data)
2677     {
2678       return new T(parser,parent,line,data);
2679     }
2680     TemplateImpl *getTemplate()
2681     {
2682       TemplateNode *root = this;
2683       while (root && root->parent())
2684       {
2685         root = root->parent();
2686       }
2687       return dynamic_cast<TemplateImpl*>(root);
2688     }
2689   protected:
2690     QCString m_templateName;
2691     int m_line;
2692 };
2693
2694 //----------------------------------------------------------
2695
2696 /** @brief Class representing an 'if' tag in a template */
2697 class TemplateNodeIf : public TemplateNodeCreator<TemplateNodeIf>
2698 {
2699   public:
2700     TemplateNodeIf(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data) :
2701       TemplateNodeCreator<TemplateNodeIf>(parser,parent,line)
2702     {
2703       m_ifGuardedNodes.setAutoDelete(TRUE);
2704       TRACE(("{TemplateNodeIf(%s)\n",data.data()));
2705       if (data.isEmpty())
2706       {
2707         parser->warn(m_templateName,line,"missing argument for if tag");
2708       }
2709       QStrList stopAt;
2710       stopAt.append("endif");
2711       stopAt.append("elif");
2712       stopAt.append("else");
2713
2714       // if 'nodes'
2715       GuardedNodes *guardedNodes = new GuardedNodes;
2716       ExpressionParser ex(parser,line);
2717       guardedNodes->line = line;
2718       guardedNodes->guardAst = ex.parse(data);
2719       parser->parse(this,line,stopAt,guardedNodes->trueNodes);
2720       m_ifGuardedNodes.append(guardedNodes);
2721       TemplateToken *tok = parser->takeNextToken();
2722
2723       // elif 'nodes'
2724       while (tok && tok->data.left(5)=="elif ")
2725       {
2726         ExpressionParser ex(parser,line);
2727         guardedNodes = new GuardedNodes;
2728         guardedNodes->line = tok->line;
2729         guardedNodes->guardAst = ex.parse(tok->data.mid(5));
2730         parser->parse(this,tok->line,stopAt,guardedNodes->trueNodes);
2731         m_ifGuardedNodes.append(guardedNodes);
2732         // proceed to the next token
2733         delete tok;
2734         tok = parser->takeNextToken();
2735       }
2736
2737       // else 'nodes'
2738       if (tok && tok->data=="else")
2739       {
2740         stopAt.removeLast(); // remove "else"
2741         stopAt.removeLast(); // remove "elif"
2742         parser->parse(this,line,stopAt,m_falseNodes);
2743         parser->removeNextToken(); // skip over endif
2744       }
2745       delete tok;
2746       TRACE(("}TemplateNodeIf(%s)\n",data.data()));
2747     }
2748     ~TemplateNodeIf()
2749     {
2750     }
2751
2752     void render(FTextStream &ts, TemplateContext *c)
2753     {
2754       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2755       ci->setLocation(m_templateName,m_line);
2756       //printf("TemplateNodeIf::render #trueNodes=%d #falseNodes=%d\n",m_trueNodes.count(),m_falseNodes.count());
2757       bool processed=FALSE;
2758       QListIterator<GuardedNodes> li(m_ifGuardedNodes);
2759       GuardedNodes *nodes;
2760       for (li.toFirst();(nodes=li.current()) && !processed;++li)
2761       {
2762         if (nodes->guardAst)
2763         {
2764           TemplateVariant guardValue = nodes->guardAst->resolve(c);
2765           if (guardValue.toBool()) // render nodes for the first guard that evaluated to 'true'
2766           {
2767             nodes->trueNodes.render(ts,c);
2768            processed=TRUE;
2769           }
2770         }
2771         else
2772         {
2773           ci->warn(m_templateName,nodes->line,"invalid expression for if/elif");
2774         }
2775       }
2776       if (!processed)
2777       {
2778         // all guards are false, render 'else' nodes
2779         m_falseNodes.render(ts,c);
2780       }
2781     }
2782   private:
2783     struct GuardedNodes
2784     {
2785       GuardedNodes() : guardAst(0) {}
2786      ~GuardedNodes() { delete guardAst; }
2787       int line;
2788       ExprAst *guardAst;
2789       TemplateNodeList trueNodes;
2790     };
2791     QList<GuardedNodes> m_ifGuardedNodes;
2792     TemplateNodeList m_falseNodes;
2793 };
2794
2795 //----------------------------------------------------------
2796 /** @brief Class representing a 'for' tag in a template */
2797 class TemplateNodeRepeat : public TemplateNodeCreator<TemplateNodeRepeat>
2798 {
2799   public:
2800     TemplateNodeRepeat(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2801       : TemplateNodeCreator<TemplateNodeRepeat>(parser,parent,line)
2802     {
2803       TRACE(("{TemplateNodeRepeat(%s)\n",data.data()));
2804       ExpressionParser expParser(parser,line);
2805       m_expr = expParser.parse(data);
2806       QStrList stopAt;
2807       stopAt.append("endrepeat");
2808       parser->parse(this,line,stopAt,m_repeatNodes);
2809       parser->removeNextToken(); // skip over endrepeat
2810       TRACE(("}TemplateNodeRepeat(%s)\n",data.data()));
2811     }
2812     ~TemplateNodeRepeat()
2813     {
2814       delete m_expr;
2815     }
2816     void render(FTextStream &ts, TemplateContext *c)
2817     {
2818       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2819       ci->setLocation(m_templateName,m_line);
2820       TemplateVariant v;
2821       if (m_expr && (v=m_expr->resolve(c)).type()==TemplateVariant::Integer)
2822       {
2823         int i, n = v.toInt();
2824         for (i=0;i<n;i++)
2825         {
2826           TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2827           s->set("counter0",    (int)i);
2828           s->set("counter",     (int)(i+1));
2829           s->set("revcounter",  (int)(n-i));
2830           s->set("revcounter0", (int)(n-i-1));
2831           s->set("first",i==0);
2832           s->set("last", i==n-1);
2833           c->set("repeatloop",s.get());
2834           // render all items for this iteration of the loop
2835           m_repeatNodes.render(ts,c);
2836         }
2837       }
2838       else // simple type...
2839       {
2840         ci->warn(m_templateName,m_line,"for requires a variable of list type!");
2841       }
2842     }
2843   private:
2844     TemplateNodeList m_repeatNodes;
2845     ExprAst *m_expr;
2846 };
2847
2848 //----------------------------------------------------------
2849
2850 /** @brief Class representing a 'range' tag in a template */
2851 class TemplateNodeRange : public TemplateNodeCreator<TemplateNodeRange>
2852 {
2853   public:
2854     TemplateNodeRange(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
2855       : TemplateNodeCreator<TemplateNodeRange>(parser,parent,line)
2856     {
2857       TRACE(("{TemplateNodeRange(%s)\n",data.data()));
2858       QCString start,end;
2859       int i1 = data.find(" from ");
2860       int i2 = data.find(" to ");
2861       int i3 = data.find(" downto ");
2862       if (i1==-1)
2863       {
2864         if (data.right(5)==" from")
2865         {
2866           parser->warn(m_templateName,line,"range missing after 'from' keyword");
2867         }
2868         else if (data=="from")
2869         {
2870           parser->warn(m_templateName,line,"range needs an iterator variable and a range");
2871         }
2872         else
2873         {
2874           parser->warn(m_templateName,line,"range is missing 'from' keyword");
2875         }
2876       }
2877       else if (i2==-1 && i3==-1)
2878       {
2879         if (data.right(3)==" to")
2880         {
2881           parser->warn(m_templateName,line,"range is missing end value after 'to' keyword");
2882         }
2883         else if (data.right(7)==" downto")
2884         {
2885           parser->warn(m_templateName,line,"range is missing end value after 'downto' keyword");
2886         }
2887         else
2888         {
2889           parser->warn(m_templateName,line,"range is missing 'to' or 'downto' keyword");
2890         }
2891       }
2892       else
2893       {
2894         m_var = data.left(i1).stripWhiteSpace();
2895         if (m_var.isEmpty())
2896         {
2897           parser->warn(m_templateName,line,"range needs an iterator variable");
2898         }
2899         start = data.mid(i1+6,i2-i1-6).stripWhiteSpace();
2900         if (i2!=-1)
2901         {
2902           end = data.right(data.length()-i2-4).stripWhiteSpace();
2903           m_down = FALSE;
2904         }
2905         else if (i3!=-1)
2906         {
2907           end = data.right(data.length()-i3-8).stripWhiteSpace();
2908           m_down = TRUE;
2909         }
2910       }
2911       ExpressionParser expParser(parser,line);
2912       m_startExpr = expParser.parse(start);
2913       m_endExpr   = expParser.parse(end);
2914
2915       QStrList stopAt;
2916       stopAt.append("endrange");
2917       parser->parse(this,line,stopAt,m_loopNodes);
2918       parser->removeNextToken(); // skip over endrange
2919       TRACE(("}TemplateNodeRange(%s)\n",data.data()));
2920     }
2921
2922     ~TemplateNodeRange()
2923     {
2924       delete m_startExpr;
2925       delete m_endExpr;
2926     }
2927
2928     void render(FTextStream &ts, TemplateContext *c)
2929     {
2930       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
2931       ci->setLocation(m_templateName,m_line);
2932       //printf("TemplateNodeRange::render #loopNodes=%d\n",
2933       //    m_loopNodes.count());
2934       if (m_startExpr && m_endExpr)
2935       {
2936         TemplateVariant vs = m_startExpr->resolve(c);
2937         TemplateVariant ve = m_endExpr->resolve(c);
2938         if (vs.type()==TemplateVariant::Integer && ve.type()==TemplateVariant::Integer)
2939         {
2940           int s = vs.toInt();
2941           int e = ve.toInt();
2942           int l = m_down ? s-e+1 : e-s+1;
2943           if (l>0)
2944           {
2945             c->push();
2946             //int index = m_reversed ? list.count() : 0;
2947             TemplateVariant v;
2948             const TemplateVariant *parentLoop = c->getRef("forloop");
2949             uint index = 0;
2950             int i = m_down ? e : s;
2951             bool done=false;
2952             while (!done)
2953             {
2954               // set the forloop meta-data variable
2955               TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
2956               s->set("counter0",    (int)index);
2957               s->set("counter",     (int)(index+1));
2958               s->set("revcounter",  (int)(l-index));
2959               s->set("revcounter0", (int)(l-index-1));
2960               s->set("first",index==0);
2961               s->set("last", (int)index==l-1);
2962               s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
2963               c->set("forloop",s.get());
2964
2965               // set the iterator variable
2966               c->set(m_var,i);
2967
2968               // render all items for this iteration of the loop
2969               m_loopNodes.render(ts,c);
2970
2971               index++;
2972               if (m_down)
2973               {
2974                 i--;
2975                 done = i<e;
2976               }
2977               else
2978               {
2979                 i++;
2980                 done = i>e;
2981               }
2982             }
2983             c->pop();
2984           }
2985           else
2986           {
2987             ci->warn(m_templateName,m_line,"range %d %s %d is empty!",
2988                 s,m_down?"downto":"to",e);
2989           }
2990         }
2991         else if (vs.type()!=TemplateVariant::Integer)
2992         {
2993           ci->warn(m_templateName,m_line,"range requires a start value of integer type!");
2994         }
2995         else if (ve.type()!=TemplateVariant::Integer)
2996         {
2997           ci->warn(m_templateName,m_line,"range requires an end value of integer type!");
2998         }
2999       }
3000       else if (!m_startExpr)
3001       {
3002         ci->warn(m_templateName,m_line,"range has empty start value");
3003       }
3004       else if (!m_endExpr)
3005       {
3006         ci->warn(m_templateName,m_line,"range has empty end value");
3007       }
3008     }
3009
3010   private:
3011     bool m_down;
3012     ExprAst *m_startExpr;
3013     ExprAst *m_endExpr;
3014     QCString m_var;
3015     TemplateNodeList m_loopNodes;
3016 };
3017
3018 //----------------------------------------------------------
3019
3020 /** @brief Class representing a 'for' tag in a template */
3021 class TemplateNodeFor : public TemplateNodeCreator<TemplateNodeFor>
3022 {
3023   public:
3024     TemplateNodeFor(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3025       : TemplateNodeCreator<TemplateNodeFor>(parser,parent,line)
3026     {
3027       TRACE(("{TemplateNodeFor(%s)\n",data.data()));
3028       QCString exprStr;
3029       int i = data.find(" in ");
3030       if (i==-1)
3031       {
3032         if (data.right(3)==" in")
3033         {
3034           parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
3035         }
3036         else if (data=="in")
3037         {
3038           parser->warn(m_templateName,line,"for needs at least one iterator variable");
3039         }
3040         else
3041         {
3042           parser->warn(m_templateName,line,"for is missing 'in' keyword");
3043         }
3044       }
3045       else
3046       {
3047         m_vars = split(data.left(i),",");
3048         if (m_vars.count()==0)
3049         {
3050           parser->warn(m_templateName,line,"for needs at least one iterator variable");
3051         }
3052
3053         int j = data.find(" reversed",i);
3054         m_reversed = (j!=-1);
3055
3056         if (j==-1) j=data.length();
3057         if (j>i+4)
3058         {
3059           exprStr = data.mid(i+4,j-i-4); // skip over " in " part
3060         }
3061         if (exprStr.isEmpty())
3062         {
3063           parser->warn(m_templateName,line,"for is missing container after 'in' keyword");
3064         }
3065       }
3066       ExpressionParser expParser(parser,line);
3067       m_expr = expParser.parse(exprStr);
3068
3069       QStrList stopAt;
3070       stopAt.append("endfor");
3071       stopAt.append("empty");
3072       parser->parse(this,line,stopAt,m_loopNodes);
3073       TemplateToken *tok = parser->takeNextToken();
3074       if (tok && tok->data=="empty")
3075       {
3076         stopAt.removeLast();
3077         parser->parse(this,line,stopAt,m_emptyNodes);
3078         parser->removeNextToken(); // skip over endfor
3079       }
3080       delete tok;
3081       TRACE(("}TemplateNodeFor(%s)\n",data.data()));
3082     }
3083
3084     ~TemplateNodeFor()
3085     {
3086       delete m_expr;
3087     }
3088
3089     void render(FTextStream &ts, TemplateContext *c)
3090     {
3091       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3092       ci->setLocation(m_templateName,m_line);
3093       //printf("TemplateNodeFor::render #loopNodes=%d #emptyNodes=%d\n",
3094       //    m_loopNodes.count(),m_emptyNodes.count());
3095       if (m_expr)
3096       {
3097         TemplateVariant v = m_expr->resolve(c);
3098         if (v.type()==TemplateVariant::Function)
3099         {
3100           v = v.call(QValueList<TemplateVariant>());
3101         }
3102         const TemplateListIntf *list = v.toList();
3103         if (list)
3104         {
3105           uint listSize = list->count();
3106           if (listSize==0) // empty for loop
3107           {
3108             m_emptyNodes.render(ts,c);
3109             return;
3110           }
3111           c->push();
3112           //int index = m_reversed ? list.count() : 0;
3113           TemplateVariant v;
3114           const TemplateVariant *parentLoop = c->getRef("forloop");
3115           uint index = m_reversed ? listSize-1 : 0;
3116           TemplateListIntf::ConstIterator *it = list->createIterator();
3117           for (m_reversed ? it->toLast() : it->toFirst();
3118               (it->current(v));
3119               m_reversed ? it->toPrev() : it->toNext())
3120           {
3121             TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
3122             s->set("counter0",    (int)index);
3123             s->set("counter",     (int)(index+1));
3124             s->set("revcounter",  (int)(listSize-index));
3125             s->set("revcounter0", (int)(listSize-index-1));
3126             s->set("first",index==0);
3127             s->set("last", index==listSize-1);
3128             s->set("parentloop",parentLoop ? *parentLoop : TemplateVariant());
3129             c->set("forloop",s.get());
3130
3131             // add variables for this loop to the context
3132             //obj->addVariableToContext(index,m_vars,c);
3133             uint vi=0;
3134             if (m_vars.count()==1) // loop variable represents an item
3135             {
3136               c->set(m_vars[vi++],v);
3137             }
3138             else if (m_vars.count()>1 && v.type()==TemplateVariant::Struct)
3139               // loop variables represent elements in a list item
3140             {
3141               for (uint i=0;i<m_vars.count();i++,vi++)
3142               {
3143                 c->set(m_vars[vi],v.toStruct()->get(m_vars[vi]));
3144               }
3145             }
3146             for (;vi<m_vars.count();vi++)
3147             {
3148               c->set(m_vars[vi],TemplateVariant());
3149             }
3150
3151             // render all items for this iteration of the loop
3152             m_loopNodes.render(ts,c);
3153
3154             if (m_reversed) index--; else index++;
3155           }
3156           c->pop();
3157           delete it;
3158         }
3159         else // simple type...
3160         {
3161           ci->warn(m_templateName,m_line,"for requires a variable of list type, got type '%s'!",v.typeAsString().data());
3162         }
3163       }
3164     }
3165
3166   private:
3167     bool m_reversed;
3168     ExprAst *m_expr;
3169     QValueList<QCString> m_vars;
3170     TemplateNodeList m_loopNodes;
3171     TemplateNodeList m_emptyNodes;
3172 };
3173
3174 //----------------------------------------------------------
3175
3176 /** @brief Class representing an 'markers' tag in a template */
3177 class TemplateNodeMsg : public TemplateNodeCreator<TemplateNodeMsg>
3178 {
3179   public:
3180     TemplateNodeMsg(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
3181       : TemplateNodeCreator<TemplateNodeMsg>(parser,parent,line)
3182     {
3183       TRACE(("{TemplateNodeMsg()\n"));
3184       QStrList stopAt;
3185       stopAt.append("endmsg");
3186       parser->parse(this,line,stopAt,m_nodes);
3187       parser->removeNextToken(); // skip over endmarkers
3188       TRACE(("}TemplateNodeMsg()\n"));
3189     }
3190     void render(FTextStream &, TemplateContext *c)
3191     {
3192       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3193       ci->setLocation(m_templateName,m_line);
3194       TemplateEscapeIntf *escIntf = ci->escapeIntf();
3195       ci->setActiveEscapeIntf(0); // avoid escaping things we send to standard out
3196       bool enable = ci->spacelessEnabled();
3197       ci->enableSpaceless(FALSE);
3198       FTextStream ts(stdout);
3199       m_nodes.render(ts,c);
3200       ts << endl;
3201       ci->setActiveEscapeIntf(escIntf);
3202       ci->enableSpaceless(enable);
3203     }
3204   private:
3205     TemplateNodeList m_nodes;
3206 };
3207
3208
3209 //----------------------------------------------------------
3210
3211 /** @brief Class representing a 'block' tag in a template */
3212 class TemplateNodeBlock : public TemplateNodeCreator<TemplateNodeBlock>
3213 {
3214   public:
3215     TemplateNodeBlock(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3216       : TemplateNodeCreator<TemplateNodeBlock>(parser,parent,line)
3217     {
3218       TRACE(("{TemplateNodeBlock(%s)\n",data.data()));
3219       m_blockName = data;
3220       if (m_blockName.isEmpty())
3221       {
3222         parser->warn(parser->templateName(),line,"block tag without name");
3223       }
3224       QStrList stopAt;
3225       stopAt.append("endblock");
3226       parser->parse(this,line,stopAt,m_nodes);
3227       parser->removeNextToken(); // skip over endblock
3228       TRACE(("}TemplateNodeBlock(%s)\n",data.data()));
3229     }
3230
3231     void render(FTextStream &ts, TemplateContext *c)
3232     {
3233       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3234       ci->setLocation(m_templateName,m_line);
3235       TemplateImpl *t = getTemplate();
3236       if (t)
3237       {
3238         // remove block from the context, so block.super can work
3239         TemplateNodeBlock *nb = ci->blockContext()->pop(m_blockName);
3240         if (nb) // block is overruled
3241         {
3242           ci->push();
3243           QGString super;
3244           FTextStream ss(&super);
3245           // get super block of block nb
3246           TemplateNodeBlock *sb = ci->blockContext()->get(m_blockName);
3247           if (sb && sb!=nb && sb!=this) // nb and sb both overrule this block
3248           {
3249             sb->render(ss,c); // render parent of nb to string
3250           }
3251           else if (nb!=this) // only nb overrules this block
3252           {
3253             m_nodes.render(ss,c); // render parent of nb to string
3254           }
3255           // add 'block.super' variable to allow access to parent block content
3256           TemplateAutoRef<TemplateStruct> superBlock(TemplateStruct::alloc());
3257           superBlock->set("super",TemplateVariant(super.data(),TRUE));
3258           ci->set("block",superBlock.get());
3259           // render the overruled block contents
3260           t->engine()->enterBlock(nb->m_templateName,nb->m_blockName,nb->m_line);
3261           nb->m_nodes.render(ts,c);
3262           t->engine()->leaveBlock();
3263           ci->pop();
3264           // re-add block to the context
3265           ci->blockContext()->push(nb);
3266         }
3267         else // block has no overrule
3268         {
3269           t->engine()->enterBlock(m_templateName,m_blockName,m_line);
3270           m_nodes.render(ts,c);
3271           t->engine()->leaveBlock();
3272         }
3273       }
3274     }
3275
3276     QCString name() const
3277     {
3278       return m_blockName;
3279     }
3280
3281   private:
3282     QCString m_blockName;
3283     TemplateNodeList m_nodes;
3284 };
3285
3286 //----------------------------------------------------------
3287
3288 /** @brief Class representing a 'extend' tag in a template */
3289 class TemplateNodeExtend : public TemplateNodeCreator<TemplateNodeExtend>
3290 {
3291   public:
3292     TemplateNodeExtend(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3293       : TemplateNodeCreator<TemplateNodeExtend>(parser,parent,line)
3294     {
3295       TRACE(("{TemplateNodeExtend(%s)\n",data.data()));
3296       ExpressionParser ep(parser,line);
3297       if (data.isEmpty())
3298       {
3299         parser->warn(m_templateName,line,"extend tag is missing template file argument");
3300       }
3301       m_extendExpr = ep.parse(data);
3302       QStrList stopAt;
3303       parser->parse(this,line,stopAt,m_nodes);
3304       TRACE(("}TemplateNodeExtend(%s)\n",data.data()));
3305     }
3306    ~TemplateNodeExtend()
3307     {
3308       delete m_extendExpr;
3309     }
3310
3311     void render(FTextStream &ts, TemplateContext *c)
3312     {
3313       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3314       ci->setLocation(m_templateName,m_line);
3315       if (m_extendExpr==0) return;
3316
3317       QCString extendFile = m_extendExpr->resolve(c).toString();
3318       if (extendFile.isEmpty())
3319       {
3320         ci->warn(m_templateName,m_line,"invalid parameter for extend command");
3321       }
3322
3323       // goto root of tree (template node)
3324       TemplateImpl *t = getTemplate();
3325       if (t)
3326       {
3327         Template *bt = t->engine()->loadByName(extendFile,m_line);
3328         TemplateImpl *baseTemplate = bt ? dynamic_cast<TemplateImpl*>(bt) : 0;
3329         if (baseTemplate)
3330         {
3331           // fill block context
3332           TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3333           TemplateBlockContext *bc = ci->blockContext();
3334
3335           // add overruling blocks to the context
3336           QListIterator<TemplateNode> li(m_nodes);
3337           TemplateNode *n;
3338           for (li.toFirst();(n=li.current());++li)
3339           {
3340             TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
3341             if (nb)
3342             {
3343               bc->add(nb);
3344             }
3345             TemplateNodeMsg *msg = dynamic_cast<TemplateNodeMsg*>(n);
3346             if (msg)
3347             {
3348               msg->render(ts,c);
3349             }
3350           }
3351
3352           // render the base template with the given context
3353           baseTemplate->render(ts,c);
3354
3355           // clean up
3356           bc->clear();
3357           t->engine()->unload(t);
3358         }
3359         else
3360         {
3361           ci->warn(m_templateName,m_line,"failed to load template %s for extend",extendFile.data());
3362         }
3363       }
3364     }
3365
3366   private:
3367     ExprAst *m_extendExpr;
3368     TemplateNodeList m_nodes;
3369 };
3370
3371 /** @brief Class representing an 'include' tag in a template */
3372 class TemplateNodeInclude : public TemplateNodeCreator<TemplateNodeInclude>
3373 {
3374   public:
3375     TemplateNodeInclude(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3376       : TemplateNodeCreator<TemplateNodeInclude>(parser,parent,line)
3377     {
3378       TRACE(("TemplateNodeInclude(%s)\n",data.data()));
3379       ExpressionParser ep(parser,line);
3380       if (data.isEmpty())
3381       {
3382         parser->warn(m_templateName,line,"include tag is missing template file argument");
3383       }
3384       m_includeExpr = ep.parse(data);
3385     }
3386    ~TemplateNodeInclude()
3387     {
3388       delete m_includeExpr;
3389     }
3390     void render(FTextStream &ts, TemplateContext *c)
3391     {
3392       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3393       ci->setLocation(m_templateName,m_line);
3394       if (m_includeExpr)
3395       {
3396         QCString includeFile = m_includeExpr->resolve(c).toString();
3397         if (includeFile.isEmpty())
3398         {
3399           ci->warn(m_templateName,m_line,"invalid parameter for include command\n");
3400         }
3401         else
3402         {
3403           TemplateImpl *t = getTemplate();
3404           if (t)
3405           {
3406             Template *it = t->engine()->loadByName(includeFile,m_line);
3407             TemplateImpl *incTemplate = it ? dynamic_cast<TemplateImpl*>(it) : 0;
3408             if (incTemplate)
3409             {
3410               incTemplate->render(ts,c);
3411               t->engine()->unload(t);
3412             }
3413             else
3414             {
3415               ci->warn(m_templateName,m_line,"failed to load template '%s' for include",includeFile.data()?includeFile.data():"");
3416             }
3417           }
3418         }
3419       }
3420     }
3421
3422   private:
3423     ExprAst *m_includeExpr;
3424 };
3425
3426 //----------------------------------------------------------
3427
3428 /** @brief Class representing an 'create' tag in a template */
3429 class TemplateNodeCreate : public TemplateNodeCreator<TemplateNodeCreate>
3430 {
3431   public:
3432     TemplateNodeCreate(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3433       : TemplateNodeCreator<TemplateNodeCreate>(parser,parent,line)
3434     {
3435       TRACE(("TemplateNodeCreate(%s)\n",data.data()));
3436       ExpressionParser ep(parser,line);
3437       if (data.isEmpty())
3438       {
3439         parser->warn(m_templateName,line,"create tag is missing arguments");
3440       }
3441       int i = data.find(" from ");
3442       if (i==-1)
3443       {
3444         if (data.right(3)==" from")
3445         {
3446           parser->warn(m_templateName,line,"create is missing template name after 'from' keyword");
3447         }
3448         else if (data=="from")
3449         {
3450           parser->warn(m_templateName,line,"create needs a file name and a template name");
3451         }
3452         else
3453         {
3454           parser->warn(m_templateName,line,"create is missing 'from' keyword");
3455         }
3456       }
3457       else
3458       {
3459         ExpressionParser ep(parser,line);
3460         m_fileExpr = ep.parse(data.left(i).stripWhiteSpace());
3461         m_templateExpr = ep.parse(data.mid(i+6).stripWhiteSpace());
3462       }
3463     }
3464    ~TemplateNodeCreate()
3465     {
3466       delete m_templateExpr;
3467       delete m_fileExpr;
3468     }
3469     void mkpath(TemplateContextImpl *ci,const QCString &fileName)
3470     {
3471       int i=fileName.find('/');
3472       QCString outputDir = ci->outputDirectory();
3473       QDir d(outputDir);
3474       int j=0;
3475       while (i!=-1) // fileName contains path part
3476       {
3477         if (d.exists())
3478         {
3479           bool ok = d.mkdir(fileName.mid(j,i-j));
3480           if (!ok) break;
3481           QCString dirName = outputDir+'/'+fileName.left(i);
3482           d = QDir(dirName);
3483           j = i+1;
3484         }
3485         i=fileName.find('/',i+1);
3486       }
3487     }
3488     void render(FTextStream &, TemplateContext *c)
3489     {
3490       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3491       ci->setLocation(m_templateName,m_line);
3492       if (m_templateExpr && m_fileExpr)
3493       {
3494         QCString templateFile = m_templateExpr->resolve(c).toString();
3495         QCString outputFile = m_fileExpr->resolve(c).toString();
3496         if (templateFile.isEmpty())
3497         {
3498           ci->warn(m_templateName,m_line,"empty template name parameter for create command\n");
3499         }
3500         else if (outputFile.isEmpty())
3501         {
3502           ci->warn(m_templateName,m_line,"empty file name parameter for create command\n");
3503         }
3504         else
3505         {
3506           TemplateImpl *t = getTemplate();
3507           if (t)
3508           {
3509             Template *ct = t->engine()->loadByName(templateFile,m_line);
3510             TemplateImpl *createTemplate = ct ? dynamic_cast<TemplateImpl*>(ct) : 0;
3511             if (createTemplate)
3512             {
3513               //mkpath(ci,outputFile);
3514               QCString extension=outputFile;
3515               int i=extension.findRev('.');
3516               if (i!=-1)
3517               {
3518                 extension=extension.right(extension.length()-i-1);
3519               }
3520               if (!ci->outputDirectory().isEmpty())
3521               {
3522                 outputFile.prepend(ci->outputDirectory()+"/");
3523               }
3524               QFile f(outputFile);
3525               if (f.open(IO_WriteOnly))
3526               {
3527                 TemplateEscapeIntf *escIntf = ci->escapeIntf();
3528                 ci->selectEscapeIntf(extension);
3529                 FTextStream ts(&f);
3530                 createTemplate->render(ts,c);
3531                 t->engine()->unload(t);
3532                 ci->setActiveEscapeIntf(escIntf);
3533               }
3534               else
3535               {
3536                 ci->warn(m_templateName,m_line,"failed to open output file '%s' for create command",outputFile.data());
3537               }
3538             }
3539             else
3540             {
3541               ci->warn(m_templateName,m_line,"failed to load template '%s' for include",templateFile.data());
3542             }
3543           }
3544         }
3545       }
3546     }
3547
3548   private:
3549     ExprAst *m_templateExpr;
3550     ExprAst *m_fileExpr;
3551 };
3552
3553 //----------------------------------------------------------
3554
3555 /** @brief Class representing an 'tree' tag in a template */
3556 class TemplateNodeTree : public TemplateNodeCreator<TemplateNodeTree>
3557 {
3558     struct TreeContext
3559     {
3560       TreeContext(TemplateNodeTree *o,const TemplateListIntf *l,TemplateContext *c)
3561         : object(o), list(l), templateCtx(c) {}
3562       TemplateNodeTree *object;
3563       const TemplateListIntf *list;
3564       TemplateContext  *templateCtx;
3565     };
3566   public:
3567     TemplateNodeTree(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3568       : TemplateNodeCreator<TemplateNodeTree>(parser,parent,line)
3569     {
3570       TRACE(("{TemplateNodeTree(%s)\n",data.data()));
3571       ExpressionParser ep(parser,line);
3572       if (data.isEmpty())
3573       {
3574         parser->warn(m_templateName,line,"recursetree tag is missing data argument");
3575       }
3576       m_treeExpr = ep.parse(data);
3577       QStrList stopAt;
3578       stopAt.append("endrecursetree");
3579       parser->parse(this,line,stopAt,m_treeNodes);
3580       parser->removeNextToken(); // skip over endrecursetree
3581       TRACE(("}TemplateNodeTree(%s)\n",data.data()));
3582     }
3583     ~TemplateNodeTree()
3584     {
3585       delete m_treeExpr;
3586     }
3587     static TemplateVariant renderChildrenStub(const void *ctx, const QValueList<TemplateVariant> &)
3588     {
3589       return TemplateVariant(((TreeContext*)ctx)->object->
3590                              renderChildren((const TreeContext*)ctx),TRUE);
3591     }
3592     QCString renderChildren(const TreeContext *ctx)
3593     {
3594       //printf("TemplateNodeTree::renderChildren(%d)\n",ctx->list->count());
3595       // render all children of node to a string and return it
3596       QGString result;
3597       FTextStream ss(&result);
3598       TemplateContext *c = ctx->templateCtx;
3599       c->push();
3600       TemplateVariant node;
3601       TemplateListIntf::ConstIterator *it = ctx->list->createIterator();
3602       for (it->toFirst();(it->current(node));it->toNext())
3603       {
3604         c->set("node",node);
3605         bool hasChildren=FALSE;
3606         const TemplateStructIntf *ns = node.toStruct();
3607         if (ns) // node is a struct
3608         {
3609           TemplateVariant v = ns->get("children");
3610           if (v.isValid()) // with a field 'children'
3611           {
3612             const TemplateListIntf *list = v.toList();
3613             if (list && list->count()>0) // non-empty list
3614             {
3615               TreeContext childCtx(this,list,ctx->templateCtx);
3616 //              TemplateVariant children(&childCtx,renderChildrenStub);
3617               TemplateVariant children(TemplateVariant::Delegate::fromFunction(&childCtx,renderChildrenStub));
3618               children.setRaw(TRUE);
3619               c->set("children",children);
3620               m_treeNodes.render(ss,c);
3621               hasChildren=TRUE;
3622             }
3623           }
3624         }
3625         if (!hasChildren)
3626         {
3627           c->set("children",TemplateVariant("")); // provide default
3628           m_treeNodes.render(ss,c);
3629         }
3630       }
3631       c->pop();
3632       return result.data();
3633     }
3634     void render(FTextStream &ts, TemplateContext *c)
3635     {
3636       //printf("TemplateNodeTree::render()\n");
3637       TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
3638       ci->setLocation(m_templateName,m_line);
3639       TemplateVariant v = m_treeExpr->resolve(c);
3640       const TemplateListIntf *list = v.toList();
3641       if (list)
3642       {
3643         TreeContext ctx(this,list,c);
3644         ts << renderChildren(&ctx);
3645       }
3646       else
3647       {
3648         ci->warn(m_templateName,m_line,"recursetree's argument should be a list type");
3649       }
3650     }
3651
3652   private:
3653     ExprAst         *m_treeExpr;
3654     TemplateNodeList m_treeNodes;
3655 };
3656
3657 //----------------------------------------------------------
3658
3659 /** @brief Class representing an 'indexentry' tag in a template */
3660 class TemplateNodeIndexEntry : public TemplateNodeCreator<TemplateNodeIndexEntry>
3661 {
3662     struct Mapping
3663     {
3664       Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3665      ~Mapping() { delete value; }
3666       QCString name;
3667       ExprAst *value;
3668     };
3669   public:
3670     TemplateNodeIndexEntry(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3671       : TemplateNodeCreator<TemplateNodeIndexEntry>(parser,parent,line)
3672     {
3673       TRACE(("{TemplateNodeIndexEntry(%s)\n",data.data()));
3674       m_args.setAutoDelete(TRUE);
3675       ExpressionParser expParser(parser,line);
3676       QValueList<QCString> args = split(data," ");
3677       QValueListIterator<QCString> it = args.begin();
3678       if (it==args.end() || (*it).find('=')!=-1)
3679       {
3680         parser->warn(parser->templateName(),line,"Missing name for indexentry tag");
3681       }
3682       else
3683       {
3684         m_name = *it;
3685         ++it;
3686         while (it!=args.end())
3687         {
3688           QCString arg = *it;
3689           int j=arg.find('=');
3690           if (j>0)
3691           {
3692             ExprAst *expr = expParser.parse(arg.mid(j+1));
3693             if (expr)
3694             {
3695               m_args.append(new Mapping(arg.left(j),expr));
3696             }
3697           }
3698           else
3699           {
3700             parser->warn(parser->templateName(),line,"invalid argument '%s' for indexentry tag",arg.data());
3701           }
3702           ++it;
3703         }
3704       }
3705       TRACE(("}TemplateNodeIndexEntry(%s)\n",data.data()));
3706     }
3707     void render(FTextStream &, TemplateContext *c)
3708     {
3709       if (!m_name.isEmpty())
3710       {
3711         TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3712         ci->setLocation(m_templateName,m_line);
3713         QListIterator<Mapping> it(m_args);
3714         Mapping *mapping;
3715         QValueList<TemplateKeyValue> list;
3716         for (it.toFirst();(mapping=it.current());++it)
3717         {
3718           list.append(TemplateKeyValue(mapping->name,mapping->value->resolve(c)));
3719         }
3720         ci->addIndexEntry(m_name,list);
3721       }
3722     }
3723   private:
3724     QCString m_name;
3725     QList<Mapping> m_args;
3726 };
3727
3728 //----------------------------------------------------------
3729
3730 /** @brief Class representing an 'opensubindex' tag in a template */
3731 class TemplateNodeOpenSubIndex : public TemplateNodeCreator<TemplateNodeOpenSubIndex>
3732 {
3733   public:
3734     TemplateNodeOpenSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3735       : TemplateNodeCreator<TemplateNodeOpenSubIndex>(parser,parent,line)
3736     {
3737       TRACE(("{TemplateNodeOpenSubIndex(%s)\n",data.data()));
3738       m_name = data.stripWhiteSpace();
3739       if (m_name.isEmpty())
3740       {
3741         parser->warn(parser->templateName(),line,"Missing argument for opensubindex tag");
3742       }
3743       else if (m_name.find(' ')!=-1)
3744       {
3745         parser->warn(parser->templateName(),line,"Expected single argument for opensubindex tag got '%s'",data.data());
3746         m_name="";
3747       }
3748       TRACE(("}TemplateNodeOpenSubIndex(%s)\n",data.data()));
3749     }
3750     void render(FTextStream &, TemplateContext *c)
3751     {
3752       if (!m_name.isEmpty())
3753       {
3754         TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3755         ci->setLocation(m_templateName,m_line);
3756         ci->openSubIndex(m_name);
3757       }
3758     }
3759   private:
3760     QCString m_name;
3761 };
3762
3763 //----------------------------------------------------------
3764
3765 /** @brief Class representing an 'closesubindex' tag in a template */
3766 class TemplateNodeCloseSubIndex : public TemplateNodeCreator<TemplateNodeCloseSubIndex>
3767 {
3768   public:
3769     TemplateNodeCloseSubIndex(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3770       : TemplateNodeCreator<TemplateNodeCloseSubIndex>(parser,parent,line)
3771     {
3772       TRACE(("{TemplateNodeCloseSubIndex(%s)\n",data.data()));
3773       m_name = data.stripWhiteSpace();
3774       if (m_name.isEmpty())
3775       {
3776         parser->warn(parser->templateName(),line,"Missing argument for closesubindex tag");
3777       }
3778       else if (m_name.find(' ')!=-1 || m_name.isEmpty())
3779       {
3780         parser->warn(parser->templateName(),line,"Expected single argument for closesubindex tag got '%s'",data.data());
3781         m_name="";
3782       }
3783       TRACE(("}TemplateNodeCloseSubIndex(%s)\n",data.data()));
3784     }
3785     void render(FTextStream &, TemplateContext *c)
3786     {
3787       if (!m_name.isEmpty())
3788       {
3789         TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3790         ci->setLocation(m_templateName,m_line);
3791         ci->closeSubIndex(m_name);
3792       }
3793     }
3794   private:
3795     QCString m_name;
3796 };
3797
3798
3799 //----------------------------------------------------------
3800
3801 /** @brief Class representing an 'with' tag in a template */
3802 class TemplateNodeWith : public TemplateNodeCreator<TemplateNodeWith>
3803 {
3804     struct Mapping
3805     {
3806       Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3807      ~Mapping() { delete value; }
3808       QCString name;
3809       ExprAst *value;
3810     };
3811   public:
3812     TemplateNodeWith(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3813       : TemplateNodeCreator<TemplateNodeWith>(parser,parent,line)
3814     {
3815       TRACE(("{TemplateNodeWith(%s)\n",data.data()));
3816       m_args.setAutoDelete(TRUE);
3817       ExpressionParser expParser(parser,line);
3818       QValueList<QCString> args = split(data," ");
3819       QValueListIterator<QCString> it = args.begin();
3820       while (it!=args.end())
3821       {
3822         QCString arg = *it;
3823         int j=arg.find('=');
3824         if (j>0)
3825         {
3826           ExprAst *expr = expParser.parse(arg.mid(j+1));
3827           if (expr)
3828           {
3829             m_args.append(new Mapping(arg.left(j),expr));
3830           }
3831         }
3832         else
3833         {
3834           parser->warn(parser->templateName(),line,"invalid argument '%s' for 'with' tag",arg.data());
3835         }
3836         ++it;
3837       }
3838       QStrList stopAt;
3839       stopAt.append("endwith");
3840       parser->parse(this,line,stopAt,m_nodes);
3841       parser->removeNextToken(); // skip over endwith
3842       TRACE(("}TemplateNodeWith(%s)\n",data.data()));
3843     }
3844     ~TemplateNodeWith()
3845     {
3846     }
3847     void render(FTextStream &ts, TemplateContext *c)
3848     {
3849       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3850       ci->setLocation(m_templateName,m_line);
3851       c->push();
3852       QListIterator<Mapping> it(m_args);
3853       Mapping *mapping;
3854       for (it.toFirst();(mapping=it.current());++it)
3855       {
3856         TemplateVariant value = mapping->value->resolve(c);
3857         ci->set(mapping->name,value);
3858       }
3859       m_nodes.render(ts,c);
3860       c->pop();
3861     }
3862   private:
3863     TemplateNodeList m_nodes;
3864     QList<Mapping> m_args;
3865 };
3866
3867 //----------------------------------------------------------
3868
3869 /** @brief Class representing an 'set' tag in a template */
3870 class TemplateNodeCycle : public TemplateNodeCreator<TemplateNodeCycle>
3871 {
3872   public:
3873     TemplateNodeCycle(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3874       : TemplateNodeCreator<TemplateNodeCycle>(parser,parent,line)
3875     {
3876       TRACE(("{TemplateNodeCycle(%s)\n",data.data()));
3877       m_args.setAutoDelete(TRUE);
3878       m_index=0;
3879       ExpressionParser expParser(parser,line);
3880       QValueList<QCString> args = split(data," ");
3881       QValueListIterator<QCString> it = args.begin();
3882       while (it!=args.end())
3883       {
3884         ExprAst *expr = expParser.parse(*it);
3885         if (expr)
3886         {
3887           m_args.append(expr);
3888         }
3889         ++it;
3890       }
3891       if (m_args.count()<2)
3892       {
3893           parser->warn(parser->templateName(),line,"expected at least two arguments for cycle command, got %d",m_args.count());
3894       }
3895       TRACE(("}TemplateNodeCycle(%s)\n",data.data()));
3896     }
3897     void render(FTextStream &ts, TemplateContext *c)
3898     {
3899       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3900       ci->setLocation(m_templateName,m_line);
3901       if (m_index<m_args.count())
3902       {
3903         TemplateVariant v = m_args.at(m_index)->resolve(c);
3904         if (v.type()==TemplateVariant::Function)
3905         {
3906           v = v.call(QValueList<TemplateVariant>());
3907         }
3908         if (ci->escapeIntf() && !v.raw())
3909         {
3910           ts << ci->escapeIntf()->escape(v.toString());
3911         }
3912         else
3913         {
3914           ts << v.toString();
3915         }
3916       }
3917       if (++m_index==m_args.count()) // wrap around
3918       {
3919         m_index=0;
3920       }
3921     }
3922   private:
3923     uint m_index;
3924     QList<ExprAst> m_args;
3925 };
3926
3927 //----------------------------------------------------------
3928
3929 /** @brief Class representing an 'set' tag in a template */
3930 class TemplateNodeSet : public TemplateNodeCreator<TemplateNodeSet>
3931 {
3932     struct Mapping
3933     {
3934       Mapping(const QCString &n,ExprAst *e) : name(n), value(e) {}
3935      ~Mapping() { delete value; }
3936       QCString name;
3937       ExprAst *value;
3938     };
3939   public:
3940     TemplateNodeSet(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
3941       : TemplateNodeCreator<TemplateNodeSet>(parser,parent,line), m_mapping(0)
3942     {
3943       TRACE(("{TemplateNodeSet(%s)\n",data.data()));
3944       ExpressionParser expParser(parser,line);
3945       // data format: name=expression
3946       int j=data.find('=');
3947       ExprAst *expr = 0;
3948       if (j>0 && (expr = expParser.parse(data.mid(j+1))))
3949       {
3950         m_mapping = new Mapping(data.left(j),expr);
3951       }
3952       TRACE(("}TemplateNodeSet(%s)\n",data.data()));
3953     }
3954     ~TemplateNodeSet()
3955     {
3956       delete m_mapping;
3957     }
3958     void render(FTextStream &, TemplateContext *c)
3959     {
3960       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3961       ci->setLocation(m_templateName,m_line);
3962       if (m_mapping)
3963       {
3964         TemplateVariant value = m_mapping->value->resolve(c);
3965         ci->set(m_mapping->name,value);
3966       }
3967     }
3968   private:
3969     Mapping *m_mapping;
3970 };
3971
3972 //----------------------------------------------------------
3973
3974 /** @brief Class representing an 'spaceless' tag in a template */
3975 class TemplateNodeSpaceless : public TemplateNodeCreator<TemplateNodeSpaceless>
3976 {
3977   public:
3978     TemplateNodeSpaceless(TemplateParser *parser,TemplateNode *parent,int line,const QCString &)
3979       : TemplateNodeCreator<TemplateNodeSpaceless>(parser,parent,line)
3980     {
3981       TRACE(("{TemplateNodeSpaceless()\n"));
3982       QStrList stopAt;
3983       stopAt.append("endspaceless");
3984       parser->parse(this,line,stopAt,m_nodes);
3985       parser->removeNextToken(); // skip over endwith
3986       TRACE(("}TemplateNodeSpaceless()\n"));
3987     }
3988     void render(FTextStream &ts, TemplateContext *c)
3989     {
3990       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
3991       ci->setLocation(m_templateName,m_line);
3992       bool wasSpaceless = ci->spacelessEnabled();
3993       ci->enableSpaceless(TRUE);
3994       m_nodes.render(ts,c);
3995       ci->enableSpaceless(wasSpaceless);
3996     }
3997   private:
3998     TemplateNodeList m_nodes;
3999 };
4000
4001 //----------------------------------------------------------
4002
4003 /** @brief Class representing an 'markers' tag in a template */
4004 class TemplateNodeMarkers : public TemplateNodeCreator<TemplateNodeMarkers>
4005 {
4006   public:
4007     TemplateNodeMarkers(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
4008       : TemplateNodeCreator<TemplateNodeMarkers>(parser,parent,line)
4009     {
4010       TRACE(("{TemplateNodeMarkers(%s)\n",data.data()));
4011       int i = data.find(" in ");
4012       int w = data.find(" with ");
4013       if (i==-1 || w==-1 || w<i)
4014       {
4015         parser->warn(m_templateName,line,"markers tag as wrong format. Expected: markers <var> in <list> with <string_with_markers>");
4016       }
4017       else
4018       {
4019         ExpressionParser expParser(parser,line);
4020         m_var = data.left(i);
4021         m_listExpr = expParser.parse(data.mid(i+4,w-i-4));
4022         m_patternExpr = expParser.parse(data.right(data.length()-w-6));
4023       }
4024       QStrList stopAt;
4025       stopAt.append("endmarkers");
4026       parser->parse(this,line,stopAt,m_nodes);
4027       parser->removeNextToken(); // skip over endmarkers
4028       TRACE(("}TemplateNodeMarkers(%s)\n",data.data()));
4029     }
4030     void render(FTextStream &ts, TemplateContext *c)
4031     {
4032       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4033       ci->setLocation(m_templateName,m_line);
4034       if (!m_var.isEmpty() && m_listExpr && m_patternExpr)
4035       {
4036         TemplateVariant v = m_listExpr->resolve(c);
4037         const TemplateListIntf *list = v.toList();
4038         TemplateVariant patternStr = m_patternExpr->resolve(c);
4039         if (list)
4040         {
4041           if (patternStr.type()==TemplateVariant::String)
4042           {
4043             TemplateListIntf::ConstIterator *it = list->createIterator();
4044             c->push();
4045             QCString str = patternStr.toString();
4046             QRegExp marker("@[0-9]+"); // pattern for a marker, i.e. @0, @1 ... @12, etc
4047             int index=0,newIndex,matchLen;
4048             while ((newIndex=marker.match(str,index,&matchLen))!=-1)
4049             {
4050               ts << str.mid(index,newIndex-index); // write text before marker
4051               bool ok;
4052               uint entryIndex = str.mid(newIndex+1,matchLen-1).toUInt(&ok); // get marker id
4053               TemplateVariant var;
4054               uint i=0;
4055               // search for list element at position id
4056               for (it->toFirst(); (it->current(var)) && i<entryIndex; it->toNext(),i++) {}
4057               if (ok && i==entryIndex) // found element
4058               {
4059                 TemplateAutoRef<TemplateStruct> s(TemplateStruct::alloc());
4060                 s->set("id",(int)i);
4061                 c->set("markers",s.get());
4062                 c->set(m_var,var); // define local variable to hold element of list type
4063                 bool wasSpaceless = ci->spacelessEnabled();
4064                 ci->enableSpaceless(TRUE);
4065                 m_nodes.render(ts,c);
4066                 ci->enableSpaceless(wasSpaceless);
4067               }
4068               else if (!ok)
4069               {
4070                 ci->warn(m_templateName,m_line,"markers pattern string has invalid markers '%s'",str.data());
4071               }
4072               else if (i<entryIndex)
4073               {
4074                 ci->warn(m_templateName,m_line,"markers list does not an element for marker position %d",i);
4075               }
4076               index=newIndex+matchLen; // set index just after marker
4077             }
4078             ts << str.right(str.length()-index); // write text after last marker
4079             c->pop();
4080           }
4081           else
4082           {
4083             ci->warn(m_templateName,m_line,"markers requires a parameter of string type after 'with'!");
4084           }
4085         }
4086         else
4087         {
4088           ci->warn(m_templateName,m_line,"markers requires a parameter of list type after 'in'!");
4089         }
4090       }
4091     }
4092   private:
4093     TemplateNodeList m_nodes;
4094     QCString m_var;
4095     ExprAst *m_listExpr;
4096     ExprAst *m_patternExpr;
4097 };
4098
4099 //----------------------------------------------------------
4100
4101 /** @brief Factory class for creating tag AST nodes found in a template */
4102 class TemplateNodeFactory
4103 {
4104   public:
4105     typedef TemplateNode *(*CreateFunc)(TemplateParser *parser,
4106                                         TemplateNode *parent,
4107                                         int line,
4108                                         const QCString &data);
4109
4110     static TemplateNodeFactory *instance()
4111     {
4112       static TemplateNodeFactory *instance = 0;
4113       if (instance==0) instance = new TemplateNodeFactory;
4114       return instance;
4115     }
4116
4117     TemplateNode *create(const QCString &name,
4118                          TemplateParser *parser,
4119                          TemplateNode *parent,
4120                          int line,
4121                          const QCString &data)
4122     {
4123       if (m_registry.find(name)==0) return 0;
4124       return ((CreateFunc)m_registry[name])(parser,parent,line,data);
4125     }
4126
4127     void registerTemplateNode(const QCString &name,CreateFunc func)
4128     {
4129       m_registry.insert(name,(void*)func);
4130     }
4131
4132     /** @brief Helper class for registering a template AST node */
4133     template<class T> class AutoRegister
4134     {
4135       public:
4136         AutoRegister<T>(const QCString &key)
4137         {
4138           TemplateNodeFactory::instance()->registerTemplateNode(key,T::createInstance);
4139         }
4140     };
4141
4142   private:
4143     QDict<void> m_registry;
4144 };
4145
4146 // register a handler for each start tag we support
4147 static TemplateNodeFactory::AutoRegister<TemplateNodeIf>        autoRefIf("if");
4148 static TemplateNodeFactory::AutoRegister<TemplateNodeFor>       autoRefFor("for");
4149 static TemplateNodeFactory::AutoRegister<TemplateNodeMsg>       autoRefMsg("msg");
4150 static TemplateNodeFactory::AutoRegister<TemplateNodeSet>       autoRefSet("set");
4151 static TemplateNodeFactory::AutoRegister<TemplateNodeTree>      autoRefTree("recursetree");
4152 static TemplateNodeFactory::AutoRegister<TemplateNodeWith>      autoRefWith("with");
4153 static TemplateNodeFactory::AutoRegister<TemplateNodeBlock>     autoRefBlock("block");
4154 static TemplateNodeFactory::AutoRegister<TemplateNodeCycle>     autoRefCycle("cycle");
4155 static TemplateNodeFactory::AutoRegister<TemplateNodeRange>     autoRefRange("range");
4156 static TemplateNodeFactory::AutoRegister<TemplateNodeExtend>    autoRefExtend("extend");
4157 static TemplateNodeFactory::AutoRegister<TemplateNodeCreate>    autoRefCreate("create");
4158 static TemplateNodeFactory::AutoRegister<TemplateNodeRepeat>    autoRefRepeat("repeat");
4159 static TemplateNodeFactory::AutoRegister<TemplateNodeInclude>   autoRefInclude("include");
4160 static TemplateNodeFactory::AutoRegister<TemplateNodeMarkers>   autoRefMarkers("markers");
4161 static TemplateNodeFactory::AutoRegister<TemplateNodeSpaceless> autoRefSpaceless("spaceless");
4162 static TemplateNodeFactory::AutoRegister<TemplateNodeIndexEntry> autoRefIndexEntry("indexentry");
4163 static TemplateNodeFactory::AutoRegister<TemplateNodeOpenSubIndex> autoRefOpenSubIndex("opensubindex");
4164 static TemplateNodeFactory::AutoRegister<TemplateNodeCloseSubIndex> autoRefCloseSubIndex("closesubindex");
4165
4166 //----------------------------------------------------------
4167
4168 TemplateBlockContext::TemplateBlockContext() : m_blocks(257)
4169 {
4170   m_blocks.setAutoDelete(TRUE);
4171 }
4172
4173 TemplateNodeBlock *TemplateBlockContext::get(const QCString &name) const
4174 {
4175   QList<TemplateNodeBlock> *list = m_blocks.find(name);
4176   if (list==0 || list->count()==0)
4177   {
4178     return 0;
4179   }
4180   else
4181   {
4182     return list->getLast();
4183   }
4184 }
4185
4186 TemplateNodeBlock *TemplateBlockContext::pop(const QCString &name) const
4187 {
4188   QList<TemplateNodeBlock> *list = m_blocks.find(name);
4189   if (list==0 || list->count()==0)
4190   {
4191     return 0;
4192   }
4193   else
4194   {
4195     return list->take(list->count()-1);
4196   }
4197 }
4198
4199 void TemplateBlockContext::add(TemplateNodeBlock *block)
4200 {
4201   QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
4202   if (list==0)
4203   {
4204     list = new QList<TemplateNodeBlock>;
4205     m_blocks.insert(block->name(),list);
4206   }
4207   list->prepend(block);
4208 }
4209
4210 void TemplateBlockContext::add(TemplateBlockContext *ctx)
4211 {
4212   QDictIterator< QList<TemplateNodeBlock> > di(ctx->m_blocks);
4213   QList<TemplateNodeBlock> *list;
4214   for (di.toFirst();(list=di.current());++di)
4215   {
4216     QListIterator<TemplateNodeBlock> li(*list);
4217     TemplateNodeBlock *nb;
4218     for (li.toFirst();(nb=li.current());++li)
4219     {
4220       add(nb);
4221     }
4222   }
4223 }
4224
4225 void TemplateBlockContext::clear()
4226 {
4227   m_blocks.clear();
4228 }
4229
4230 void TemplateBlockContext::push(TemplateNodeBlock *block)
4231 {
4232   QList<TemplateNodeBlock> *list = m_blocks.find(block->name());
4233   if (list==0)
4234   {
4235     list = new QList<TemplateNodeBlock>;
4236     m_blocks.insert(block->name(),list);
4237   }
4238   list->append(block);
4239 }
4240
4241
4242 //----------------------------------------------------------
4243
4244 /** @brief Lexer class for turning a template into a list of tokens */
4245 class TemplateLexer
4246 {
4247   public:
4248     TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data);
4249     void tokenize(QList<TemplateToken> &tokens);
4250   private:
4251     void addToken(QList<TemplateToken> &tokens,
4252                   const char *data,int line,int startPos,int endPos,
4253                   TemplateToken::Type type);
4254     void reset();
4255     const TemplateEngine *m_engine;
4256     QCString m_fileName;
4257     QCString m_data;
4258 };
4259
4260 TemplateLexer::TemplateLexer(const TemplateEngine *engine,const QCString &fileName,const QCString &data) :
4261   m_engine(engine), m_fileName(fileName), m_data(data)
4262 {
4263 }
4264
4265 void TemplateLexer::tokenize(QList<TemplateToken> &tokens)
4266 {
4267   enum LexerStates
4268   {
4269     StateText,
4270     StateBeginTemplate,
4271     StateTag,
4272     StateEndTag,
4273     StateComment,
4274     StateEndComment,
4275     StateMaybeVar,
4276     StateVariable,
4277     StateEndVariable
4278   };
4279
4280   const char *p=m_data.data();
4281   int  state=StateText;
4282   int  pos=0;
4283   int  lastTokenPos=0;
4284   int  startLinePos=0;
4285   bool emptyOutputLine=TRUE;
4286   int  line=1;
4287   char c;
4288   int  markStartPos=-1;
4289   for (;(c=*p);p++,pos++)
4290   {
4291     switch (state)
4292     {
4293       case StateText:
4294         if (c=='{') // {{ or {% or {# or something else
4295         {
4296           state=StateBeginTemplate;
4297         }
4298         else if (c!=' ' && c!='\t' && c!='\n') // non-whitepace text
4299         {
4300           emptyOutputLine=FALSE;
4301         }
4302         break;
4303       case StateBeginTemplate:
4304         switch (c)
4305         {
4306           case '%': // {%
4307             state=StateTag;
4308             markStartPos=pos-1;
4309             break;
4310           case '#': // {#
4311             state=StateComment;
4312             markStartPos=pos-1;
4313             break;
4314           case '{': // {{
4315             state=StateMaybeVar;
4316             markStartPos=pos-1;
4317             break;
4318           default:
4319             state=StateText;
4320             emptyOutputLine=FALSE;
4321             break;
4322         }
4323         break;
4324       case StateTag:
4325         if (c=='\n')
4326         {
4327           warn(m_fileName,line,"unexpected new line inside {%%...%%} block");
4328           m_engine->printIncludeContext(m_fileName,line);
4329         }
4330         else if (c=='%') // %} or something else
4331         {
4332           state=StateEndTag;
4333         }
4334         break;
4335       case StateEndTag:
4336         if (c=='}') // %}
4337         {
4338           // found tag!
4339           state=StateText;
4340           addToken(tokens,m_data.data(),line,lastTokenPos,
4341                    emptyOutputLine ? startLinePos : markStartPos,
4342                    TemplateToken::Text);
4343           addToken(tokens,m_data.data(),line,markStartPos+2,
4344                    pos-1,TemplateToken::Block);
4345           lastTokenPos = pos+1;
4346         }
4347         else // something else
4348         {
4349           if (c=='\n')
4350           {
4351             warn(m_fileName,line,"unexpected new line inside {%%...%%} block");
4352             m_engine->printIncludeContext(m_fileName,line);
4353           }
4354           state=StateTag;
4355         }
4356         break;
4357       case StateComment:
4358         if (c=='\n')
4359         {
4360           warn(m_fileName,line,"unexpected new line inside {#...#} block");
4361           m_engine->printIncludeContext(m_fileName,line);
4362         }
4363         else if (c=='#') // #} or something else
4364         {
4365           state=StateEndComment;
4366         }
4367         break;
4368       case StateEndComment:
4369         if (c=='}') // #}
4370         {
4371           // found comment tag!
4372           state=StateText;
4373           addToken(tokens,m_data.data(),line,lastTokenPos,
4374                    emptyOutputLine ? startLinePos : markStartPos,
4375                    TemplateToken::Text);
4376           lastTokenPos = pos+1;
4377         }
4378         else // something else
4379         {
4380           if (c=='\n')
4381           {
4382             warn(m_fileName,line,"unexpected new line inside {#...#} block");
4383             m_engine->printIncludeContext(m_fileName,line);
4384           }
4385           state=StateComment;
4386         }
4387         break;
4388       case StateMaybeVar:
4389         switch (c)
4390         {
4391           case '#': // {{#
4392             state=StateComment;
4393             markStartPos=pos-1;
4394             break;
4395           case '%': // {{%
4396             state=StateTag;
4397             markStartPos=pos-1;
4398             break;
4399           default:  // {{
4400             state=StateVariable;
4401             break;
4402         }
4403         break;
4404       case StateVariable:
4405         if (c=='\n')
4406         {
4407           warn(m_fileName,line,"unexpected new line inside {{...}} block");
4408           m_engine->printIncludeContext(m_fileName,line);
4409         }
4410         else if (c=='}') // }} or something else
4411         {
4412           state=StateEndVariable;
4413         }
4414         break;
4415       case StateEndVariable:
4416         if (c=='}') // }}
4417         {
4418           // found variable tag!
4419           state=StateText;
4420           addToken(tokens,m_data.data(),line,lastTokenPos,
4421                    emptyOutputLine ? startLinePos : markStartPos,
4422                    TemplateToken::Text);
4423           addToken(tokens,m_data.data(),line,markStartPos+2,
4424                    pos-1,TemplateToken::Variable);
4425           lastTokenPos = pos+1;
4426         }
4427         else // something else
4428         {
4429           if (c=='\n')
4430           {
4431             warn(m_fileName,line,"unexpected new line inside {{...}} block");
4432             m_engine->printIncludeContext(m_fileName,line);
4433           }
4434           state=StateVariable;
4435         }
4436         break;
4437     }
4438     if (c=='\n') // new line
4439     {
4440       state=StateText;
4441       startLinePos=pos+1;
4442       // if the current line only contain commands and whitespace,
4443       // then skip it in the output by moving lastTokenPos
4444       if (markStartPos!=-1 && emptyOutputLine) lastTokenPos = startLinePos;
4445       // reset markers
4446       markStartPos=-1;
4447       line++;
4448       emptyOutputLine=TRUE;
4449     }
4450   }
4451   if (lastTokenPos<pos)
4452   {
4453     addToken(tokens,m_data.data(),line,
4454              lastTokenPos,pos,
4455              TemplateToken::Text);
4456   }
4457 }
4458
4459 void TemplateLexer::addToken(QList<TemplateToken> &tokens,
4460                              const char *data,int line,
4461                              int startPos,int endPos,
4462                              TemplateToken::Type type)
4463 {
4464   if (startPos<endPos)
4465   {
4466     int len = endPos-startPos+1;
4467     QCString text(len+1);
4468     qstrncpy(text.data(),data+startPos,len);
4469     text[len]='\0';
4470     if (type!=TemplateToken::Text) text = text.stripWhiteSpace();
4471     tokens.append(new TemplateToken(type,text,line));
4472   }
4473 }
4474
4475 //----------------------------------------------------------
4476
4477 TemplateParser::TemplateParser(const TemplateEngine *engine,
4478                                const QCString &templateName,
4479                                QList<TemplateToken> &tokens) :
4480    m_engine(engine), m_templateName(templateName), m_tokens(tokens)
4481 {
4482 }
4483
4484 void TemplateParser::parse(
4485                      TemplateNode *parent,int line,const QStrList &stopAt,
4486                      QList<TemplateNode> &nodes)
4487 {
4488   TRACE(("{TemplateParser::parse\n"));
4489   // process the tokens. Build node list
4490   while (hasNextToken())
4491   {
4492     TemplateToken *tok = takeNextToken();
4493     //printf("%p:Token type=%d data='%s' line=%d\n",
4494     //       parent,tok->type,tok->data.data(),tok->line);
4495     switch(tok->type)
4496     {
4497       case TemplateToken::Text:
4498         nodes.append(new TemplateNodeText(this,parent,tok->line,tok->data));
4499         break;
4500       case TemplateToken::Variable: // {{ var }}
4501         nodes.append(new TemplateNodeVariable(this,parent,tok->line,tok->data));
4502         break;
4503       case TemplateToken::Block:    // {% tag %}
4504         {
4505           QCString command = tok->data;
4506           int sep = command.find(' ');
4507           if (sep!=-1)
4508           {
4509             command=command.left(sep);
4510           }
4511           if (stopAt.contains(command))
4512           {
4513             prependToken(tok);
4514             TRACE(("}TemplateParser::parse: stop\n"));
4515             return;
4516           }
4517           QCString arg;
4518           if (sep!=-1)
4519           {
4520             arg = tok->data.mid(sep+1);
4521           }
4522           TemplateNode *node = TemplateNodeFactory::instance()->
4523                                create(command,this,parent,tok->line,arg);
4524           if (node)
4525           {
4526             nodes.append(node);
4527           }
4528           else if (command=="empty"          || command=="else"         ||
4529                    command=="endif"          || command=="endfor"       ||
4530                    command=="endblock"       || command=="endwith"      ||
4531                    command=="endrecursetree" || command=="endspaceless" ||
4532                    command=="endmarkers"     || command=="endmsg"       ||
4533                    command=="endrepeat"      || command=="elif"         ||
4534                    command=="endrange")
4535           {
4536             warn(m_templateName,tok->line,"Found tag '%s' without matching start tag",command.data());
4537           }
4538           else
4539           {
4540             warn(m_templateName,tok->line,"Unknown tag '%s'",command.data());
4541           }
4542         }
4543         break;
4544     }
4545     delete tok;
4546   }
4547   if (!stopAt.isEmpty())
4548   {
4549     QStrListIterator it(stopAt);
4550     const char *s;
4551     QCString options;
4552     for (it.toFirst();(s=it.current());++it)
4553     {
4554       if (!options.isEmpty()) options+=", ";
4555       options+=s;
4556     }
4557     warn(m_templateName,line,"Unclosed tag in template, expected one of: %s",
4558         options.data());
4559   }
4560   TRACE(("}TemplateParser::parse: last token\n"));
4561 }
4562
4563 bool TemplateParser::hasNextToken() const
4564 {
4565   return !m_tokens.isEmpty();
4566 }
4567
4568 TemplateToken *TemplateParser::takeNextToken()
4569 {
4570   return m_tokens.take(0);
4571 }
4572
4573 const TemplateToken *TemplateParser::currentToken() const
4574 {
4575   return m_tokens.getFirst();
4576 };
4577
4578 void TemplateParser::removeNextToken()
4579 {
4580   m_tokens.removeFirst();
4581 }
4582
4583 void TemplateParser::prependToken(const TemplateToken *token)
4584 {
4585   m_tokens.prepend(token);
4586 }
4587
4588 void TemplateParser::warn(const char *fileName,int line,const char *fmt,...) const
4589 {
4590   va_list args;
4591   va_start(args,fmt);
4592   va_warn(fileName,line,fmt,args);
4593   va_end(args);
4594   m_engine->printIncludeContext(fileName,line);
4595 }
4596
4597
4598
4599 //----------------------------------------------------------
4600
4601
4602 TemplateImpl::TemplateImpl(TemplateEngine *engine,const QCString &name,const QCString &data)
4603   : TemplateNode(0)
4604 {
4605   m_name = name;
4606   m_engine = engine;
4607   TemplateLexer lexer(engine,name,data);
4608   QList<TemplateToken> tokens;
4609   tokens.setAutoDelete(TRUE);
4610   lexer.tokenize(tokens);
4611   TemplateParser parser(engine,name,tokens);
4612   parser.parse(this,1,QStrList(),m_nodes);
4613 }
4614
4615 void TemplateImpl::render(FTextStream &ts, TemplateContext *c)
4616 {
4617   if (!m_nodes.isEmpty())
4618   {
4619     TemplateNodeExtend *ne = dynamic_cast<TemplateNodeExtend*>(m_nodes.getFirst());
4620     if (ne==0) // normal template, add blocks to block context
4621     {
4622       TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
4623       TemplateBlockContext *bc = ci->blockContext();
4624       QListIterator<TemplateNode> li(m_nodes);
4625       TemplateNode *n;
4626       for (li.toFirst();(n=li.current());++li)
4627       {
4628         TemplateNodeBlock *nb = dynamic_cast<TemplateNodeBlock*>(n);
4629         if (nb)
4630         {
4631           bc->add(nb);
4632         }
4633       }
4634     }
4635     m_nodes.render(ts,c);
4636   }
4637 }
4638
4639 //----------------------------------------------------------
4640
4641 /** @brief Private data of the template engine */
4642 class TemplateEngine::Private
4643 {
4644     class IncludeEntry
4645     {
4646       public:
4647         enum Type { Template, Block };
4648         IncludeEntry(Type type,const QCString &fileName,const QCString &blockName,int line)
4649           : m_type(type), m_fileName(fileName), m_blockName(blockName), m_line(line) {}
4650         Type type() const { return m_type; }
4651         QCString fileName() const { return m_fileName; }
4652         QCString blockName() const { return m_blockName; }
4653         int line() const { return m_line; }
4654
4655       private:
4656         Type m_type;
4657         QCString m_fileName;
4658         QCString m_blockName;
4659         int m_line;
4660     };
4661   public:
4662     Private(TemplateEngine *engine) : m_templateCache(17) /*, m_indent(0)*/, m_engine(engine)
4663     {
4664       m_templateCache.setAutoDelete(TRUE);
4665       m_includeStack.setAutoDelete(TRUE);
4666     }
4667     Template *loadByName(const QCString &fileName,int line)
4668     {
4669       //for (int i=0;i<m_indent;i++) printf("  ");
4670       //m_indent++;
4671       //printf("loadByName(%s,%d) {\n",fileName.data(),line);
4672       m_includeStack.append(new IncludeEntry(IncludeEntry::Template,fileName,QCString(),line));
4673       Template *templ = m_templateCache.find(fileName);
4674       if (templ==0)
4675       {
4676         QFile f(fileName);
4677         if (f.open(IO_ReadOnly))
4678         {
4679           uint size=f.size();
4680           char *data = new char[size+1];
4681           if (data)
4682           {
4683             data[size]=0;
4684             if (f.readBlock(data,f.size()))
4685             {
4686               templ = new TemplateImpl(m_engine,fileName,data);
4687               m_templateCache.insert(fileName,templ);
4688             }
4689             delete[] data;
4690           }
4691         }
4692         else
4693         {
4694           err("Cound not open template file %s\n",fileName.data());
4695         }
4696       }
4697       return templ;
4698     }
4699     void unload(Template * /*t*/)
4700     {
4701       //(void)t;
4702       //m_indent--;
4703       //for (int i=0;i<m_indent;i++) printf("  ");
4704       //printf("}\n");
4705       m_includeStack.removeLast();
4706     }
4707
4708     void enterBlock(const QCString &fileName,const QCString &blockName,int line)
4709     {
4710       //for (int i=0;i<m_indent;i++) printf("  ");
4711       //m_indent++;
4712       //printf("enterBlock(%s,%s,%d) {\n",fileName.data(),blockName.data(),line);
4713       m_includeStack.append(new IncludeEntry(IncludeEntry::Block,fileName,blockName,line));
4714     }
4715
4716     void leaveBlock()
4717     {
4718       //m_indent--;
4719       //for (int i=0;i<m_indent;i++) printf("  ");
4720       //printf("}\n");
4721       m_includeStack.removeLast();
4722     }
4723
4724     void printIncludeContext(const char *fileName,int line) const
4725     {
4726       QListIterator<IncludeEntry> li(m_includeStack);
4727       li.toLast();
4728       IncludeEntry *ie=li.current();
4729       while ((ie=li.current()))
4730       {
4731         --li;
4732         IncludeEntry *next=li.current();
4733         if (ie->type()==IncludeEntry::Template)
4734         {
4735           if (next)
4736           {
4737             warn(fileName,line,"  inside template '%s' included from template '%s' at line %d",ie->fileName().data(),next->fileName().data(),ie->line());
4738           }
4739         }
4740         else // ie->type()==IncludeEntry::Block
4741         {
4742           warn(fileName,line,"  included by block '%s' inside template '%s' at line %d",ie->blockName().data(),
4743               ie->fileName().data(),ie->line());
4744         }
4745       }
4746     }
4747
4748   private:
4749     QDict<Template> m_templateCache;
4750     //mutable int m_indent;
4751     TemplateEngine *m_engine;
4752     QList<IncludeEntry> m_includeStack;
4753 };
4754
4755 TemplateEngine::TemplateEngine()
4756 {
4757   p = new Private(this);
4758 }
4759
4760 TemplateEngine::~TemplateEngine()
4761 {
4762   delete p;
4763 }
4764
4765 TemplateContext *TemplateEngine::createContext() const
4766 {
4767   return new TemplateContextImpl(this);
4768 }
4769
4770 void TemplateEngine::destroyContext(TemplateContext *ctx)
4771 {
4772   delete ctx;
4773 }
4774
4775 Template *TemplateEngine::loadByName(const QCString &fileName,int line)
4776 {
4777   return p->loadByName(fileName,line);
4778 }
4779
4780 void TemplateEngine::unload(Template *t)
4781 {
4782   p->unload(t);
4783 }
4784
4785 void TemplateEngine::enterBlock(const QCString &fileName,const QCString &blockName,int line)
4786 {
4787   p->enterBlock(fileName,blockName,line);
4788 }
4789
4790 void TemplateEngine::leaveBlock()
4791 {
4792   p->leaveBlock();
4793 }
4794
4795 void TemplateEngine::printIncludeContext(const char *fileName,int line) const
4796 {
4797   p->printIncludeContext(fileName,line);
4798 }
4799