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