]> git.saurik.com Git - wxWidgets.git/blob - utils/ifacecheck/src/xmlparser.cpp
more logic to avoid false warnings
[wxWidgets.git] / utils / ifacecheck / src / xmlparser.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: xmlparser.cpp
3 // Purpose: Parser of the API/interface XML files
4 // Author: Francesco Montorsi
5 // Created: 2008/03/17
6 // RCS-ID: $Id$
7 // Copyright: (c) 2008 Francesco Montorsi
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 // for all others, include the necessary headers
19 #ifndef WX_PRECOMP
20 #endif
21
22 #include "wx/xml/xml.h"
23 #include "wx/wfstream.h"
24 #include "wx/hashmap.h"
25 #include "wx/filename.h"
26 #include "xmlparser.h"
27 #include <errno.h>
28
29 #include <wx/arrimpl.cpp>
30 WX_DEFINE_OBJARRAY(wxTypeArray)
31 WX_DEFINE_OBJARRAY(wxArgumentTypeArray)
32 WX_DEFINE_OBJARRAY(wxMethodArray)
33 WX_DEFINE_OBJARRAY(wxClassArray)
34
35
36 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
37 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
38
39
40 // defined in ifacecheck.cpp
41 extern bool g_verbose;
42
43 // global variable:
44 bool g_bLogEnabled = true;
45
46
47
48 // ----------------------------------------------------------------------------
49 // wxType
50 // ----------------------------------------------------------------------------
51
52 wxType wxEmptyType;
53
54 void wxType::SetTypeFromString(const wxString& t)
55 {
56 /*
57 TODO: optimize the following code writing a single function
58 which works at char-level and does everything in a single pass
59 */
60
61 m_strType = t;
62
63 // [] is the same as * for gccxml
64 m_strType.Replace("[]", "*");
65 m_strType.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
66
67 // make sure the * and & operator always use the same spacing rules
68 // (to make sure GetAsString() output is always consistent)
69 m_strType.Replace("*", "* ");
70 m_strType.Replace("&", "& ");
71 m_strType.Replace(" *", "*");
72 m_strType.Replace(" &", "&");
73
74 while (m_strType.Contains(" "))
75 m_strType.Replace(" ", " "); // do it once again
76
77 m_strType.Replace(" ,", ",");
78
79 m_strType = m_strType.Strip(wxString::both);
80
81 // now set the clean version
82 m_strTypeClean = m_strType;
83 m_strTypeClean.Replace("const", "");
84 m_strTypeClean.Replace("static", "");
85 m_strTypeClean.Replace("*", "");
86 m_strTypeClean.Replace("&", "");
87 m_strTypeClean.Replace("[]", "");
88 m_strTypeClean = m_strTypeClean.Strip(wxString::both);
89
90 // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
91 // need to be considered as the same type
92 if (m_strTypeClean.EndsWith("Base"))
93 m_strTypeClean = m_strTypeClean.Left(m_strTypeClean.Len()-4);
94
95 // ADHOC-FIX:
96 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
97 // fix this to avoid false positives
98 m_strTypeClean.Replace("wxDateTime::", "");
99 m_strTypeClean.Replace("wxStockGDI::", ""); // same story for some other classes
100 m_strTypeClean.Replace("wxHelpEvent::", "");
101 }
102
103 bool wxType::IsOk() const
104 {
105 // NB: m_strType can contain the :: operator; think to e.g. the
106 // "reverse_iterator_impl<wxString::const_iterator>" type
107 // It can also contain commas, * and & operators etc
108
109 return !m_strTypeClean.IsEmpty();
110 }
111
112 bool wxType::operator==(const wxType& m) const
113 {
114 // brain-dead comparison:
115
116 if (m_strTypeClean == m.m_strTypeClean &&
117 IsConst() == m.IsConst() &&
118 IsStatic() == m.IsStatic() &&
119 IsPointer() == m.IsPointer() &&
120 IsReference() == m.IsReference())
121 return true;
122
123 if (g_verbose)
124 LogMessage("Type '%s' does not match type '%s'", m_strType, m.m_strType);
125
126 return false;
127 }
128
129
130 // ----------------------------------------------------------------------------
131 // wxArgumentType
132 // ----------------------------------------------------------------------------
133
134 void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& defvalForCmp)
135 {
136 m_strDefaultValue = defval.Strip(wxString::both);
137 m_strDefaultValueForCmp = defvalForCmp.IsEmpty() ? m_strDefaultValue : defvalForCmp.Strip(wxString::both);
138
139 // adjust aesthetic form of DefaultValue for the modify mode of ifacecheck:
140 // we may need to write it out in an interface header
141 if (m_strDefaultValue == "0u")
142 m_strDefaultValue = "0";
143
144 // in order to make valid&simple comparison on argument defaults,
145 // we reduce some of the multiple forms in which the same things may appear
146 // to a single form:
147 if (m_strDefaultValueForCmp == "0u")
148 m_strDefaultValueForCmp = "0";
149
150 // fix for unicode strings:
151 m_strDefaultValueForCmp.Replace("\\000\\000\\000", "");
152
153 if (m_strDefaultValueForCmp.StartsWith("wxT(") &&
154 m_strDefaultValueForCmp.EndsWith(")"))
155 {
156 // get rid of the wxT() part
157 unsigned int len = m_strDefaultValueForCmp.Len();
158 m_strDefaultValueForCmp = m_strDefaultValueForCmp.Mid(4,len-5);
159 }
160
161 /*
162 if (IsPointer())
163 m_strDefaultValueForCmp.Replace("0", "NULL");
164 else
165 m_strDefaultValueForCmp.Replace("NULL", "0");
166 */
167 // ADHOC-FIX:
168 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
169 // fix this to avoid false positives
170 m_strDefaultValueForCmp.Replace("wxDateTime::", "");
171 m_strDefaultValueForCmp.Replace("wxStockGDI::", ""); // same story for some other classes
172 m_strDefaultValueForCmp.Replace("wxHelpEvent::", ""); // same story for some other classes
173
174 m_strDefaultValueForCmp.Replace("wxGet_wxConvLocal()", "wxConvLocal");
175
176 m_strDefaultValueForCmp.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
177
178 // ADHOC-FIX:
179 if (m_strDefaultValueForCmp.Contains("wxGetTranslation"))
180 m_strDefaultValueForCmp = "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
181 }
182
183 bool wxArgumentType::operator==(const wxArgumentType& m) const
184 {
185 if ((const wxType&)(*this) != (const wxType&)m)
186 return false;
187
188 // ADHOC-FIX:
189 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
190 // numbers; avoid false positives in this case!
191 if (m_strArgName == m.m_strArgName && m_strArgName == "style" &&
192 (m_strDefaultValueForCmp.IsNumber() || m.m_strDefaultValueForCmp.IsNumber()))
193 return true;
194
195 // fix for default values which were replaced by gcc-xml with their numeric values
196 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
197 if (m_strTypeClean == "long" || m_strTypeClean == "int")
198 {
199 if ((m_strDefaultValueForCmp.IsNumber() && m.m_strDefaultValueForCmp.StartsWith("wx")) ||
200 (m.m_strDefaultValueForCmp.IsNumber() && m_strDefaultValueForCmp.StartsWith("wx")))
201 {
202 if (g_verbose)
203 LogMessage("Supposing '%s' default value to be the same of '%s'...",
204 m_strDefaultValueForCmp, m.m_strDefaultValueForCmp);
205
206 return true;
207 }
208 }
209
210 if (m_strDefaultValueForCmp != m.m_strDefaultValueForCmp)
211 {
212 // maybe the default values are numbers.
213 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
214 // To handle these cases, we try to convert the default value strings to numbers:
215 long def1val, def2val;
216 if (m_strDefaultValueForCmp.ToLong(&def1val, 0 /* auto-detect */) &&
217 m.m_strDefaultValueForCmp.ToLong(&def2val, 0 /* auto-detect */))
218 {
219 if (def1val == def2val)
220 return true; // the default values match
221 }
222
223 if (g_verbose)
224 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
225 m_strType, m_strDefaultValueForCmp, m.m_strType, m.m_strDefaultValueForCmp);
226 return false;
227 }
228
229 // we deliberately avoid checks on the argument name
230
231 return true;
232 }
233
234
235 // ----------------------------------------------------------------------------
236 // wxMethod
237 // ----------------------------------------------------------------------------
238
239 bool wxMethod::IsOk() const
240 {
241 // NOTE: m_retType can be a wxEmptyType, and means that this method
242 // is a ctor or a dtor.
243 if (!m_retType.IsOk() && m_retType!=wxEmptyType) {
244 LogError("'%s' method has invalid return type: %s", m_retType.GetAsString());
245 return false;
246 }
247
248 if (m_strName.IsEmpty())
249 return false;
250
251 // a function can't be both const and static or virtual and static!
252 if ((m_bConst && m_bStatic) || ((m_bVirtual || m_bPureVirtual) && m_bStatic)) {
253 LogError("'%s' method can't be both const/static or virtual/static", m_strName);
254 return false;
255 }
256
257 wxASSERT(!m_bPureVirtual || (m_bPureVirtual && m_bVirtual));
258
259 for (unsigned int i=0; i<m_args.GetCount(); i++)
260 if (!m_args[i].IsOk()) {
261 LogError("'%s' method has invalid %d-th argument type: %s",
262 m_strName, i+1, m_args[i].GetAsString());
263 return false;
264 }
265
266 // NB: the default value of the arguments can contain pretty much everything
267 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
268 // so we don't do any test on their contents
269 if (m_args.GetCount()>0)
270 {
271 bool previousArgHasDefault = m_args[0].HasDefaultValue();
272 for (unsigned int i=1; i<m_args.GetCount(); i++)
273 {
274 if (previousArgHasDefault && !m_args[i].HasDefaultValue()) {
275 LogError("'%s' method has %d-th argument which has no default value "
276 "(while the previous one had one!)",
277 m_strName, i+1);
278 return false;
279 }
280
281 previousArgHasDefault = m_args[i].HasDefaultValue();
282 }
283 }
284
285 return true;
286 }
287
288 bool wxMethod::MatchesExceptForAttributes(const wxMethod& m) const
289 {
290 if (GetReturnType() != m.GetReturnType() ||
291 GetName() != m.GetName())
292 return false;
293
294 if (m_args.GetCount()!=m.m_args.GetCount()) {
295 if (g_verbose)
296 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
297 m_strName, m_args.GetCount(), m_strName, m.m_args.GetCount());
298 return false;
299 }
300
301 // compare argument types
302 for (unsigned int i=0; i<m_args.GetCount(); i++)
303 if (m_args[i] != m.m_args[i])
304 return false;
305
306 return true;
307 }
308
309 bool wxMethod::operator==(const wxMethod& m) const
310 {
311 // check attributes
312 if (IsConst() != m.IsConst() ||
313 IsStatic() != m.IsStatic() ||
314 IsVirtual() != m.IsVirtual() ||
315 IsPureVirtual() != m.IsPureVirtual() ||
316 IsDeprecated() != m.IsDeprecated() ||
317 GetAccessSpecifier() != m.GetAccessSpecifier())
318 return false;
319
320 // check everything else
321 return MatchesExceptForAttributes(m);
322 }
323
324 wxString wxMethod::GetAsString(bool bWithArgumentNames, bool bCleanDefaultValues,
325 bool bDeprecated, bool bAccessSpec) const
326 {
327 wxString ret;
328
329 // NOTE: for return and argument types, never use wxType::GetAsCleanString
330 // since in that way we'd miss important decorators like &,*,const etc
331
332 if (m_retType!=wxEmptyType)
333 ret += m_retType.GetAsString() + " ";
334 //else; this is a ctor or dtor
335
336 ret += m_strName + "(";
337
338 for (unsigned int i=0; i<m_args.GetCount(); i++)
339 {
340 ret += m_args[i].GetAsString();
341
342 const wxString& name = m_args[i].GetArgumentName();
343 if (bWithArgumentNames && !name.IsEmpty())
344 ret += " " + name;
345
346 const wxString& def = bCleanDefaultValues ?
347 m_args[i].GetDefaultCleanValue() : m_args[i].GetDefaultValue();
348 if (!def.IsEmpty())
349 ret += " = " + def;
350
351 ret += ", ";
352 }
353
354 if (m_args.GetCount()>0)
355 ret = ret.Left(ret.Len()-2);
356
357 ret += ")";
358
359 if (m_bConst)
360 ret += " const";
361 if (m_bStatic)
362 ret = "static " + ret;
363 if (m_bVirtual || m_bPureVirtual)
364 ret = "virtual " + ret;
365 if (m_bPureVirtual)
366 ret += " = 0";
367 if (m_bDeprecated && bDeprecated)
368 ret += " [deprecated]";
369
370 if (bAccessSpec)
371 {
372 switch (m_access)
373 {
374 case wxMAS_PUBLIC:
375 ret += " [public]";
376 break;
377 case wxMAS_PROTECTED:
378 ret += " [protected]";
379 break;
380 case wxMAS_PRIVATE:
381 ret += " [private]";
382 break;
383 }
384 }
385
386 return ret;
387 }
388
389 void wxMethod::Dump(wxTextOutputStream& stream) const
390 {
391 stream << "[" + m_retType.GetAsString() + "]";
392 stream << "[" + m_strName + "]";
393
394 for (unsigned int i=0; i<m_args.GetCount(); i++)
395 stream << "[" + m_args[i].GetAsString() + " " + m_args[i].GetArgumentName() +
396 "=" + m_args[i].GetDefaultValue() + "]";
397
398 if (IsConst())
399 stream << " CONST";
400 if (IsStatic())
401 stream << " STATIC";
402 if (IsVirtual())
403 stream << " VIRTUAL";
404 if (IsPureVirtual())
405 stream << " PURE-VIRTUAL";
406 if (IsDeprecated())
407 stream << " DEPRECATED";
408
409 // no final newline
410 }
411
412 // ----------------------------------------------------------------------------
413 // wxClass
414 // ----------------------------------------------------------------------------
415
416 wxString wxClass::GetNameWithoutTemplate() const
417 {
418 // NB: I'm not sure this is the right terminology for this function!
419
420 if (m_strName.Contains("<"))
421 return m_strName.Left(m_strName.Find("<"));
422 return m_strName;
423 }
424
425 bool wxClass::IsValidCtorForThisClass(const wxMethod& m) const
426 {
427 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
428 // named wxWritableCharTypeBuffer, without the <...> part!
429
430 if (m.IsCtor() && m.GetName() == GetNameWithoutTemplate())
431 return true;
432
433 return false;
434 }
435
436 bool wxClass::IsValidDtorForThisClass(const wxMethod& m) const
437 {
438 if (m.IsDtor() && m.GetName() == "~" + GetNameWithoutTemplate())
439 return true;
440
441 return false;
442 }
443
444 void wxClass::Dump(wxTextOutputStream& out) const
445 {
446 out << m_strName + "\n";
447
448 for (unsigned int i=0; i<m_methods.GetCount(); i++) {
449
450 // dump all our methods
451 out << "|- ";
452 m_methods[i].Dump(out);
453 out << "\n";
454 }
455
456 out << "\n";
457 }
458
459 bool wxClass::CheckConsistency() const
460 {
461 for (unsigned int i=0; i<m_methods.GetCount(); i++)
462 for (unsigned int j=0; j<m_methods.GetCount(); j++)
463 if (i!=j && m_methods[i] == m_methods[j])
464 {
465 LogError("class %s has two methods with the same prototype: '%s'",
466 m_strName, m_methods[i].GetAsString());
467 return false;
468 ((wxClass*)this)->m_methods.RemoveAt(j);
469 j--;
470 }
471
472 return true;
473 }
474
475 const wxMethod* wxClass::FindMethod(const wxMethod& m) const
476 {
477 for (unsigned int i=0; i<m_methods.GetCount(); i++)
478 if (m_methods[i] == m)
479 return &m_methods[i];
480 return NULL;
481 }
482
483 wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const
484 {
485 wxMethodPtrArray ret;
486
487 for (unsigned int i=0; i<m_methods.GetCount(); i++)
488 if (m_methods[i].GetName() == name)
489 ret.Add(&m_methods[i]);
490
491 return ret;
492 }
493
494
495 // ----------------------------------------------------------------------------
496 // wxXmlInterface
497 // ----------------------------------------------------------------------------
498
499 WX_DEFINE_SORTED_ARRAY(wxClass*, wxSortedClassArray);
500
501 int CompareWxClassObjects(wxClass *item1, wxClass *item2)
502 {
503 // sort alphabetically
504 return item1->GetName().Cmp(item2->GetName());
505 }
506
507 void wxXmlInterface::Dump(const wxString& filename)
508 {
509 wxFFileOutputStream apioutput( filename );
510 wxTextOutputStream apiout( apioutput );
511
512 // dump the classes in alphabetical order
513 wxSortedClassArray sorted(CompareWxClassObjects);
514 sorted.Alloc(m_classes.GetCount());
515 for (unsigned int i=0; i<m_classes.GetCount(); i++)
516 sorted.Add(&m_classes[i]);
517
518 // now they have been sorted
519 for (unsigned int i=0; i<sorted.GetCount(); i++)
520 sorted[i]->Dump(apiout);
521 }
522
523 bool wxXmlInterface::CheckParseResults() const
524 {
525 // this check can be quite slow, so do it only for debug releases:
526 //#ifdef __WXDEBUG__
527 for (unsigned int i=0; i<m_classes.GetCount(); i++)
528 if (!m_classes[i].CheckConsistency())
529 return false;
530 //#endif
531
532 return true;
533 }
534
535 wxClassPtrArray wxXmlInterface::FindClassesDefinedIn(const wxString& headerfile) const
536 {
537 wxClassPtrArray ret;
538
539 for (unsigned int i=0; i<m_classes.GetCount(); i++)
540 if (m_classes[i].GetHeader() == headerfile)
541 ret.Add(&m_classes[i]);
542
543 return ret;
544 }
545
546
547 // ----------------------------------------------------------------------------
548 // wxXmlGccInterface helper declarations
549 // ----------------------------------------------------------------------------
550
551 // or-able flags for a toResolveTypeItem->attrib:
552 #define ATTRIB_CONST 1
553 #define ATTRIB_REFERENCE 2
554 #define ATTRIB_POINTER 4
555 #define ATTRIB_ARRAY 8
556
557 #define GCCXML_BASE 35
558
559 class toResolveTypeItem
560 {
561 public:
562 toResolveTypeItem() { attribs=0; }
563 toResolveTypeItem(unsigned int refID, unsigned int attribint)
564 : ref(refID), attribs(attribint) {}
565
566 unsigned long ref, // the referenced type's ID
567 attribs; // the attributes of this reference
568 };
569
570 #if 1
571
572 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
573 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem,
574 wxIntegerHash, wxIntegerEqual,
575 wxToResolveTypeHashMap );
576
577 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
578 WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
579 wxIntegerHash, wxIntegerEqual,
580 wxClassMemberIdHashMap );
581 #else
582 #include <map>
583 typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap;
584 #endif
585
586
587 // utility to parse gccXML ID values;
588 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
589 // but is a little bit faster
590 bool getID(unsigned long *id, const wxStringCharType* str)
591 {
592 wxStringCharType *end;
593 #if wxUSE_UNICODE_WCHAR
594 unsigned long val = wcstoul(str+1, &end, GCCXML_BASE);
595 #else
596 unsigned long val = strtoul(str+1, &end, GCCXML_BASE);
597 #endif
598
599 // return true only if scan was stopped by the terminating NUL and
600 // if the string was not empty to start with and no under/overflow
601 // occurred:
602 if ( *end != '\0' || end == str+1 || errno == ERANGE || errno == EINVAL )
603 return false;
604
605 *id = val;
606 return true;
607 }
608
609 // utility specialized to parse efficiently the gccXML list of IDs which occur
610 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
611 bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxStringCharType* str)
612 {
613 #if wxUSE_UNICODE_WCHAR
614 size_t len = wcslen(str);
615 #else
616 size_t len = strlen(str);
617 #endif
618
619 if (len == 0 || str[0] != '_')
620 return false;
621
622 const wxStringCharType *curpos = str,
623 *end = str + len;
624 wxStringCharType *nexttoken;
625
626 while (curpos < end)
627 {
628 // curpos always points to the underscore of the next token to parse:
629 #if wxUSE_UNICODE_WCHAR
630 unsigned long id = wcstoul(curpos+1, &nexttoken, GCCXML_BASE);
631 #else
632 unsigned long id = strtoul(curpos+1, &nexttoken, GCCXML_BASE);
633 #endif
634 if ( *nexttoken != ' ' || errno == ERANGE || errno == EINVAL )
635 return false;
636
637 // advance current position
638 curpos = nexttoken + 1;
639
640 // add this ID to the hashmap
641 wxClassMemberIdHashMap::value_type v(id, p);
642 map->insert(v);
643 }
644
645 return true;
646 }
647
648
649 // ----------------------------------------------------------------------------
650 // wxXmlGccInterface
651 // ----------------------------------------------------------------------------
652
653 bool wxXmlGccInterface::Parse(const wxString& filename)
654 {
655 wxXmlDocument doc;
656 wxXmlNode *child;
657 int nodes = 0;
658
659 LogMessage("Parsing %s...", filename);
660
661 if (!doc.Load(filename)) {
662 LogError("can't load %s", filename);
663 return false;
664 }
665
666 // start processing the XML file
667 if (doc.GetRoot()->GetName() != "GCC_XML") {
668 LogError("invalid root node for %s", filename);
669 return false;
670 }
671
672 wxString version = doc.GetRoot()->GetAttribute("cvs_revision");
673 bool old = false;
674
675 #define MIN_REVISION 120
676
677 if (!version.StartsWith("1."))
678 old = true;
679 if (!old)
680 {
681 unsigned long rev = 0;
682 if (!version.Mid(2).ToULong(&rev))
683 old = true;
684 else
685 if (rev < MIN_REVISION)
686 old = true;
687 }
688
689 if (old)
690 {
691 LogError("The version of GCC-XML used for the creation of %s is too old; "
692 "the cvs_revision attribute of the root node reports '%s', "
693 "minimal required is 1.%d.", filename, version, MIN_REVISION);
694 return false;
695 }
696
697 wxToResolveTypeHashMap toResolveTypes;
698 wxClassMemberIdHashMap members;
699 wxTypeIdHashMap types;
700 wxTypeIdHashMap files;
701 wxTypeIdHashMap typedefs;
702
703 // prealloc quite a lot of memory!
704 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
705
706 // build a list of wx classes and in general of all existent types
707 child = doc.GetRoot()->GetChildren();
708 while (child)
709 {
710 const wxString& n = child->GetName();
711
712 unsigned long id = 0;
713 if (!getID(&id, child->GetAttribute("id")) || (id == 0 && n != "File")) {
714
715 // NOTE: <File> nodes can have an id == "f0"...
716
717 LogError("Invalid id for node %s: %s", n, child->GetAttribute("id"));
718 return false;
719 }
720
721 if (n == "Class")
722 {
723 wxString cname = child->GetAttribute("name");
724 if (cname.IsEmpty()) {
725 LogError("Invalid empty name for '%s' node", n);
726 return false;
727 }
728
729 // only register wx classes (do remember also the IDs of their members)
730 if (cname.StartsWith("wx"))
731 {
732 // NB: "file" attribute contains an ID value that we'll resolve later
733 m_classes.Add(wxClass(cname, child->GetAttribute("file")));
734
735 const wxString& ids = child->GetAttribute("members");
736 if (ids.IsEmpty())
737 {
738 if (child->GetAttribute("incomplete") != "1") {
739 LogError("Invalid member IDs for '%s' class node: %s",
740 cname, child->GetAttribute("id"));
741 return false;
742 }
743 //else: don't warn the user; it looks like "incomplete" classes
744 // never have any member...
745 }
746 else
747 {
748 // decode the non-empty list of IDs:
749 if (!getMemberIDs(&members, &m_classes.Last(), ids)) {
750 LogError("Invalid member IDs for '%s' class node: %s",
751 cname, child->GetAttribute("id"));
752 return false;
753 }
754 }
755 }
756
757 // register this class also as possible return/argument type:
758 types[id] = cname;
759 }
760 else if (n == "Typedef")
761 {
762 unsigned long typeId = 0;
763 if (!getID(&typeId, child->GetAttribute("type"))) {
764 LogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
765 return false;
766 }
767
768 // this typedef node tell us that every type referenced with the
769 // "typeId" ID should be called with another name:
770 wxString name = child->GetAttribute("name");
771
772 // save this typedef in a separate hashmap...
773 typedefs[typeId] = name;
774
775 types[id] = name;
776 }
777 else if (n == "PointerType" || n == "ReferenceType" ||
778 n == "CvQualifiedType" || n == "ArrayType")
779 {
780 unsigned long type = 0;
781 if (!getID(&type, child->GetAttribute("type")) || type == 0) {
782 LogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
783 return false;
784 }
785
786 unsigned long attr = 0;
787 if (n == "PointerType")
788 attr = ATTRIB_POINTER;
789 else if (n == "ReferenceType")
790 attr = ATTRIB_REFERENCE;
791 else if (n == "CvQualifiedType" && child->GetAttribute("const") == "1")
792 attr = ATTRIB_CONST;
793 else if (n == "ArrayType")
794 attr = ATTRIB_ARRAY;
795
796 // these nodes make reference to other types... we'll resolve them later
797 toResolveTypes[id] = toResolveTypeItem(type, attr);
798 }
799 else if (n == "FunctionType" || n == "MethodType")
800 {
801 /*
802 TODO: parsing FunctionType and MethodType nodes is not as easy
803 as for other "simple" types.
804 */
805
806 wxString argstr;
807 wxXmlNode *arg = child->GetChildren();
808 while (arg)
809 {
810 if (arg->GetName() == "Argument")
811 argstr += arg->GetAttribute("type") + ", ";
812 arg = arg->GetNext();
813 }
814
815 if (argstr.Len() > 0)
816 argstr = argstr.Left(argstr.Len()-2); // remove final comma
817
818 // these nodes make reference to other types... we'll resolve them later
819 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
820 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
821
822 types[id] = "TOFIX"; // typically this type will be "fixed" thanks
823 // to a typedef later...
824 }
825 else if (n == "File")
826 {
827 if (!child->GetAttribute("id").StartsWith("f")) {
828 LogError("Unexpected file ID: %s", child->GetAttribute("id"));
829 return false;
830 }
831
832 // just ignore this node... all file IDs/names were already parsed
833 files[id] = child->GetAttribute("name");
834 }
835 else
836 {
837 // we register everything else as a possible return/argument type:
838 const wxString& name = child->GetAttribute("name");
839
840 if (!name.IsEmpty())
841 {
842 //typeIds.Add(id);
843 //typeNames.Add(name);
844 types[id] = name;
845 }
846 else
847 {
848 // this may happen with unnamed structs/union, special ctors,
849 // or other exotic things which we are not interested to, since
850 // they're never used as return/argument types by wxWidgets methods
851
852 if (g_verbose)
853 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
854 n, child->GetAttribute("id"));
855
856 types[id] = "TOFIX";
857 }
858 }
859
860 child = child->GetNext();
861
862 // give feedback to the user about the progress...
863 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
864 }
865
866 // some nodes with IDs referenced by methods as return/argument types, do reference
867 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
868 // thus we need to resolve their name iteratively:
869 while (toResolveTypes.size()>0)
870 {
871 if (g_verbose)
872 LogMessage("%d types were collected; %d types need yet to be resolved...",
873 types.size(), toResolveTypes.size());
874
875 for (wxToResolveTypeHashMap::iterator i = toResolveTypes.begin();
876 i != toResolveTypes.end();)
877 {
878 unsigned long id = i->first;
879 unsigned long referenced = i->second.ref;
880
881 wxTypeIdHashMap::iterator primary = types.find(referenced);
882 if (primary != types.end())
883 {
884 // this to-resolve-type references a "primary" type
885
886 wxString newtype = primary->second;
887 int attribs = i->second.attribs;
888
889 // attribs may contain a combination of ATTRIB_* flags:
890 if (attribs & ATTRIB_CONST)
891 newtype = "const " + newtype;
892 if (attribs & ATTRIB_REFERENCE)
893 newtype = newtype + "&";
894 if (attribs & ATTRIB_POINTER)
895 newtype = newtype + "*";
896 if (attribs & ATTRIB_ARRAY)
897 newtype = newtype + "[]";
898
899 // add the resolved type to the list of "primary" types
900 if (newtype.Contains("TOFIX") && typedefs[id] != "")
901 types[id] = typedefs[id]; // better use a typedef for this type!
902 else
903 types[id] = newtype;
904
905 // this one has been resolved; erase it through its iterator!
906 toResolveTypes.erase(i);
907
908 // now iterator i is invalid; assign it again to the beginning
909 i = toResolveTypes.begin();
910 }
911 else
912 {
913 // then search in the referenced types themselves:
914 wxToResolveTypeHashMap::iterator idx2 = toResolveTypes.find(referenced);
915 if (idx2 != toResolveTypes.end())
916 {
917 // merge this to-resolve-type with the idx2->second type
918 i->second.ref = idx2->second.ref;
919 i->second.attribs |= idx2->second.attribs;
920
921 // this type will eventually be solved in the next while() iteration
922 i++;
923 }
924 else
925 {
926 LogError("Cannot solve '%d' reference type!", referenced);
927 return false;
928 }
929 }
930 }
931 }
932
933 // resolve header names
934 for (unsigned int i=0; i<m_classes.GetCount(); i++)
935 {
936 unsigned long fileID = 0;
937 if (!getID(&fileID, m_classes[i].GetHeader()) || fileID == 0) {
938 LogError("invalid header id: %s", m_classes[i].GetHeader());
939 return false;
940 }
941
942 // search this file
943 wxTypeIdHashMap::const_iterator idx = files.find(fileID);
944 if (idx == files.end())
945 {
946 // this is an error!
947 LogError("couldn't find file ID '%s'", m_classes[i].GetHeader());
948 }
949 else
950 m_classes[i].SetHeader(idx->second);
951 }
952
953 // build the list of the wx methods
954 child = doc.GetRoot()->GetChildren();
955 while (child)
956 {
957 wxString n = child->GetName(), acc = child->GetAttribute("access");
958
959 // only register public&protected methods
960 if ((acc == "public" || acc == "protected") &&
961 (n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod"))
962 {
963 unsigned long id = 0;
964 if (!getID(&id, child->GetAttribute("id"))) {
965 LogError("invalid ID for node '%s' with ID '%s'", n, child->GetAttribute("id"));
966 return false;
967 }
968
969 wxClassMemberIdHashMap::const_iterator it = members.find(id);
970 if (it != members.end())
971 {
972 wxClass *p = it->second;
973
974 // this <Method> node is a method of the i-th class!
975 wxMethod newfunc;
976 if (!ParseMethod(child, types, newfunc)) {
977 LogError("The method '%s' could not be added to class '%s'",
978 child->GetAttribute("demangled"), p->GetName());
979 return false;
980 }
981
982 // do some additional check that we can do only here:
983
984 if (newfunc.IsCtor() && !p->IsValidCtorForThisClass(newfunc)) {
985 LogError("The method '%s' does not seem to be a ctor for '%s'",
986 newfunc.GetName(), p->GetName());
987 return false;
988 }
989 if (newfunc.IsDtor() && !p->IsValidDtorForThisClass(newfunc)) {
990 LogError("The method '%s' does not seem to be a dtor for '%s'",
991 newfunc.GetName(), p->GetName());
992 return false;
993 }
994
995 p->AddMethod(newfunc);
996 }
997 }
998
999 child = child->GetNext();
1000
1001 // give feedback to the user about the progress...
1002 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
1003 }
1004
1005 if (!CheckParseResults())
1006 return false;
1007
1008 return true;
1009 }
1010
1011 bool wxXmlGccInterface::ParseMethod(const wxXmlNode *p,
1012 const wxTypeIdHashMap& types,
1013 wxMethod& m)
1014 {
1015 // get the real name
1016 wxString name = p->GetAttribute("name").Strip(wxString::both);
1017 if (p->GetName() == "Destructor")
1018 name = "~" + name;
1019 else if (p->GetName() == "OperatorMethod")
1020 name = "operator" + name;
1021
1022 // resolve return type
1023 wxType ret;
1024 unsigned long retid = 0;
1025 if (!getID(&retid, p->GetAttribute("returns")) || retid == 0)
1026 {
1027 if (p->GetName() != "Destructor" && p->GetName() != "Constructor") {
1028 LogError("Empty return ID for method '%s', with ID '%s'",
1029 name, p->GetAttribute("id"));
1030 return false;
1031 }
1032 }
1033 else
1034 {
1035 wxTypeIdHashMap::const_iterator retidx = types.find(retid);
1036 if (retidx == types.end()) {
1037 LogError("Could not find return type ID '%s'", retid);
1038 return false;
1039 }
1040
1041 ret = wxType(retidx->second);
1042 if (!ret.IsOk()) {
1043 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1044 retidx->second, name, p->GetAttribute("id"));
1045 return false;
1046 }
1047 }
1048
1049 // resolve argument types
1050 wxArgumentTypeArray argtypes;
1051 wxXmlNode *arg = p->GetChildren();
1052 while (arg)
1053 {
1054 if (arg->GetName() == "Argument")
1055 {
1056 unsigned long id = 0;
1057 if (!getID(&id, arg->GetAttribute("type")) || id == 0) {
1058 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1059 arg->GetAttribute("type"), name, p->GetAttribute("id"));
1060 return false;
1061 }
1062
1063 wxTypeIdHashMap::const_iterator idx = types.find(id);
1064 if (idx == types.end()) {
1065 LogError("Could not find argument type ID '%s'", id);
1066 return false;
1067 }
1068
1069 argtypes.Add(wxArgumentType(idx->second,
1070 arg->GetAttribute("default"),
1071 arg->GetAttribute("name")));
1072 }
1073
1074 arg = arg->GetNext();
1075 }
1076
1077 m.SetReturnType(ret);
1078 m.SetName(name);
1079 m.SetArgumentTypes(argtypes);
1080 m.SetConst(p->GetAttribute("const") == "1");
1081 m.SetStatic(p->GetAttribute("static") == "1");
1082
1083 // NOTE: gccxml is smart enough to mark as virtual those functions
1084 // which are declared virtual in base classes but don't have
1085 // the "virtual" keyword explicitely indicated in the derived
1086 // classes... so we don't need any further logic for virtuals
1087
1088 m.SetVirtual(p->GetAttribute("virtual") == "1");
1089 m.SetPureVirtual(p->GetAttribute("pure_virtual") == "1");
1090 m.SetDeprecated(p->GetAttribute("attributes") == "deprecated");
1091
1092 // decode access specifier
1093 if (p->GetAttribute("access") == "public")
1094 m.SetAccessSpecifier(wxMAS_PUBLIC);
1095 else if (p->GetAttribute("access") == "protected")
1096 m.SetAccessSpecifier(wxMAS_PROTECTED);
1097 else if (p->GetAttribute("access") == "private")
1098 m.SetAccessSpecifier(wxMAS_PRIVATE);
1099
1100 if (!m.IsOk()) {
1101 LogError("The prototype '%s' is not valid!", m.GetAsString());
1102 return false;
1103 }
1104
1105 return true;
1106 }
1107
1108
1109
1110 // ----------------------------------------------------------------------------
1111 // wxXmlDoxygenInterface global helpers
1112 // ----------------------------------------------------------------------------
1113
1114 static wxString GetTextFromChildren(const wxXmlNode *n)
1115 {
1116 wxString text;
1117
1118 // consider the tree
1119 //
1120 // <a><b>this</b> is a <b>string</b></a>
1121 //
1122 // <a>
1123 // |- <b>
1124 // | |- this
1125 // |- is a
1126 // |- <b>
1127 // |- string
1128 //
1129 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1130 // this function returns "this is a string"
1131
1132 wxXmlNode *ref = n->GetChildren();
1133 while (ref) {
1134 if (ref->GetType() == wxXML_ELEMENT_NODE)
1135 text += ref->GetNodeContent();
1136 else if (ref->GetType() == wxXML_TEXT_NODE)
1137 text += ref->GetContent();
1138 else
1139 LogWarning("Unexpected node type while getting text from '%s' node", n->GetName());
1140
1141 ref = ref->GetNext();
1142 }
1143
1144 return text;
1145 }
1146
1147 static bool HasTextNodeContaining(const wxXmlNode *parent, const wxString& name)
1148 {
1149 if (!parent)
1150 return false;
1151
1152 wxXmlNode *p = parent->GetChildren();
1153 while (p)
1154 {
1155 switch (p->GetType())
1156 {
1157 case wxXML_TEXT_NODE:
1158 if (p->GetContent() == name)
1159 return true;
1160 break;
1161
1162 case wxXML_ELEMENT_NODE:
1163 // recurse into this node...
1164 if (HasTextNodeContaining(p, name))
1165 return true;
1166 break;
1167
1168 default:
1169 // skip it
1170 break;
1171 }
1172
1173 p = p->GetNext();
1174 }
1175
1176 return false;
1177 }
1178
1179 static const wxXmlNode* FindNodeNamed(const wxXmlNode* parent, const wxString& name)
1180 {
1181 if (!parent)
1182 return NULL;
1183
1184 const wxXmlNode *p = parent->GetChildren();
1185 while (p)
1186 {
1187 if (p->GetName() == name)
1188 return p; // found!
1189
1190 // search recursively in the children of this node
1191 const wxXmlNode *ret = FindNodeNamed(p, name);
1192 if (ret)
1193 return ret;
1194
1195 p = p->GetNext();
1196 }
1197
1198 return NULL;
1199 }
1200
1201 int GetAvailabilityFor(const wxXmlNode *node)
1202 {
1203 // identify <onlyfor> custom XML tags
1204 const wxXmlNode* onlyfor = FindNodeNamed(node, "onlyfor");
1205 if (!onlyfor)
1206 return wxPORT_UNKNOWN;
1207
1208 wxArrayString ports = wxSplit(onlyfor->GetNodeContent(), ',');
1209 int nAvail = wxPORT_UNKNOWN;
1210 for (unsigned int i=0; i < ports.GetCount(); i++)
1211 {
1212 if (!ports[i].StartsWith("wx")) {
1213 LogError("unexpected port ID '%s'", ports[i]);
1214 return false;
1215 }
1216
1217 nAvail |= wxPlatformInfo::GetPortId(ports[i].Mid(2));
1218 }
1219
1220 return nAvail;
1221 }
1222
1223
1224 // ----------------------------------------------------------------------------
1225 // wxXmlDoxygenInterface
1226 // ----------------------------------------------------------------------------
1227
1228 bool wxXmlDoxygenInterface::Parse(const wxString& filename)
1229 {
1230 wxXmlDocument index;
1231 wxXmlNode *compound;
1232
1233 LogMessage("Parsing %s...", filename);
1234
1235 if (!index.Load(filename)) {
1236 LogError("can't load %s", filename);
1237 return false;
1238 }
1239
1240 // start processing the index:
1241 if (index.GetRoot()->GetName() != "doxygenindex") {
1242 LogError("invalid root node for %s", filename);
1243 return false;
1244 }
1245
1246 /*
1247 NB: we may need in future to do a version-check here if the
1248 format of the XML generated by doxygen changes.
1249 For now (doxygen version 1.5.5), this check is not required
1250 since AFAIK the XML format never changed since it was introduced.
1251 */
1252
1253 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
1254
1255 // process files referenced by this index file
1256 compound = index.GetRoot()->GetChildren();
1257 while (compound)
1258 {
1259 if (compound->GetName() == "compound" &&
1260 compound->GetAttribute("kind") == "class")
1261 {
1262 wxString refid = compound->GetAttribute("refid");
1263
1264 wxFileName fn(filename);
1265 if (!ParseCompoundDefinition(fn.GetPath(wxPATH_GET_SEPARATOR) + refid + ".xml"))
1266 return false;
1267 }
1268
1269 compound = compound->GetNext();
1270 }
1271 //wxPrint("\n");
1272
1273 if (!CheckParseResults())
1274 return false;
1275
1276 return true;
1277 }
1278
1279 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
1280 {
1281 wxXmlDocument doc;
1282 wxXmlNode *child;
1283 int nodes = 0;
1284
1285 if (g_verbose)
1286 LogMessage("Parsing %s...", filename);
1287
1288 if (!doc.Load(filename)) {
1289 LogError("can't load %s", filename);
1290 return false;
1291 }
1292
1293 // start processing this compound definition XML
1294 if (doc.GetRoot()->GetName() != "doxygen") {
1295 LogError("invalid root node for %s", filename);
1296 return false;
1297 }
1298
1299 // build a list of wx classes
1300 child = doc.GetRoot()->GetChildren();
1301 while (child)
1302 {
1303 if (child->GetName() == "compounddef" &&
1304 child->GetAttribute("kind") == "class")
1305 {
1306 // parse this class
1307 wxClass klass;
1308 wxString absoluteFile, header;
1309
1310 wxXmlNode *subchild = child->GetChildren();
1311 while (subchild)
1312 {
1313 wxString kind = subchild->GetAttribute("kind");
1314
1315 // parse only public&protected functions:
1316 if (subchild->GetName() == "sectiondef" &&
1317 (kind == "public-func" || kind == "protected-func"))
1318 {
1319
1320 wxXmlNode *membernode = subchild->GetChildren();
1321 while (membernode)
1322 {
1323 if (membernode->GetName() == "memberdef" &&
1324 membernode->GetAttribute("kind") == "function")
1325 {
1326
1327 wxMethod m;
1328 if (!ParseMethod(membernode, m, header)) {
1329 LogError("The method '%s' could not be added to class '%s'",
1330 m.GetName(), klass.GetName());
1331 return false;
1332 }
1333
1334 if (kind == "public-func")
1335 m.SetAccessSpecifier(wxMAS_PUBLIC);
1336 else if (kind == "protected-func")
1337 m.SetAccessSpecifier(wxMAS_PROTECTED);
1338 else if (kind == "private-func")
1339 m.SetAccessSpecifier(wxMAS_PRIVATE);
1340
1341 if (absoluteFile.IsEmpty())
1342 absoluteFile = header;
1343 else if (header != absoluteFile)
1344 {
1345 LogError("The method '%s' is documented in a different "
1346 "file from others (which belong to '%s') ?",
1347 header, absoluteFile);
1348 return false;
1349 }
1350
1351 klass.AddMethod(m);
1352 }
1353
1354 membernode = membernode->GetNext();
1355 }
1356
1357 // all methods of this class were taken from the header "absoluteFile":
1358 klass.SetHeader(absoluteFile);
1359 }
1360 else if (subchild->GetName() == "compoundname")
1361 {
1362 klass.SetName(subchild->GetNodeContent());
1363 }
1364 /*else if (subchild->GetName() == "includes")
1365 {
1366 // NOTE: we'll get the header from the <location> tags
1367 // scattered inside <memberdef> tags instead of
1368 // this <includes> tag since it does not contain
1369 // the absolute path of the header
1370
1371 klass.SetHeader(subchild->GetNodeContent());
1372 }*/
1373 else if (subchild->GetName() == "detaileddescription")
1374 {
1375 // identify <onlyfor> custom XML tags
1376 klass.SetAvailability(GetAvailabilityFor(subchild));
1377 }
1378
1379 subchild = subchild->GetNext();
1380 }
1381
1382 // add a new class
1383 if (klass.IsOk())
1384 m_classes.Add(klass);
1385 else if (g_verbose)
1386 LogWarning("discarding class '%s' with %d methods...",
1387 klass.GetName(), klass.GetMethodCount());
1388 }
1389
1390 child = child->GetNext();
1391
1392 // give feedback to the user about the progress...
1393 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
1394 }
1395
1396 return true;
1397 }
1398
1399 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode* p, wxMethod& m, wxString& header)
1400 {
1401 wxArgumentTypeArray args;
1402 long line;
1403
1404 wxXmlNode *child = p->GetChildren();
1405 while (child)
1406 {
1407 if (child->GetName() == "name")
1408 m.SetName(child->GetNodeContent());
1409 else if (child->GetName() == "type")
1410 m.SetReturnType(wxType(GetTextFromChildren(child)));
1411 else if (child->GetName() == "param")
1412 {
1413 wxString typestr, namestr, defstr, arrstr;
1414 wxXmlNode *n = child->GetChildren();
1415 while (n)
1416 {
1417 if (n->GetName() == "type")
1418 // if the <type> node has children, they should be all TEXT and <ref> nodes
1419 // and we need to take the text they contain, in the order they appear
1420 typestr = GetTextFromChildren(n);
1421 else if (n->GetName() == "declname")
1422 namestr = GetTextFromChildren(n);
1423 else if (n->GetName() == "defval")
1424 defstr = GetTextFromChildren(n).Strip(wxString::both);
1425 else if (n->GetName() == "array")
1426 arrstr = GetTextFromChildren(n);
1427
1428 n = n->GetNext();
1429 }
1430
1431 if (typestr.IsEmpty()) {
1432 LogError("cannot find type node for a param in method '%s'", m.GetName());
1433 return false;
1434 }
1435
1436 wxArgumentType newarg(typestr + arrstr, defstr, namestr);
1437
1438 // can we use preprocessor output to transform the default value
1439 // into the same form which gets processed by wxXmlGccInterface?
1440 wxStringHashMap::const_iterator it = m_preproc.find(defstr);
1441 if (it != m_preproc.end())
1442 newarg.SetDefaultValue(defstr, it->second);
1443
1444 args.Add(newarg);
1445 }
1446 else if (child->GetName() == "location")
1447 {
1448 line = -1;
1449 if (child->GetAttribute("line").ToLong(&line))
1450 m.SetLocation((int)line);
1451 header = child->GetAttribute("file");
1452 }
1453 else if (child->GetName() == "detaileddescription")
1454 {
1455 // when a method has a @deprecated tag inside its description,
1456 // Doxygen outputs somewhere nested inside <detaileddescription>
1457 // a <xreftitle>Deprecated</xreftitle> tag.
1458 m.SetDeprecated(HasTextNodeContaining(child, "Deprecated"));
1459
1460 // identify <onlyfor> custom XML tags
1461 m.SetAvailability(GetAvailabilityFor(child));
1462 }
1463
1464 child = child->GetNext();
1465 }
1466
1467 m.SetArgumentTypes(args);
1468 m.SetConst(p->GetAttribute("const")=="yes");
1469 m.SetStatic(p->GetAttribute("static")=="yes");
1470
1471 // NOTE: Doxygen is smart enough to mark as virtual those functions
1472 // which are declared virtual in base classes but don't have
1473 // the "virtual" keyword explicitely indicated in the derived
1474 // classes... so we don't need any further logic for virtuals
1475
1476 m.SetVirtual(p->GetAttribute("virt")=="virtual");
1477 m.SetPureVirtual(p->GetAttribute("virt")=="pure-virtual");
1478
1479 if (!m.IsOk()) {
1480 LogError("The prototype '%s' is not valid!", m.GetAsString());
1481 return false;
1482 }
1483
1484 return true;
1485 }