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