Fix for UBSan build
[platform/upstream/doxygen.git] / src / dbusxmlscanner.cpp
1 /******************************************************************************
2  *
3  *
4  *
5  * Copyright (C) 2009 by Tobias Hunger <tobias@aquazul.com>
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation under the terms of the GNU General Public License is hereby
9  * granted. No representations are made about the suitability of this software
10  * for any purpose. It is provided "as is" without express or implied warranty.
11  * See the GNU General Public License for more details.
12  *
13  * Documents produced by Doxygen are derivative works derived from the
14  * input used in their production; they are not affected by this license.
15  *
16  */
17
18 #include "dbusxmlscanner.h"
19
20 #include "commentscan.h"
21 #include "entry.h"
22
23 #include <qfile.h>
24 #include <qxml.h>
25 #include <qstring.h>
26
27 #include "message.h"
28 #include "util.h"
29 #include "arguments.h"
30
31 // -----------------------------------------------------------------------
32 // Convenience defines:
33 // -----------------------------------------------------------------------
34
35 #define CONDITION(cond, msg) \
36     do {\
37         if (cond)\
38         {\
39             if (m_errorString.isEmpty()) { m_errorString = msg; }\
40             return false;\
41         }\
42     }\
43     while (0)
44
45 #define DOC_ERROR(msg) \
46     warn_doc_error(m_fileName.data(), lineNumber(), msg.data())
47
48 #define COND_DOC_ERROR(cond, msg) \
49     do {\
50         if (cond)\
51         {\
52              DOC_ERROR(msg);\
53              return true;\
54         }\
55     }\
56     while (0)
57
58 #define DBUS(name) isDBusElement(namespaceURI, localName, qName, name)
59 #define EXTENSION(name) isExtensionElement(namespaceURI, localName, qName, name)
60
61 // -----------------------------------------------------------------------
62 // DBusXMLHandler class
63 // -----------------------------------------------------------------------
64
65 const QString EXTENSION_URI("http://psiamp.org/dtd/doxygen_dbusxml.dtd");
66
67 /** DBus implementation of the generic QXmlDefaultHandler. */
68 class DBusXMLHandler : public QXmlDefaultHandler
69 {
70 public:
71     DBusXMLHandler(ParserInterface * parser,
72                    QXmlSimpleReader * reader,
73                    const char * file_name,
74                    Entry * root) :
75         m_parser(parser),
76         m_locator(reader),
77         m_currentEntry(0),
78         m_currentInterface(0),
79         m_currentMethod(0),
80         m_currentArgument(0),
81         m_currentProperty(0),
82         m_currentEnum(0),
83         m_fileName(file_name),
84         m_currentComment(0)
85     {
86         setDocumentLocator(&m_locator);
87
88         m_scopeCount = 0;
89
90         // Set up stack cleanup:
91         m_structStack.setAutoDelete(TRUE);
92         m_elementStack.setAutoDelete(TRUE);
93         m_scopeStack.setAutoDelete(TRUE);
94
95         openScopes(root);
96     }
97
98     ~DBusXMLHandler()
99     { closeScopes(); }
100
101     QString errorString()
102     { return m_errorString; }
103
104     bool startElement(const QString &namespaceURI,
105                       const QString &localName,
106                       const QString &qName,
107                       const QXmlAttributes &attributes)
108     {
109         // add to elements stack:
110         m_elementStack.append(new ElementData(qName.utf8()));
111
112         // First we need a node.
113         if (DBUS("node"))
114         {
115             CONDITION(!m_currentNode.isEmpty(), "Node inside a node.");
116
117             const int idx(indexOf(attributes, "name"));
118             COND_DOC_ERROR(idx < 0, QCString("Anonymous node found."));
119
120             m_currentNode = attributes.value(idx).utf8();
121             // A node is actually of little interest, so do nothing here.
122             return true;
123         }
124
125         // Then we need an interface.
126         if (DBUS("interface"))
127         {
128             // We need a nodeName for interfaces:
129             CONDITION(m_currentNode.isEmpty(), "Interface without a node.");
130             CONDITION(m_currentInterface, "Interface within another interface.");
131
132             const int idx(indexOf(attributes, "name"));
133             COND_DOC_ERROR(idx < 0, QString("Interface without a name found."));
134
135             // A interface is roughly equivalent to a class:
136             m_currentInterface = createEntry();
137
138             m_currentInterface->section = Entry::CLASS_SEC;
139             m_currentInterface->spec |= Entry::Interface;
140             m_currentInterface->type = "Interface";
141             m_currentInterface->name = substitute(attributes.value(idx).utf8(), ".", "::");
142
143             openScopes(m_currentInterface);
144
145             return true;
146         }
147
148         if (DBUS("method") || DBUS("signal"))
149         {
150             // We need a interfaceName for methods and signals:
151             CONDITION(!m_currentInterface, "Method or signal found outside a interface.");
152             CONDITION(m_currentMethod, "Method or signal found inside another method or signal.");
153             CONDITION(m_currentProperty, "Methor or signal found inside a property.");
154             CONDITION(!m_structStack.isEmpty(), "Method or signal found inside a struct.");
155             CONDITION(m_currentEnum, "Methor or signal found inside a enum.");
156
157             const int idx(indexOf(attributes, "name"));
158             COND_DOC_ERROR(idx < 0, QString("Method or signal without a name found."));
159
160             m_currentMethod = createEntry();
161
162             m_currentMethod->section = Entry::FUNCTION_SEC;
163             m_currentMethod->name = attributes.value(idx).utf8();
164             m_currentMethod->mtype = Method;
165             m_currentMethod->type = "void";
166
167             if (DBUS("signal"))
168             { m_currentMethod->mtype = Signal; }
169         }
170
171         if (DBUS("arg"))
172         {
173             // We need a method for arguments:
174             CONDITION(!m_currentMethod, "Argument found outside a method or signal.");
175             CONDITION(m_currentArgument, "Argument found inside another argument.");
176
177             const int name_idx(indexOf(attributes, "name"));
178             COND_DOC_ERROR(name_idx < 0, QString("Argument without a name found."));
179             COND_DOC_ERROR(!hasType(attributes), QString("Argument without a type found."));
180
181             const int direction_idx(indexOf(attributes, "direction"));
182
183             if ((m_currentMethod->mtype == Signal &&
184                  direction_idx >= 0 &&
185                  attributes.value(direction_idx) != "in") ||
186                 (m_currentMethod->mtype == Method &&
187                  direction_idx >= 0 &&
188                  attributes.value(direction_idx) != "in" &&
189                  attributes.value(direction_idx) != "out"))
190             {
191                 m_errorString = "Invalid direction found.";
192                 return false;
193             }
194
195             m_currentArgument = new Argument;
196             m_currentArgument->type = getType(attributes).utf8();
197             m_currentArgument->name = attributes.value(name_idx).utf8();
198             if (direction_idx >= 0)
199             { m_currentArgument->attrib = attributes.value(direction_idx).utf8(); }
200             else
201             {
202                 if (m_currentMethod->mtype == Signal)
203                 { m_currentArgument->attrib = "in"; }
204                 else
205                 { m_currentArgument->attrib = "out"; }
206             }
207         }
208
209         if (DBUS("property"))
210         {
211             CONDITION(m_currentMethod, "Property found inside a method or signal.");
212             CONDITION(!m_currentInterface, "Property found outside an interface.");
213             CONDITION(m_currentProperty, "Property found inside another property.");
214             CONDITION(!m_structStack.isEmpty(), "Property found inside a struct.");
215             CONDITION(m_currentEnum, "Property found inside a enum.");
216
217             const int name_idx(indexOf(attributes, "name"));
218             COND_DOC_ERROR(name_idx < 0, QString("Anonymous property found."));
219             COND_DOC_ERROR(!hasType(attributes), QString("Property without a type found."));
220
221             const int access_idx(indexOf(attributes, "access"));
222             COND_DOC_ERROR(access_idx < 0, QString("Property without a access attribute found."));
223             COND_DOC_ERROR(attributes.value(access_idx) != "read" &&
224                            attributes.value(access_idx) != "write" &&
225                            attributes.value(access_idx) != "readwrite",
226                            QString("Property with invalid access attribute \"%1\" found.").
227                            arg(attributes.value(access_idx)));
228
229             m_currentProperty = createEntry();
230
231             m_currentProperty->section = Entry::FUNCTION_SEC;
232
233             if (attributes.value(access_idx) == "read" ||
234                 attributes.value(access_idx) == "readwrite")
235             { m_currentProperty->spec |= Entry::Readable; }
236
237             if (attributes.value(access_idx) == "write" ||
238                 attributes.value(access_idx) == "readwrite")
239             { m_currentProperty->spec |= Entry::Writable; }
240
241             m_currentProperty->name = attributes.value(name_idx).utf8();
242             m_currentProperty->mtype = Property;
243             m_currentProperty->type = getType(attributes).utf8();
244         }
245
246         if (EXTENSION("namespace"))
247         {
248             CONDITION(m_currentNode.isEmpty(), "Namespace found outside a node.");
249             CONDITION(m_currentInterface, "Namespace found inside an interface.");
250
251             const int idx(indexOf(attributes, "name"));
252             COND_DOC_ERROR(idx < 0, QString("Anonymous namespace found."));
253
254             m_namespaceStack.append(openNamespace(attributes.value(idx)));
255             openScopes(m_namespaceStack.last());
256         }
257
258         if (EXTENSION("struct"))
259         {
260             CONDITION(m_currentMethod, "Struct found inside a method or signal.");
261             CONDITION(m_currentProperty, "Struct found inside a property.");
262             CONDITION(m_currentEnum, "Struct found inside an enum.");
263
264             const int idx(indexOf(attributes, "name"));
265             COND_DOC_ERROR(idx < 0, QString("Anonymous struct found."));
266
267             Entry * current_struct = createEntry();
268             current_struct->section = Entry::CLASS_SEC;
269             current_struct->spec = Entry::Struct;
270             current_struct->name = attributes.value(idx).utf8();
271
272             openScopes(current_struct);
273
274             current_struct->type = current_struct->name + " struct";
275
276             m_structStack.append(new StructData(current_struct));
277         }
278
279         if (EXTENSION("member"))
280         {
281             CONDITION(m_structStack.isEmpty(), "Member found outside of struct.");
282
283             const int name_idx(indexOf(attributes, "name"));
284             COND_DOC_ERROR(name_idx < 0, QString("Anonymous member found."));
285             COND_DOC_ERROR(!hasType(attributes), QString("Member without a type found."));
286
287             createEntry();
288
289             m_currentEntry->section = Entry::VARIABLE_SEC;
290             m_currentEntry->name = attributes.value(name_idx).utf8();
291             m_currentEntry->type = getType(attributes).utf8();
292
293             QString type(getDBusType(m_currentEntry->type));
294             m_structStack.last()->type.append(type.utf8());
295         }
296
297         if (EXTENSION("enum") || EXTENSION("flagset"))
298         {
299             CONDITION(m_currentMethod, "Enum found inside a method or signal.");
300             CONDITION(m_currentProperty, "Enum found inside a property.");
301
302             const int name_idx(indexOf(attributes, "name"));
303             COND_DOC_ERROR(name_idx < 0, QString("Anonymous enum found."));
304
305             const int type_idx(indexOf(attributes, "type"));
306             QString type = "u";
307             if (type_idx >= 0)
308             { type = attributes.value(type_idx); }
309             if (type != "y" && type != "q" && type != "u" && type != "t")
310             { DOC_ERROR(QString("Invalid enum type \"%1\" found.").arg(type)); }
311
312             m_currentEnum = createEntry();
313             m_currentEnum->section = Entry::ENUM_SEC;
314             m_currentEnum->name = attributes.value(name_idx).utf8();
315
316             openScopes(m_currentEnum);
317
318             m_currentEnum->type = m_currentEntry->name + " enum";
319
320             addNamedType(type.utf8());
321         }
322
323         if (EXTENSION("value"))
324         {
325             CONDITION(!m_currentEnum, "Value found outside an enum.");
326
327             const int name_idx(indexOf(attributes, "name"));
328             COND_DOC_ERROR(name_idx < 0, QString("Anonymous value found."));
329
330             const int value_idx(indexOf(attributes, "value"));
331
332             createEntry();
333
334             m_currentEntry->section = Entry::VARIABLE_SEC;
335             m_currentEntry->name = attributes.value(name_idx).utf8();
336             m_currentEntry->type = m_currentEnum->name; // "@"; // enum marker!
337             if (value_idx >= 0)
338             { m_currentEntry->initializer = attributes.value(value_idx).utf8(); }
339         }
340
341         return true;
342     }
343
344     bool endElement(const QString &namespaceURI,
345                     const QString &localName,
346                     const QString &qName)
347     {
348         // Clean up elements stack:
349         // Since we made sure to get the elements in the proper order when
350         // adding we do not need to do so again here.
351         COND_DOC_ERROR(m_elementStack.last()->element != qName.utf8(),
352                        QString("Malformed XML: Unexpected closing element found.").
353                        arg(m_elementStack.last()->element).utf8());
354         m_elementStack.removeLast();
355
356         // Interface:
357         if (DBUS("interface"))
358         {
359             CONDITION(!m_currentInterface, "end of interface found without start.");
360             m_currentInterface->endBodyLine = lineNumber();
361             closeScopes();
362             m_currentInterface = 0;
363         }
364
365         if (DBUS("method") || DBUS("signal"))
366         {
367             CONDITION(!m_currentMethod, "end of method found without start.");
368             CONDITION(!m_currentInterface, "end of method found outside interface.");
369             m_currentMethod->endBodyLine = lineNumber();
370             m_currentInterface->addSubEntry(m_currentMethod);
371             m_currentMethod = 0;
372         }
373
374         if (DBUS("property"))
375         {
376             CONDITION(!m_currentProperty, "end of property found without start.");
377             CONDITION(!m_currentInterface, "end of property found outside interface.");
378             m_currentProperty->endBodyLine = lineNumber();
379             m_currentInterface->addSubEntry(m_currentProperty);
380             m_currentProperty = 0;
381         }
382
383         if (DBUS("arg"))
384         {
385             CONDITION(!m_currentMethod, "end of arg found outside method.");
386             m_currentMethod->argList->append(m_currentArgument);
387             m_currentArgument = 0;
388         }
389
390         if (EXTENSION("namespace"))
391         {
392             Entry * current = m_namespaceStack.last();
393             CONDITION(!current, "end of namespace without start.");
394             m_namespaceStack.removeLast();
395
396             current->endBodyLine = lineNumber();
397             closeScopes();
398         }
399
400         if (EXTENSION("struct"))
401         {
402             StructData * data = m_structStack.last();
403             CONDITION(!data, "end of struct without start.");
404
405             data->entry->endBodyLine = lineNumber();
406
407             QString current_type;
408             current_type.append(QString("("));
409             current_type.append(data->type);
410             current_type.append(QString(")"));
411
412             addNamedType(current_type.utf8());
413
414             closeScopes();
415
416             m_structStack.removeLast();
417         }
418
419         if (EXTENSION("member"))
420         {
421            StructData * data = m_structStack.last();
422            CONDITION(!data, "end of member outside struct.");
423            data->entry->addSubEntry(m_currentEntry);
424         }
425
426         if (EXTENSION("enum") || EXTENSION("flagset"))
427         {
428             CONDITION(!m_currentEnum, "end of enum without start.");
429             m_currentEnum->endBodyLine = lineNumber();
430             closeScopes();
431
432             m_currentEnum = 0;
433         }
434
435         if (EXTENSION("value"))
436         {
437             CONDITION(!m_currentEntry, "end of value without start");
438             m_currentEntry->endBodyLine = lineNumber();
439
440             m_currentEnum->addSubEntry(m_currentEntry);
441         }
442
443         return true;
444     }
445
446     bool characters(const QString & /*chars*/)
447     { return true; }
448
449     bool comment(const QString & comment_)
450     {
451         if (m_currentComment)
452         { handleComment(); }
453
454         m_currentComment = new CommentData(m_fileName, lineNumber(), comment_.utf8());
455
456         if (m_currentComment->shouldIgnore)
457         {
458             delete m_currentComment;
459             m_currentComment = 0;
460             return true;
461         }
462
463         if (m_currentComment->associateWithPrevious)
464         { handleComment(); }
465
466         return true;
467     }
468
469     void handleComment()
470     {
471         if (m_currentComment == 0 || m_currentEntry == 0)
472         { return; }
473
474         QCString text(m_currentComment->text);
475
476         m_currentEntry->docFile = m_currentComment->fileName;
477         m_currentEntry->docLine = m_currentComment->line;
478
479         int position(0);
480         bool needs_entry(false);
481         bool brief(false);
482         Protection prot(Public);
483         int lineNr = lineNumber();
484
485         while (parseCommentBlock(m_parser,
486                                  m_currentEntry,
487                                  text, m_fileName.data(), 
488                                  lineNr,
489                                  brief, m_currentComment->isJavaStyle,
490                                  false,
491                                  prot,
492                                  position,
493                                  needs_entry))
494         {
495             if (needs_entry) { createEntry(); }
496         }
497         if (needs_entry) { createEntry(); }
498
499         delete m_currentComment;
500         m_currentComment = 0;
501     }
502
503     QXmlLocator * locator()
504     { return &m_locator; }
505
506     int lineNumber()
507     { return m_locator.lineNumber(); }
508
509     void setSection()
510     {
511         Entry * current = createEntry();
512         current->reset();
513
514         current->name    = m_fileName;
515         current->section = Entry::SOURCE_SEC;
516
517         // Open/Close the scope to do the bookkeeping:
518         openScopes(current);
519         closeScopes();
520     }
521
522 private:
523     bool isDBusElement(const QString & namespaceURI,
524                        const QString & localName,
525                        const QString & qName,
526                        const QString & element)
527     {
528         return (namespaceURI.isEmpty() && localName == element && qName == element) ||
529                (namespaceURI.isEmpty() && localName.isEmpty() && qName == element);
530     }
531
532     bool isExtensionElement(const QString & namespaceURI,
533                             const QString & localName,
534                             const QString & qName,
535                             const QString & element)
536     {
537         (void)qName;
538
539         return namespaceURI == EXTENSION_URI && localName == element;
540     }
541
542     bool hasType(const QXmlAttributes & attributes)
543     {
544         const int type_idx(indexOf(attributes, "type"));
545         const int named_type_idx(indexOf(attributes, "named-type"));
546
547         return named_type_idx >= 0 || type_idx >= 0;
548     }
549
550     QString getType(const QXmlAttributes & attributes)
551     {
552         const int type_idx(indexOf(attributes, "type"));
553         const int named_type_idx(indexOf(attributes, "named-type"));
554
555         QCString type;
556
557         if (named_type_idx >= 0)
558         {
559             type = attributes.value(named_type_idx).utf8();
560             if (type.left(2)!="::")
561             { type = getCurrentScope(attributes.value(named_type_idx).utf8()); }
562             else
563             { type = type.mid(2); }
564             if (m_namedTypeMap.contains(type))
565             {
566                 if (type_idx >= 0)
567                 {
568                     const QCString dbus_type(attributes.value(type_idx).utf8());
569                     if (dbus_type != m_namedTypeMap[type])
570                     {
571                         DOC_ERROR(QString("Type \"%1\" does not match up with "
572                                           "previous definition of named type \"%2\" (which was \"%3\".").
573                                           arg(dbus_type).
574                                           arg(type).
575                                           arg(m_namedTypeMap[type]));
576                     }
577                 }
578                 return type;
579             }
580
581             DOC_ERROR(QString("Undefined named type \"%1\" used.").arg(type));
582         }
583
584         if (type_idx >= 0)
585         {
586             type = attributes.value(type_idx).utf8();
587
588             QRegExp reg_exp(QCString("(a?[ybnqiuxdtsogv]|a[{]sv[}])"));
589             if (reg_exp.match(type.data()))
590             { return type; }
591
592             DOC_ERROR(QString("Unnamed complex D-Bus type \"%1\" found.").arg(type));
593         }
594
595         return QString();
596     }
597
598     QString getDBusType(const QCString & type)
599     {
600         QCString scoped_type = type;
601         if (!scoped_type.contains("::"))
602         { scoped_type = getCurrentScope(type); }
603
604         if (m_namedTypeMap.contains(scoped_type))
605         { return m_namedTypeMap[scoped_type]; }
606         else
607         { return type; }
608     }
609
610     void addNamedType(const QCString &type)
611     {
612         QCString scoped_name(getCurrentScope());
613
614         if (m_namedTypeMap.contains(scoped_name))
615         {
616             DOC_ERROR(QString("Named type \"%1\" is already defined.").arg(scoped_name));
617             return;
618         }
619
620         m_namedTypeMap.insert(scoped_name, type);
621     }
622
623     QCString getCurrentScope(const QCString & type = QCString())
624     {
625         QCString scoped_name;
626         if (!m_scopeStack.isEmpty())
627         {
628             scoped_name = m_scopeStack.last()->scope->name;
629             scoped_name.append("::");
630         }
631         if (!type.isEmpty())
632         { scoped_name.append(type); }
633         else
634         { scoped_name = scoped_name.left(scoped_name.length() - 2); }
635
636         return scoped_name;
637     }
638
639     int indexOf(const QXmlAttributes & attributes, const QString & name,
640                  const QString & type = "CDATA", const bool mandatory = true)
641     {
642         const int idx(attributes.index(name));
643         if (idx < 0 || idx > attributes.length()) { return -1; }
644         if (attributes.type(idx) != type) { return -1; }
645         if (mandatory && attributes.value(idx).isEmpty()) { return -1; }
646
647         return idx;
648     }
649
650     Entry * createEntry()
651     {
652         Entry * entry = new Entry();
653
654         entry->protection = Public ;
655         entry->virt       = Normal;
656         entry->stat       = false;
657         entry->lang       = SrcLangExt_XML;
658         entry->spec       = 0;
659
660         entry->fileName  = m_fileName;
661         entry->startLine = lineNumber();
662         entry->bodyLine = lineNumber();
663
664         entry->callGraph = false;
665         entry->callerGraph = false;
666
667         initGroupInfo(entry);
668
669         m_currentEntry = entry;
670
671         handleComment();
672
673         return entry;
674     }
675
676     void openScopes(Entry * object)
677     {
678         int cur_scope_separator_pos = 0;
679         int last_scope_separator_pos = 0;
680         while (0 <= (cur_scope_separator_pos = object->name.find("::", last_scope_separator_pos)))
681         {
682             QString scope = object->name.mid(last_scope_separator_pos,
683                                              cur_scope_separator_pos - last_scope_separator_pos);
684             last_scope_separator_pos = cur_scope_separator_pos + 2;
685
686             Entry * current_namespace = openNamespace(scope);
687
688             if (!m_scopeStack.isEmpty())
689             { m_scopeStack.last()->scope->addSubEntry(current_namespace); }
690
691             m_scopeStack.append(new ScopeData(current_namespace, m_scopeCount));
692         }
693
694         QCString scoped_name(getCurrentScope());
695         if (!scoped_name.isEmpty())
696         { scoped_name.append("::"); }
697         scoped_name.append(object->name.mid(last_scope_separator_pos));
698
699         object->name = scoped_name;
700
701         if (!m_scopeStack.isEmpty())
702         { m_scopeStack.last()->scope->addSubEntry(object); }
703         m_scopeStack.append(new ScopeData(object, m_scopeCount));
704
705         ++m_scopeCount;
706     }
707
708     Entry * openNamespace(const QString & name)
709     {
710         Entry * current_namespace = createEntry();
711         QCString scoped_name(getCurrentScope());
712         if (!scoped_name.isEmpty())
713         { scoped_name.append("::"); }
714         scoped_name.append(name.utf8());
715         current_namespace->name = scoped_name;
716         current_namespace->section = Entry::NAMESPACE_SEC;
717         current_namespace->type = "namespace" ;
718
719         return current_namespace;
720     }
721
722     void closeScopes()
723     {
724         const int current_scope_count(m_scopeStack.last()->count);
725
726         // Do not close the root scope.
727         if (current_scope_count == 0)
728         { return; }
729
730         while (current_scope_count == m_scopeStack.last()->count)
731         { m_scopeStack.removeLast(); }
732     }
733
734     ParserInterface * m_parser;
735
736     QXmlLocator m_locator;
737     QCString m_currentNode; // Nodes can not be nested, no entry necessary.
738
739     struct ElementData
740     {
741         ElementData(const QCString & e) :
742             element(e)
743         { }
744         ~ElementData() { }
745
746         QCString element; //*< The element name
747         QCString text; //*< The actual xml code.
748     };
749     QList<ElementData> m_elementStack;
750
751     Entry * m_currentEntry; // The currently open entry.
752
753     Entry * m_currentInterface; // Interfaces can not be nested.
754     Entry * m_currentMethod; // Methods can not be nested.
755     Argument * m_currentArgument; // Arguments can not be nested.
756     Entry * m_currentProperty; // Properties can not be nested.
757     Entry * m_currentEnum; // Enums can not be nested.
758     QList<Entry> m_namespaceStack;
759
760     struct StructData
761     {
762         StructData(Entry * e) : entry(e) { }
763         ~StructData() { }
764
765         QCString type;
766         Entry * entry;
767     };
768     QList<StructData> m_structStack; // Structs can be nested.
769
770     struct ScopeData
771     {
772         ScopeData(Entry * s, int c) :
773             scope(s),
774             count(c)
775         { }
776         ~ScopeData() { }
777
778         Entry * scope;
779         int count;
780     };
781     QList<ScopeData> m_scopeStack; // Scopes are nested.
782
783     QCString m_fileName;
784
785     struct CommentData
786     {
787         CommentData(const QCString & f, const int l, const QCString & t) :
788             isJavaStyle(false),
789             isQtStyle(false),
790             line(l),
791             fileName(f)
792         {
793             isJavaStyle = t.length()>0 && t.at(0)=='*';
794             isQtStyle = t.length()>0 && t.at(0)=='!';
795             shouldIgnore = (!isJavaStyle && !isQtStyle);
796             associateWithPrevious = (t.length()>1 && t.at(1)=='<');
797             if (associateWithPrevious)
798             { text = t.mid(2); }
799             else
800             { text = t.mid(1); }
801         }
802         ~CommentData() { }
803
804         QCString text;
805         bool isJavaStyle;
806         bool isQtStyle;
807         bool shouldIgnore;
808         bool associateWithPrevious;
809         int line;
810         QCString fileName;
811     };
812     CommentData * m_currentComment;
813
814     int m_scopeCount; //*< unique scope id.
815
816     QString m_errorString;
817
818     QMap<QCString, QCString> m_namedTypeMap;
819 };
820
821 // -----------------------------------------------------------------------
822 // DBusXMLScanner
823 // -----------------------------------------------------------------------
824
825 DBusXMLScanner::DBusXMLScanner()
826 { }
827
828 DBusXMLScanner::~DBusXMLScanner()
829 { }
830
831 void DBusXMLScanner::parseInput(const char * fileName,
832                                 const char * /* fileBuf */,
833                                 Entry * root)
834 {
835     QFile inputFile(fileName);
836
837     QXmlInputSource inputSource(inputFile);
838     QXmlSimpleReader reader;
839
840     DBusXMLHandler handler(this, &reader, fileName, root);
841     reader.setContentHandler(&handler);
842     reader.setErrorHandler(&handler);
843     reader.setLexicalHandler(&handler);
844
845     groupEnterFile(fileName, 1);
846     handler.setSection();
847     reader.parse(inputSource);
848
849     if (!handler.errorString().isEmpty())
850     { err("DBus XML Parser: Error at line %d: %s\n", 
851         handler.locator()->lineNumber(),handler.errorString().utf8().data()); }
852
853     groupLeaveFile(fileName, 1);
854 }
855
856 bool DBusXMLScanner::needsPreprocessing(const QCString & /* extension */)
857 { return (false); }
858
859 void DBusXMLScanner::parseCode(CodeOutputInterface & /* codeOutIntf */,
860                                const char * /* scopeName */,
861                                const QCString & /* input */,
862                                bool /* isExampleBlock */,
863                                const char * /* exampleName */,
864                                FileDef * /* fileDef */,
865                                int /* startLine */,
866                                int /* endLine */,
867                                bool /* inlineFragment */,
868                                MemberDef * /* memberDef */,
869                                bool /*showLineNumbers*/,
870                                Definition * /* searchCtx */)
871 { }
872
873 void DBusXMLScanner::resetCodeParserState()
874 { }
875
876 void DBusXMLScanner::parsePrototype(const char * /* text */)
877 { }