Fix for UBSan build
[platform/upstream/doxygen.git] / src / tagreader.cpp
1 /******************************************************************************
2  *
3  * $Id: tagreader.cpp,v 1.2 2001/03/19 19:27:41 root Exp $
4  *
5  *
6  * Copyright (C) 1997-2012 by Dimitri van Heesch.
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation under the terms of the GNU General Public License is hereby 
10  * granted. No representations are made about the suitability of this software 
11  * for any purpose. It is provided "as is" without express or implied warranty.
12  * See the GNU General Public License for more details.
13  *
14  * Documents produced by Doxygen are derivative works derived from the
15  * input used in their production; they are not affected by this license.
16  *
17  */
18
19 #include "tagreader.h"
20
21 #include <stdio.h>
22 #include <stdarg.h>
23
24 #include <qxml.h>
25 #include <qstack.h>
26 #include <qdict.h>
27 #include <qfileinfo.h>
28 #include <qlist.h>
29 #include <qstring.h>
30 #include <qstringlist.h>
31
32 #include "entry.h"
33 #include "classdef.h"
34 #include "doxygen.h"
35 #include "util.h"
36 #include "message.h"
37 #include "defargs.h"
38 #include "arguments.h"
39 //#include "reflist.h"
40
41 /** Information about an linkable anchor */
42 class TagAnchorInfo
43 {
44   public:
45     TagAnchorInfo(const QCString &f,const QCString &l) : label(l), fileName(f) {}
46     QCString label;
47     QCString fileName;
48 };
49
50 /** List of TagAnchorInfo objects. */
51 class TagAnchorInfoList : public QList<TagAnchorInfo>
52 {
53   public: 
54     TagAnchorInfoList() : QList<TagAnchorInfo>() { setAutoDelete(TRUE); }
55     virtual ~TagAnchorInfoList() {}
56 };
57
58 /** Container for member specific info that can be read from a tagfile */
59 class TagMemberInfo
60 {
61   public:
62     TagMemberInfo() : prot(Public), virt(Normal), isStatic(FALSE) {}
63     QCString type;
64     QCString name;
65     QCString anchorFile;
66     QCString anchor;
67     QCString arglist;
68     QCString kind;
69     TagAnchorInfoList docAnchors;
70     Protection prot;
71     Specifier virt;
72     bool isStatic; 
73 };
74
75 /** Container for class specific info that can be read from a tagfile */
76 class TagClassInfo
77 {
78   public:
79     enum Kind { Class, Struct, Union, Interface, Exception, Protocol, Category };
80     TagClassInfo() { bases=0, templateArguments=0; members.setAutoDelete(TRUE); isObjC=FALSE; }
81    ~TagClassInfo() { delete bases; delete templateArguments; }
82     QCString name;
83     QCString filename;
84     TagAnchorInfoList docAnchors;
85     QList<BaseInfo> *bases;
86     QList<TagMemberInfo> members;
87     QList<QCString> *templateArguments;
88     QStringList classList;
89     Kind kind;
90     bool isObjC;
91 };
92
93 /** Container for namespace specific info that can be read from a tagfile */
94 class TagNamespaceInfo
95 {
96   public:
97     TagNamespaceInfo() { members.setAutoDelete(TRUE); }
98     QCString name;
99     QCString filename;
100     QStringList classList;
101     QStringList namespaceList;
102     TagAnchorInfoList docAnchors;
103     QList<TagMemberInfo> members;
104 };
105
106 /** Container for package specific info that can be read from a tagfile */
107 class TagPackageInfo
108 {
109   public:
110     TagPackageInfo() { members.setAutoDelete(TRUE); }
111     QCString name;
112     QCString filename;
113     TagAnchorInfoList docAnchors;
114     QList<TagMemberInfo> members;
115     QStringList classList;
116 };
117
118 /** Container for include info that can be read from a tagfile */
119 class TagIncludeInfo
120 {
121   public:
122     QCString id;
123     QCString name;
124     QCString text;
125     bool isLocal;
126     bool isImported;
127 };
128
129 /** Container for file specific info that can be read from a tagfile */
130 class TagFileInfo
131 {
132   public:
133     TagFileInfo() { members.setAutoDelete(TRUE); includes.setAutoDelete(TRUE); }
134     QCString name;
135     QCString path;
136     QCString filename;
137     TagAnchorInfoList docAnchors;
138     QList<TagMemberInfo> members;
139     QStringList classList;
140     QStringList namespaceList;
141     QList<TagIncludeInfo> includes;
142 };
143
144 /** Container for group specific info that can be read from a tagfile */
145 class TagGroupInfo
146 {
147   public:
148     TagGroupInfo() { members.setAutoDelete(TRUE); }
149     QCString name;
150     QCString title;
151     QCString filename;
152     TagAnchorInfoList docAnchors;
153     QList<TagMemberInfo> members;
154     QStringList subgroupList;
155     QStringList classList;
156     QStringList namespaceList;
157     QStringList fileList;
158     QStringList pageList;
159     QStringList dirList;
160 };
161
162 /** Container for page specific info that can be read from a tagfile */
163 class TagPageInfo
164 {
165   public:
166     QCString name;
167     QCString title;
168     QCString filename;
169     TagAnchorInfoList docAnchors;
170 };
171
172 /** Container for directory specific info that can be read from a tagfile */
173 class TagDirInfo
174 {
175   public:
176     QCString name;
177     QCString filename;
178     QCString path;
179     QStringList subdirList;
180     QStringList fileList;
181     TagAnchorInfoList docAnchors;
182 };
183
184 /** Tag file parser. 
185  *
186  *  Reads an XML-structured tagfile and builds up the structure in
187  *  memory. The method buildLists() is used to transfer/translate 
188  *  the structures to the doxygen engine.
189  */
190 class TagFileParser : public QXmlDefaultHandler
191 {
192     enum State { Invalid,
193                  InClass,
194                  InFile,
195                  InNamespace,
196                  InGroup,
197                  InPage,
198                  InMember,
199                  InPackage,
200                  InDir,
201                  InTempArgList
202                };
203     class StartElementHandler
204     {
205         typedef void (TagFileParser::*Handler)(const QXmlAttributes &attrib); 
206       public:
207         StartElementHandler(TagFileParser *parent, Handler h) : m_parent(parent), m_handler(h) {}
208         void operator()(const QXmlAttributes &attrib) { (m_parent->*m_handler)(attrib); }
209       private:
210         TagFileParser *m_parent;
211         Handler m_handler;
212     };
213
214     class EndElementHandler
215     {
216         typedef void (TagFileParser::*Handler)(); 
217       public:
218         EndElementHandler(TagFileParser *parent, Handler h) : m_parent(parent), m_handler(h) {}
219         void operator()() { (m_parent->*m_handler)(); }
220       private:
221         TagFileParser *m_parent;
222         Handler m_handler;
223     };
224
225   public:
226     TagFileParser(const char *tagName) : m_startElementHandlers(17),
227                                          m_endElementHandlers(17),
228                                          m_tagName(tagName)
229     {
230       m_startElementHandlers.setAutoDelete(TRUE);
231       m_endElementHandlers.setAutoDelete(TRUE);
232     }
233     
234     void setDocumentLocator ( QXmlLocator * locator )
235     {
236       m_locator = locator;
237     }
238
239     void setFileName( const QString &fileName )
240     {
241       m_inputFileName = fileName.utf8();
242     }
243
244     void warn(const char *fmt)
245     {
246       ::warn(m_inputFileName,m_locator->lineNumber(),fmt);
247     }
248     void warn(const char *fmt,const char *s)
249     {
250       ::warn(m_inputFileName,m_locator->lineNumber(),fmt,s);
251     }
252
253     void startCompound( const QXmlAttributes& attrib )
254     {
255       m_curString = "";
256       QString kind = attrib.value("kind");
257       QString isObjC = attrib.value("objc");
258       if (kind=="class")
259       {
260         m_curClass = new TagClassInfo;
261         m_curClass->kind = TagClassInfo::Class;
262         m_state = InClass;
263       }
264       else if (kind=="struct")
265       {
266         m_curClass = new TagClassInfo;
267         m_curClass->kind = TagClassInfo::Struct;
268         m_state = InClass;
269       }
270       else if (kind=="union")
271       {
272         m_curClass = new TagClassInfo;
273         m_curClass->kind = TagClassInfo::Union;
274         m_state = InClass;
275       }
276       else if (kind=="interface")
277       {
278         m_curClass = new TagClassInfo;
279         m_curClass->kind = TagClassInfo::Interface;
280         m_state = InClass;
281       }
282       else if (kind=="exception")
283       {
284         m_curClass = new TagClassInfo;
285         m_curClass->kind = TagClassInfo::Exception;
286         m_state = InClass;
287       }
288       else if (kind=="protocol")
289       {
290         m_curClass = new TagClassInfo;
291         m_curClass->kind = TagClassInfo::Protocol;
292         m_state = InClass;
293       }
294       else if (kind=="category")
295       {
296         m_curClass = new TagClassInfo;
297         m_curClass->kind = TagClassInfo::Category;
298         m_state = InClass;
299       }
300       else if (kind=="file")
301       {
302         m_curFile = new TagFileInfo;
303         m_state = InFile;
304       }
305       else if (kind=="namespace")
306       {
307         m_curNamespace = new TagNamespaceInfo;
308         m_state = InNamespace;
309       }
310       else if (kind=="group")
311       {
312         m_curGroup = new TagGroupInfo;
313         m_state = InGroup;
314       }
315       else if (kind=="page")
316       {
317         m_curPage = new TagPageInfo;
318         m_state = InPage;
319       }
320       else if (kind=="package")
321       {
322         m_curPackage = new TagPackageInfo;
323         m_state = InPackage;
324       }
325       else if (kind=="dir")
326       {
327         m_curDir = new TagDirInfo;
328         m_state = InDir;
329       }
330       else
331       {
332         warn("warning: Unknown compound attribute `%s' found!\n",kind.data());
333         m_state = Invalid;
334       }
335       if (isObjC=="yes" && m_curClass)
336       {
337         m_curClass->isObjC = TRUE; 
338       }
339     }
340
341     void endCompound()
342     {
343       switch (m_state)
344       {
345         case InClass:     m_tagFileClasses.append(m_curClass); 
346                           m_curClass=0; break; 
347         case InFile:      m_tagFileFiles.append(m_curFile); 
348                           m_curFile=0; break; 
349         case InNamespace: m_tagFileNamespaces.append(m_curNamespace); 
350                           m_curNamespace=0; break; 
351         case InGroup:     m_tagFileGroups.append(m_curGroup); 
352                           m_curGroup=0; break; 
353         case InPage:      m_tagFilePages.append(m_curPage); 
354                           m_curPage=0; break; 
355         case InDir:       m_tagFileDirs.append(m_curDir);
356                           m_curDir=0; break;
357         case InPackage:   m_tagFilePackages.append(m_curPackage); 
358                           m_curPackage=0; break; 
359         default:
360                           warn("warning: tag `compound' was not expected!\n");
361       }
362     }
363
364     void startMember( const QXmlAttributes& attrib)
365     {
366       m_curMember = new TagMemberInfo;
367       m_curMember->kind = attrib.value("kind").utf8();
368       QCString protStr   = attrib.value("protection").utf8();
369       QCString virtStr   = attrib.value("virtualness").utf8();
370       QCString staticStr = attrib.value("static").utf8();
371       if (protStr=="protected")
372       {
373         m_curMember->prot = Protected;
374       }
375       else if (protStr=="private")
376       {
377         m_curMember->prot = Private;
378       }
379       if (virtStr=="virtual")
380       {
381         m_curMember->virt = Virtual;
382       }
383       else if (virtStr=="pure")
384       {
385         m_curMember->virt = Pure;
386       }
387       if (staticStr=="yes")
388       {
389         m_curMember->isStatic = TRUE;
390       }
391       m_stateStack.push(new State(m_state));
392       m_state = InMember;
393     }
394
395     void endMember()
396     {
397       m_state = *m_stateStack.top();
398       m_stateStack.remove();
399       switch(m_state)
400       {
401         case InClass:     m_curClass->members.append(m_curMember); break;
402         case InFile:      m_curFile->members.append(m_curMember); break;
403         case InNamespace: m_curNamespace->members.append(m_curMember); break;
404         case InGroup:     m_curGroup->members.append(m_curMember); break;
405         case InPackage:   m_curPackage->members.append(m_curMember); break;
406         default:   warn("warning: Unexpected tag `member' found\n"); break; 
407       }
408     }
409
410     void endDocAnchor()
411     {
412       switch(m_state)
413       {
414         case InClass:     m_curClass->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
415         case InFile:      m_curFile->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
416         case InNamespace: m_curNamespace->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
417         case InGroup:     m_curGroup->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
418         case InPage:      m_curPage->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
419         case InMember:    m_curMember->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
420         case InPackage:   m_curPackage->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
421         case InDir:       m_curDir->docAnchors.append(new TagAnchorInfo(m_fileName,m_curString)); break;
422         default:   warn("warning: Unexpected tag `member' found\n"); break; 
423       }
424     }
425
426     void endClass()
427     {
428       switch(m_state)
429       {
430         case InClass:     m_curClass->classList.append(m_curString); break;
431         case InFile:      m_curFile->classList.append(m_curString); break;
432         case InNamespace: m_curNamespace->classList.append(m_curString); break;
433         case InGroup:     m_curGroup->classList.append(m_curString); break;
434         case InPackage:   m_curPackage->classList.append(m_curString); break;
435         default:   warn("warning: Unexpected tag `class' found\n"); break; 
436       }
437     }
438
439     void endNamespace()
440     {
441       switch(m_state)
442       {
443         case InNamespace: m_curNamespace->classList.append(m_curString); break;
444         case InFile:      m_curFile->namespaceList.append(m_curString); break;
445         case InGroup:     m_curGroup->namespaceList.append(m_curString); break;
446         default:   warn("warning: Unexpected tag `namespace' found\n"); break; 
447       }
448     }
449
450     void endFile()
451     {
452       switch(m_state)
453       {
454         case InGroup:      m_curGroup->fileList.append(m_curString); break;
455         case InDir:        m_curDir->fileList.append(m_curString); break;
456         default:   warn("warning: Unexpected tag `file' found\n"); break; 
457       }
458     }
459
460     void endPage()
461     {
462       switch(m_state)
463       {
464         case InGroup:      m_curGroup->fileList.append(m_curString); break;
465         default:   warn("warning: Unexpected tag `page' found\n"); break; 
466       }
467     }
468
469     void endDir()
470     {
471       switch(m_state)
472       {
473         case InDir:      m_curDir->subdirList.append(m_curString); break;
474         default:   warn("warning: Unexpected tag `page' found\n"); break; 
475       }
476     }
477
478     void startStringValue(const QXmlAttributes& )
479     {
480       m_curString = "";
481     }
482
483     void startDocAnchor(const QXmlAttributes& attrib )
484     {
485       m_fileName = attrib.value("file").utf8();
486       m_curString = "";
487     }
488
489     void endType()
490     {
491       if (m_state==InMember)
492       {
493         m_curMember->type = m_curString; 
494       }
495       else
496       {
497         warn("warning: Unexpected tag `type' found\n");
498       }
499     }
500
501     void endName()
502     {
503       switch (m_state)
504       {
505         case InClass:     m_curClass->name     = m_curString; break;
506         case InFile:      m_curFile->name      = m_curString; break;
507         case InNamespace: m_curNamespace->name = m_curString; break;
508         case InGroup:     m_curGroup->name     = m_curString; break;
509         case InPage:      m_curPage->name      = m_curString; break;
510         case InDir:       m_curDir->name       = m_curString; break;
511         case InMember:    m_curMember->name    = m_curString; break;
512         case InPackage:   m_curPackage->name   = m_curString; break;
513         default: warn("warning: Unexpected tag `name' found\n"); break; 
514       }
515     }
516
517     void startBase(const QXmlAttributes& attrib )
518     {
519       m_curString="";
520       if (m_state==InClass && m_curClass)
521       {
522         QString protStr = attrib.value("protection");
523         QString virtStr = attrib.value("virtualness");
524         Protection prot = Public;
525         Specifier  virt = Normal;
526         if (protStr=="protected")
527         {
528           prot = Protected;
529         }
530         else if (protStr=="private")
531         {
532           prot = Private;
533         }
534         if (virtStr=="virtual")
535         {
536           virt = Virtual;
537         }
538         if (m_curClass->bases==0) 
539         {
540           m_curClass->bases = new QList<BaseInfo>;
541           m_curClass->bases->setAutoDelete(TRUE);
542         }
543         m_curClass->bases->append(new BaseInfo(m_curString,prot,virt));
544       }
545       else
546       {
547         warn("warning: Unexpected tag `base' found\n");
548       }
549     }
550
551     void endBase()
552     {
553       if (m_state==InClass && m_curClass)
554       {
555         m_curClass->bases->getLast()->name = m_curString;
556       }
557       else
558       {
559         warn("warning: Unexpected tag `base' found\n");
560       }
561     }
562
563     void startIncludes(const QXmlAttributes& attrib )
564     {
565       if (m_state==InFile && m_curFile)
566       {
567         m_curIncludes = new TagIncludeInfo;
568         m_curIncludes->id = attrib.value("id").utf8();
569         m_curIncludes->name = attrib.value("name").utf8();
570         m_curIncludes->isLocal = attrib.value("local").utf8()=="yes" ? TRUE : FALSE;
571         m_curIncludes->isImported = attrib.value("imported").utf8()=="yes" ? TRUE : FALSE;
572         m_curFile->includes.append(m_curIncludes);
573       }
574       else
575       {
576         warn("warning: Unexpected tag `includes' found\n");
577       }
578       m_curString="";
579     }
580
581     void endIncludes()
582     {
583       m_curIncludes->text = m_curString;
584     }
585
586     void endTemplateArg()
587     {
588       if (m_state==InClass && m_curClass)
589       {
590         if (m_curClass->templateArguments==0) 
591         {
592           m_curClass->templateArguments = new QList<QCString>;
593           m_curClass->templateArguments->setAutoDelete(TRUE);
594         }
595         m_curClass->templateArguments->append(new QCString(m_curString));
596       }
597       else
598       {
599         warn("warning: Unexpected tag `templarg' found\n");
600       }
601     }
602
603     void endFilename()
604     {
605       switch (m_state)
606       {
607         case InClass:     m_curClass->filename     = m_curString;    break;
608         case InNamespace: m_curNamespace->filename = m_curString;    break;
609         case InFile:      m_curFile->filename      = m_curString;    break;
610         case InGroup:     m_curGroup->filename     = m_curString;    break;
611         case InPage:      m_curPage->filename      = m_curString;    break;
612         case InPackage:   m_curPackage->filename   = m_curString;    break;
613         case InDir:       m_curDir->filename       = m_curString;    break;
614         default: warn("warning: Unexpected tag `filename' found\n"); break; 
615       }
616     }
617
618     void endPath()
619     {
620       switch (m_state)
621       {
622         case InFile:      m_curFile->path          = m_curString;    break;
623         case InDir:       m_curDir->path           = m_curString;    break;
624         default: warn("warning: Unexpected tag `path' found\n");     break; 
625       }
626     }
627     
628     void endAnchor()
629     {
630       if (m_state==InMember)
631       {
632         m_curMember->anchor = m_curString; 
633       }
634       else
635       {
636         warn("warning: Unexpected tag `anchor' found\n");
637       }
638     }
639     
640     void endAnchorFile()
641     {
642       if (m_state==InMember)
643       {
644         m_curMember->anchorFile = m_curString; 
645       }
646       else
647       {
648         warn("warning: Unexpected tag `anchorfile' found\n");
649       }
650     }
651     
652     void endArglist()
653     {
654       if (m_state==InMember)
655       {
656         m_curMember->arglist = m_curString; 
657       }
658       else
659       {
660         warn("warning: Unexpected tag `arglist' found\n");
661       }
662     }
663     void endTitle()
664     {
665       switch (m_state)
666       {
667         case InGroup:     m_curGroup->title     = m_curString;    break;
668         case InPage:      m_curPage->title      = m_curString;    break;
669         default: warn("warning: Unexpected tag `title' found\n"); break; 
670       }
671     }
672
673     void endSubgroup()
674     {
675       if (m_state==InGroup)
676       {
677         m_curGroup->subgroupList.append(m_curString);
678       }
679       else
680       {
681         warn("warning: Unexpected tag `subgroup' found\n");
682       }
683     }
684
685     void startIgnoreElement(const QXmlAttributes& )
686     {
687     }
688
689     void endIgnoreElement()
690     {
691     }
692
693     bool startDocument()
694     {
695       m_state = Invalid;
696
697       m_curClass=0;
698       m_curNamespace=0;
699       m_curFile=0;
700       m_curGroup=0;
701       m_curPage=0;
702       m_curPackage=0;
703       m_curDir=0;
704
705       m_stateStack.setAutoDelete(TRUE);
706       m_tagFileClasses.setAutoDelete(TRUE);
707       m_tagFileFiles.setAutoDelete(TRUE);
708       m_tagFileNamespaces.setAutoDelete(TRUE);
709       m_tagFileGroups.setAutoDelete(TRUE);
710       m_tagFilePages.setAutoDelete(TRUE);
711       m_tagFilePackages.setAutoDelete(TRUE);
712       m_tagFileDirs.setAutoDelete(TRUE);
713
714       m_startElementHandlers.insert("compound",    new StartElementHandler(this,&TagFileParser::startCompound));
715       m_startElementHandlers.insert("member",      new StartElementHandler(this,&TagFileParser::startMember));
716       m_startElementHandlers.insert("name",        new StartElementHandler(this,&TagFileParser::startStringValue));
717       m_startElementHandlers.insert("base",        new StartElementHandler(this,&TagFileParser::startBase));
718       m_startElementHandlers.insert("filename",    new StartElementHandler(this,&TagFileParser::startStringValue));
719       m_startElementHandlers.insert("includes",    new StartElementHandler(this,&TagFileParser::startIncludes));
720       m_startElementHandlers.insert("path",        new StartElementHandler(this,&TagFileParser::startStringValue));
721       m_startElementHandlers.insert("anchorfile",  new StartElementHandler(this,&TagFileParser::startStringValue));
722       m_startElementHandlers.insert("anchor",      new StartElementHandler(this,&TagFileParser::startStringValue));
723       m_startElementHandlers.insert("arglist",     new StartElementHandler(this,&TagFileParser::startStringValue));
724       m_startElementHandlers.insert("title",       new StartElementHandler(this,&TagFileParser::startStringValue));
725       m_startElementHandlers.insert("subgroup",    new StartElementHandler(this,&TagFileParser::startStringValue));
726       m_startElementHandlers.insert("class",       new StartElementHandler(this,&TagFileParser::startStringValue));
727       m_startElementHandlers.insert("namespace",   new StartElementHandler(this,&TagFileParser::startStringValue));
728       m_startElementHandlers.insert("file",        new StartElementHandler(this,&TagFileParser::startStringValue));
729       m_startElementHandlers.insert("dir",         new StartElementHandler(this,&TagFileParser::startStringValue));
730       m_startElementHandlers.insert("page",        new StartElementHandler(this,&TagFileParser::startStringValue));
731       m_startElementHandlers.insert("docanchor",   new StartElementHandler(this,&TagFileParser::startDocAnchor));
732       m_startElementHandlers.insert("tagfile",     new StartElementHandler(this,&TagFileParser::startIgnoreElement));
733       m_startElementHandlers.insert("templarg",    new StartElementHandler(this,&TagFileParser::startStringValue));
734       m_startElementHandlers.insert("type",        new StartElementHandler(this,&TagFileParser::startStringValue));
735
736       m_endElementHandlers.insert("compound",    new EndElementHandler(this,&TagFileParser::endCompound));
737       m_endElementHandlers.insert("member",      new EndElementHandler(this,&TagFileParser::endMember));
738       m_endElementHandlers.insert("name",        new EndElementHandler(this,&TagFileParser::endName));
739       m_endElementHandlers.insert("base",        new EndElementHandler(this,&TagFileParser::endBase));
740       m_endElementHandlers.insert("filename",    new EndElementHandler(this,&TagFileParser::endFilename));
741       m_endElementHandlers.insert("includes",    new EndElementHandler(this,&TagFileParser::endIncludes));
742       m_endElementHandlers.insert("path",        new EndElementHandler(this,&TagFileParser::endPath));
743       m_endElementHandlers.insert("anchorfile",  new EndElementHandler(this,&TagFileParser::endAnchorFile));
744       m_endElementHandlers.insert("anchor",      new EndElementHandler(this,&TagFileParser::endAnchor));
745       m_endElementHandlers.insert("arglist",     new EndElementHandler(this,&TagFileParser::endArglist));
746       m_endElementHandlers.insert("title",       new EndElementHandler(this,&TagFileParser::endTitle));
747       m_endElementHandlers.insert("subgroup",    new EndElementHandler(this,&TagFileParser::endSubgroup));
748       m_endElementHandlers.insert("class"   ,    new EndElementHandler(this,&TagFileParser::endClass));
749       m_endElementHandlers.insert("namespace",   new EndElementHandler(this,&TagFileParser::endNamespace));
750       m_endElementHandlers.insert("file",        new EndElementHandler(this,&TagFileParser::endFile));
751       m_endElementHandlers.insert("dir",         new EndElementHandler(this,&TagFileParser::endDir));
752       m_endElementHandlers.insert("page",        new EndElementHandler(this,&TagFileParser::endPage));
753       m_endElementHandlers.insert("docanchor",   new EndElementHandler(this,&TagFileParser::endDocAnchor));
754       m_endElementHandlers.insert("tagfile",     new EndElementHandler(this,&TagFileParser::endIgnoreElement));
755       m_endElementHandlers.insert("templarg",    new EndElementHandler(this,&TagFileParser::endTemplateArg));
756       m_endElementHandlers.insert("type",        new EndElementHandler(this,&TagFileParser::endType));
757
758       return TRUE;
759     }
760
761     bool startElement( const QString&, const QString&, 
762                        const QString&name, const QXmlAttributes& attrib )
763     {
764       //printf("startElement `%s'\n",name.data());
765       StartElementHandler *handler = m_startElementHandlers[name.utf8()];
766       if (handler)
767       {
768         (*handler)(attrib);
769       }
770       else 
771       {
772         warn("warning: Unknown tag `%s' found!\n",name.data());
773       }
774       return TRUE;
775     }
776
777     bool endElement( const QString&, const QString&, const QString& name )
778     {
779       //printf("endElement `%s'\n",name.data());
780       EndElementHandler *handler = m_endElementHandlers[name.utf8()];
781       if (handler)
782       {
783         (*handler)();
784       }
785       else 
786       {
787         warn("warning: Unknown tag `%s' found!\n",name.data());
788       }
789       return TRUE;
790     }
791
792     bool characters ( const QString & ch ) 
793     {
794       m_curString+=ch.utf8();
795       return TRUE;
796     }
797
798     void dump();
799     void buildLists(Entry *root);
800     void addIncludes();
801     
802   private:
803     void buildMemberList(Entry *ce,QList<TagMemberInfo> &members);
804     void addDocAnchors(Entry *e,const TagAnchorInfoList &l);
805     QList<TagClassInfo>        m_tagFileClasses;
806     QList<TagFileInfo>         m_tagFileFiles;
807     QList<TagNamespaceInfo>    m_tagFileNamespaces;
808     QList<TagGroupInfo>        m_tagFileGroups;
809     QList<TagPageInfo>         m_tagFilePages;
810     QList<TagPackageInfo>      m_tagFilePackages;
811     QList<TagDirInfo>          m_tagFileDirs;
812     QDict<StartElementHandler> m_startElementHandlers;
813     QDict<EndElementHandler>   m_endElementHandlers;
814     TagClassInfo              *m_curClass;
815     TagFileInfo               *m_curFile;
816     TagNamespaceInfo          *m_curNamespace;
817     TagPackageInfo            *m_curPackage;
818     TagGroupInfo              *m_curGroup;
819     TagPageInfo               *m_curPage;
820     TagDirInfo                *m_curDir;
821     TagMemberInfo             *m_curMember;
822     TagIncludeInfo            *m_curIncludes;
823     QCString                   m_curString;
824     QCString                   m_tagName;
825     QCString                   m_fileName;
826     State                      m_state;
827     QStack<State>              m_stateStack;
828     QXmlLocator               *m_locator;
829     QCString                   m_inputFileName;
830 };
831
832 /** Error handler for the XML tag file parser. 
833  *
834  *  Basically dumps all fatal error to stderr using err().
835  */
836 class TagFileErrorHandler : public QXmlErrorHandler
837 {
838   public:
839     virtual ~TagFileErrorHandler() {}
840     bool warning( const QXmlParseException & )
841     {
842       return FALSE;
843     }
844     bool error( const QXmlParseException & )
845     {
846       return FALSE;
847     }
848     bool fatalError( const QXmlParseException &exception )
849     {
850       err("Fatal error at line %d column %d: %s\n",
851           exception.lineNumber(),exception.columnNumber(),
852           exception.message().data());
853       return FALSE;
854     }
855     QString errorString() { return ""; }
856
857   private:
858     QString errorMsg;
859 };
860
861 /*! Dumps the internal structures. For debugging only! */
862 void TagFileParser::dump()
863 {
864   msg("Result:\n");
865   QListIterator<TagClassInfo> lci(m_tagFileClasses);
866
867   //============== CLASSES
868   TagClassInfo *cd;
869   for (;(cd=lci.current());++lci)
870   {
871     msg("class `%s'\n",cd->name.data());
872     msg("  filename `%s'\n",cd->filename.data());
873     if (cd->bases)
874     {
875       QListIterator<BaseInfo> bii(*cd->bases);
876       BaseInfo *bi;
877       for ( bii.toFirst() ; (bi=bii.current()) ; ++bii) 
878       {
879         msg( "  base: %s \n", bi->name.data() );
880       }
881     }
882
883     QListIterator<TagMemberInfo> mci(cd->members);
884     TagMemberInfo *md;
885     for (;(md=mci.current());++mci)
886     {
887       msg("  member:\n");
888       msg("    kind: `%s'\n",md->kind.data());
889       msg("    name: `%s'\n",md->name.data());
890       msg("    anchor: `%s'\n",md->anchor.data());
891       msg("    arglist: `%s'\n",md->arglist.data());
892     }
893   }
894   //============== NAMESPACES
895   QListIterator<TagNamespaceInfo> lni(m_tagFileNamespaces);
896   TagNamespaceInfo *nd;
897   for (;(nd=lni.current());++lni)
898   {
899     msg("namespace `%s'\n",nd->name.data());
900     msg("  filename `%s'\n",nd->filename.data());
901     QStringList::Iterator it;
902     for ( it = nd->classList.begin(); 
903         it != nd->classList.end(); ++it ) 
904     {
905       msg( "  class: %s \n", (*it).latin1() );
906     }
907
908     QListIterator<TagMemberInfo> mci(nd->members);
909     TagMemberInfo *md;
910     for (;(md=mci.current());++mci)
911     {
912       msg("  member:\n");
913       msg("    kind: `%s'\n",md->kind.data());
914       msg("    name: `%s'\n",md->name.data());
915       msg("    anchor: `%s'\n",md->anchor.data());
916       msg("    arglist: `%s'\n",md->arglist.data());
917     }
918   }
919   //============== FILES
920   QListIterator<TagFileInfo> lfi(m_tagFileFiles);
921   TagFileInfo *fd;
922   for (;(fd=lfi.current());++lfi)
923   {
924     msg("file `%s'\n",fd->name.data());
925     msg("  filename `%s'\n",fd->filename.data());
926     QStringList::Iterator it;
927     for ( it = fd->namespaceList.begin(); 
928         it != fd->namespaceList.end(); ++it ) 
929     {
930       msg( "  namespace: %s \n", (*it).latin1() );
931     }
932     for ( it = fd->classList.begin(); 
933         it != fd->classList.end(); ++it ) 
934     {
935       msg( "  class: %s \n", (*it).latin1() );
936     }
937
938     QListIterator<TagMemberInfo> mci(fd->members);
939     TagMemberInfo *md;
940     for (;(md=mci.current());++mci)
941     {
942       msg("  member:\n");
943       msg("    kind: `%s'\n",md->kind.data());
944       msg("    name: `%s'\n",md->name.data());
945       msg("    anchor: `%s'\n",md->anchor.data());
946       msg("    arglist: `%s'\n",md->arglist.data());
947     }
948
949     QListIterator<TagIncludeInfo> mii(fd->includes);
950     TagIncludeInfo *ii;
951     for (;(ii=mii.current());++mii)
952     {
953       msg("  includes id: %s name: %s\n",ii->id.data(),ii->name.data());
954     }
955   }
956
957   //============== GROUPS
958   QListIterator<TagGroupInfo> lgi(m_tagFileGroups);
959   TagGroupInfo *gd;
960   for (;(gd=lgi.current());++lgi)
961   {
962     msg("group `%s'\n",gd->name.data());
963     msg("  filename `%s'\n",gd->filename.data());
964     QStringList::Iterator it;
965     for ( it = gd->namespaceList.begin(); 
966         it != gd->namespaceList.end(); ++it ) 
967     {
968       msg( "  namespace: %s \n", (*it).latin1() );
969     }
970     for ( it = gd->classList.begin(); 
971         it != gd->classList.end(); ++it ) 
972     {
973       msg( "  class: %s \n", (*it).latin1() );
974     }
975     for ( it = gd->fileList.begin(); 
976         it != gd->fileList.end(); ++it ) 
977     {
978       msg( "  file: %s \n", (*it).latin1() );
979     }
980     for ( it = gd->subgroupList.begin(); 
981         it != gd->subgroupList.end(); ++it ) 
982     {
983       msg( "  subgroup: %s \n", (*it).latin1() );
984     }
985     for ( it = gd->pageList.begin(); 
986         it != gd->pageList.end(); ++it ) 
987     {
988       msg( "  page: %s \n", (*it).latin1() );
989     }
990
991     QListIterator<TagMemberInfo> mci(gd->members);
992     TagMemberInfo *md;
993     for (;(md=mci.current());++mci)
994     {
995       msg("  member:\n");
996       msg("    kind: `%s'\n",md->kind.data());
997       msg("    name: `%s'\n",md->name.data());
998       msg("    anchor: `%s'\n",md->anchor.data());
999       msg("    arglist: `%s'\n",md->arglist.data());
1000     }
1001   }
1002   //============== PAGES
1003   QListIterator<TagPageInfo> lpi(m_tagFilePages);
1004   TagPageInfo *pd;
1005   for (;(pd=lpi.current());++lpi)
1006   {
1007     msg("page `%s'\n",pd->name.data());
1008     msg("  title `%s'\n",pd->title.data());
1009     msg("  filename `%s'\n",pd->filename.data());
1010   }
1011   //============== DIRS
1012   QListIterator<TagDirInfo> ldi(m_tagFileDirs);
1013   TagDirInfo *dd;
1014   for (;(dd=ldi.current());++ldi)
1015   {
1016     msg("dir `%s'\n",dd->name.data());
1017     msg("  path `%s'\n",dd->path.data());
1018     QStringList::Iterator it;
1019     for ( it = dd->fileList.begin(); 
1020         it != dd->fileList.end(); ++it ) 
1021     {
1022       msg( "  file: %s \n", (*it).latin1() );
1023     }
1024     for ( it = dd->subdirList.begin(); 
1025         it != dd->subdirList.end(); ++it ) 
1026     {
1027       msg( "  subdir: %s \n", (*it).latin1() );
1028     }
1029   }
1030 }
1031
1032 void TagFileParser::addDocAnchors(Entry *e,const TagAnchorInfoList &l)
1033 {
1034   QListIterator<TagAnchorInfo> tli(l);
1035   TagAnchorInfo *ta;
1036   for (tli.toFirst();(ta=tli.current());++tli)
1037   {
1038     if (Doxygen::sectionDict.find(ta->label)==0)
1039     {
1040       //printf("New sectionInfo file=%s anchor=%s\n",
1041       //    ta->fileName.data(),ta->label.data());
1042       SectionInfo *si=new SectionInfo(ta->fileName,ta->label,ta->label,
1043           SectionInfo::Anchor,0,m_tagName);
1044       Doxygen::sectionDict.append(ta->label,si);
1045       e->anchors->append(si);
1046     }
1047     else
1048     {
1049       warn("Duplicate anchor %s found\n",ta->label.data());
1050     }
1051   }
1052 }
1053
1054 void TagFileParser::buildMemberList(Entry *ce,QList<TagMemberInfo> &members)
1055 {
1056   QListIterator<TagMemberInfo> mii(members);
1057   TagMemberInfo *tmi;
1058   for (;(tmi=mii.current());++mii)
1059   {
1060     Entry *me      = new Entry;
1061     me->type       = tmi->type;
1062     me->name       = tmi->name;
1063     me->args       = tmi->arglist;
1064     if (!me->args.isEmpty())
1065     {
1066       delete me->argList;
1067       me->argList = new ArgumentList;
1068       stringToArgumentList(me->args,me->argList);
1069     }
1070     me->protection = tmi->prot;
1071     me->virt       = tmi->virt;
1072     me->stat       = tmi->isStatic;
1073     me->fileName   = ce->fileName;
1074     if (ce->section == Entry::GROUPDOC_SEC)
1075     {
1076       me->groups->append(new Grouping(ce->name,Grouping::GROUPING_INGROUP));
1077     }
1078     addDocAnchors(me,tmi->docAnchors);
1079     TagInfo *ti    = new TagInfo;
1080     ti->tagName    = m_tagName;
1081     ti->anchor     = tmi->anchor;
1082     ti->fileName   = tmi->anchorFile;
1083     me->tagInfo    = ti;
1084     if (tmi->kind=="define")
1085     {
1086       me->type="#define";
1087       me->section = Entry::DEFINE_SEC;
1088     }
1089     else if (tmi->kind=="enumvalue")
1090     {
1091       me->section = Entry::VARIABLE_SEC;
1092       me->mtype = Method;
1093     }
1094     else if (tmi->kind=="property")
1095     {
1096       me->section = Entry::VARIABLE_SEC;
1097       me->mtype = Property;
1098     }
1099     else if (tmi->kind=="event")
1100     {
1101       me->section = Entry::VARIABLE_SEC;
1102       me->mtype = Event;
1103     }
1104     else if (tmi->kind=="variable")
1105     {
1106       me->section = Entry::VARIABLE_SEC;
1107       me->mtype = Method;
1108     }
1109     else if (tmi->kind=="typedef")
1110     {
1111       me->section = Entry::VARIABLE_SEC; //Entry::TYPEDEF_SEC;
1112       me->type.prepend("typedef ");
1113       me->mtype = Method;
1114     }
1115     else if (tmi->kind=="enumeration")
1116     {
1117       me->section = Entry::ENUM_SEC;
1118       me->mtype = Method;
1119     }
1120     else if (tmi->kind=="function")
1121     {
1122       me->section = Entry::FUNCTION_SEC;
1123       me->mtype = Method;
1124     }
1125     else if (tmi->kind=="signal")
1126     {
1127       me->section = Entry::FUNCTION_SEC;
1128       me->mtype = Signal;
1129     }
1130     else if (tmi->kind=="prototype")
1131     {
1132       me->section = Entry::FUNCTION_SEC;
1133       me->mtype = Method;
1134     }
1135     else if (tmi->kind=="friend")
1136     {
1137       me->section = Entry::FUNCTION_SEC;
1138       me->type.prepend("friend ");
1139       me->mtype = Method;
1140     }
1141     else if (tmi->kind=="dcop")
1142     {
1143       me->section = Entry::FUNCTION_SEC;
1144       me->mtype = DCOP;
1145     }
1146     else if (tmi->kind=="slot")
1147     {
1148       me->section = Entry::FUNCTION_SEC;
1149       me->mtype = Slot;
1150     }
1151     ce->addSubEntry(me);
1152   }
1153 }
1154
1155 static QCString stripPath(const QCString &s)
1156 {
1157   int i=s.findRev('/');
1158   if (i!=-1)
1159   {
1160     return s.right(s.length()-i-1);
1161   }
1162   else
1163   {
1164     return s;
1165   }
1166 }
1167
1168 /*! Injects the info gathered by the XML parser into the Entry tree.
1169  *  This tree contains the information extracted from the input in a 
1170  *  "unrelated" form.
1171  */
1172 void TagFileParser::buildLists(Entry *root)
1173 {
1174   // build class list
1175   TagClassInfo *tci = m_tagFileClasses.first();
1176   while (tci)
1177   {
1178     Entry *ce = new Entry;
1179     ce->section = Entry::CLASS_SEC;
1180     switch (tci->kind)
1181     {
1182       case TagClassInfo::Class:     break;
1183       case TagClassInfo::Struct:    ce->spec = Entry::Struct;    break;
1184       case TagClassInfo::Union:     ce->spec = Entry::Union;     break;
1185       case TagClassInfo::Interface: ce->spec = Entry::Interface; break;
1186       case TagClassInfo::Exception: ce->spec = Entry::Exception; break;
1187       case TagClassInfo::Protocol:  ce->spec = Entry::Protocol;  break;
1188       case TagClassInfo::Category:  ce->spec = Entry::Category;  break;
1189     }
1190     ce->name     = tci->name;
1191     if (tci->kind==TagClassInfo::Protocol) 
1192     {
1193       ce->name+="-p";
1194     }
1195     addDocAnchors(ce,tci->docAnchors);
1196     TagInfo *ti  = new TagInfo;
1197     ti->tagName  = m_tagName;
1198     ti->fileName = tci->filename;
1199     ce->tagInfo = ti;
1200     ce->lang = tci->isObjC ? SrcLangExt_ObjC : SrcLangExt_Unknown;
1201     // transfer base class list
1202     if (tci->bases)
1203     {
1204       delete ce->extends;
1205       ce->extends = tci->bases; tci->bases = 0;
1206     }
1207     if (tci->templateArguments)
1208     {
1209       if (ce->tArgLists==0) 
1210       {
1211         ce->tArgLists = new QList<ArgumentList>;
1212         ce->tArgLists->setAutoDelete(TRUE);
1213       }
1214       ArgumentList *al = new ArgumentList;
1215       ce->tArgLists->append(al);
1216       
1217       QListIterator<QCString> sli(*tci->templateArguments);
1218       QCString *argName;
1219       for (;(argName=sli.current());++sli)
1220       {
1221         Argument *a = new Argument;
1222         a->type = "class";
1223         a->name = *argName;
1224         al->append(a);
1225       }
1226     }
1227
1228     buildMemberList(ce,tci->members);
1229     root->addSubEntry(ce);
1230     tci = m_tagFileClasses.next();
1231   }
1232
1233   // build file list
1234   TagFileInfo *tfi = m_tagFileFiles.first();
1235   while (tfi)
1236   {
1237     Entry *fe = new Entry;
1238     fe->section = guessSection(tfi->name);
1239     fe->name     = tfi->name;
1240     addDocAnchors(fe,tfi->docAnchors);
1241     TagInfo *ti  = new TagInfo;
1242     ti->tagName  = m_tagName;
1243     ti->fileName = tfi->filename;
1244     fe->tagInfo  = ti;
1245     
1246     QCString fullName = m_tagName+":"+tfi->path+stripPath(tfi->name);
1247     fe->fileName = fullName;
1248     //printf("new FileDef() filename=%s\n",tfi->filename.data());
1249     FileDef *fd = new FileDef(m_tagName+":"+tfi->path,
1250                               tfi->name,m_tagName,
1251                               tfi->filename
1252                              );
1253     FileName *mn;
1254     if ((mn=Doxygen::inputNameDict->find(tfi->name)))
1255     {
1256       mn->append(fd);
1257     }
1258     else
1259     {
1260       mn = new FileName(fullName,tfi->name);
1261       mn->append(fd);
1262       Doxygen::inputNameList->inSort(mn);
1263       Doxygen::inputNameDict->insert(tfi->name,mn);
1264     }
1265     buildMemberList(fe,tfi->members);
1266     root->addSubEntry(fe);
1267     tfi = m_tagFileFiles.next();
1268   }
1269
1270   // build namespace list
1271   TagNamespaceInfo *tni = m_tagFileNamespaces.first();
1272   while (tni)
1273   {
1274     Entry *ne    = new Entry;
1275     ne->section  = Entry::NAMESPACE_SEC;
1276     ne->name     = tni->name;
1277     addDocAnchors(ne,tni->docAnchors);
1278     TagInfo *ti  = new TagInfo;
1279     ti->tagName  = m_tagName;
1280     ti->fileName = tni->filename;
1281     ne->tagInfo  = ti;
1282
1283     buildMemberList(ne,tni->members);
1284     root->addSubEntry(ne);
1285     tni = m_tagFileNamespaces.next();
1286   }
1287
1288   // build package list
1289   TagPackageInfo *tpgi = m_tagFilePackages.first();
1290   while (tpgi)
1291   {
1292     Entry *pe    = new Entry;
1293     pe->section  = Entry::PACKAGE_SEC;
1294     pe->name     = tpgi->name;
1295     addDocAnchors(pe,tpgi->docAnchors);
1296     TagInfo *ti  = new TagInfo;
1297     ti->tagName  = m_tagName;
1298     ti->fileName = tpgi->filename;
1299     pe->tagInfo  = ti;
1300
1301     buildMemberList(pe,tpgi->members);
1302     root->addSubEntry(pe);
1303     tpgi = m_tagFilePackages.next();
1304   }
1305
1306   // build group list, but only if config file says to include it
1307   //if (Config_getBool("EXTERNAL_GROUPS")) 
1308   //{
1309     TagGroupInfo *tgi = m_tagFileGroups.first();
1310     while (tgi)
1311     {
1312       Entry *ge    = new Entry;
1313       ge->section  = Entry::GROUPDOC_SEC;
1314       ge->name     = tgi->name;
1315       ge->type     = tgi->title;
1316       addDocAnchors(ge,tgi->docAnchors);
1317       TagInfo *ti  = new TagInfo;
1318       ti->tagName  = m_tagName;
1319       ti->fileName = tgi->filename;
1320       ge->tagInfo  = ti;
1321       
1322       buildMemberList(ge,tgi->members);
1323       root->addSubEntry(ge);
1324       tgi = m_tagFileGroups.next();
1325     }
1326   //}
1327
1328   // build page list
1329   TagPageInfo *tpi = m_tagFilePages.first();
1330   while (tpi)
1331   {
1332     Entry *pe    = new Entry;
1333     pe->section  = Entry::PAGEDOC_SEC;
1334     pe->name     = tpi->name;
1335     pe->args     = tpi->title;
1336     addDocAnchors(pe,tpi->docAnchors);
1337     TagInfo *ti  = new TagInfo;
1338     ti->tagName  = m_tagName;
1339     ti->fileName = tpi->filename;
1340     pe->tagInfo  = ti;
1341
1342     root->addSubEntry(pe);
1343     tpi = m_tagFilePages.next();
1344   }
1345 }
1346
1347 void TagFileParser::addIncludes()
1348 {
1349   TagFileInfo *tfi = m_tagFileFiles.first();
1350   while (tfi)
1351   {
1352     //printf("tag file tagName=%s path=%s name=%s\n",m_tagName.data(),tfi->path.data(),tfi->name.data());
1353     FileName *fn = Doxygen::inputNameDict->find(tfi->name);
1354     if (fn)
1355     {
1356       //printf("found\n");
1357       FileNameIterator fni(*fn);
1358       FileDef *fd;
1359       for (;(fd=fni.current());++fni)
1360       {
1361         //printf("input file path=%s name=%s\n",fd->getPath().data(),fd->name().data());
1362         if (fd->getPath()==QCString(m_tagName+":"+tfi->path))
1363         {
1364           //printf("found\n");
1365           QListIterator<TagIncludeInfo> mii(tfi->includes);
1366           TagIncludeInfo *ii;
1367           for (;(ii=mii.current());++mii)
1368           {
1369             //printf("ii->name=`%s'\n",ii->name.data());
1370             FileName *ifn = Doxygen::inputNameDict->find(ii->name);
1371             ASSERT(ifn!=0);
1372             if (ifn)
1373             {
1374               FileNameIterator ifni(*ifn);
1375               FileDef *ifd;
1376               for (;(ifd=ifni.current());++ifni)
1377               {
1378                 //printf("ifd->getOutputFileBase()=%s ii->id=%s\n",
1379                 //        ifd->getOutputFileBase().data(),ii->id.data());
1380                 if (ifd->getOutputFileBase()==QCString(ii->id))
1381                 {
1382                   fd->addIncludeDependency(ifd,ii->text,ii->isLocal,ii->isImported,FALSE);
1383                 }
1384               }
1385             }
1386           }
1387         }
1388       } 
1389     }
1390     tfi = m_tagFileFiles.next();
1391   }
1392 }
1393
1394 void parseTagFile(Entry *root,const char *fullName,const char *tagName)
1395 {
1396   QFileInfo fi(fullName);
1397   if (!fi.exists()) return;
1398   TagFileParser handler( tagName );
1399   handler.setFileName(fullName);
1400   TagFileErrorHandler errorHandler;
1401   QFile xmlFile( fullName );
1402   QXmlInputSource source( xmlFile );
1403   QXmlSimpleReader reader;
1404   reader.setContentHandler( &handler );
1405   reader.setErrorHandler( &errorHandler );
1406   reader.parse( source );
1407   handler.buildLists(root);
1408   handler.addIncludes();
1409   //handler.dump();
1410 }
1411
1412