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