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