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