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