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