]> git.saurik.com Git - wxWidgets.git/blame - utils/ifacecheck/src/xmlparser.cpp
handle error in GetDataSize()
[wxWidgets.git] / utils / ifacecheck / src / xmlparser.cpp
CommitLineData
fce3374f 1 /////////////////////////////////////////////////////////////////////////////
5934cda1
FM
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
11866e8e 20 #include "wx/crt.h"
5934cda1
FM
21#endif
22
23#include "wx/xml/xml.h"
24#include "wx/wfstream.h"
3cc7596e 25#include "wx/hashmap.h"
5934cda1
FM
26#include "wx/filename.h"
27#include "xmlparser.h"
0403e5dc 28#include <errno.h>
5934cda1 29
0403e5dc 30#include <wx/arrimpl.cpp>
5934cda1 31WX_DEFINE_OBJARRAY(wxTypeArray)
f270e1dd 32WX_DEFINE_OBJARRAY(wxArgumentTypeArray)
5934cda1
FM
33WX_DEFINE_OBJARRAY(wxMethodArray)
34WX_DEFINE_OBJARRAY(wxClassArray)
35
36
0403e5dc
FM
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
5934cda1
FM
42extern bool g_verbose;
43
97f0dbd6
FM
44// global variable:
45bool g_bLogEnabled = true;
46
5934cda1
FM
47
48
49// ----------------------------------------------------------------------------
50// wxType
51// ----------------------------------------------------------------------------
52
53wxType wxEmptyType;
54
f270e1dd 55void wxType::SetTypeFromString(const wxString& t)
5934cda1 56{
a7be99c8
FM
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
f3998820
FM
62 // clean the type string
63 // ---------------------
64
a7be99c8 65 m_strType = t;
5934cda1
FM
66
67 // [] is the same as * for gccxml
68 m_strType.Replace("[]", "*");
a7be99c8 69 m_strType.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
f3998820 70 m_strType.Replace("long unsigned int", "unsigned long");
a7be99c8
FM
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);
658d9804 85
f3998820
FM
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
658d9804
FM
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);
585a11d6
FM
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);
97f0dbd6
FM
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
b74134f6 109 m_strTypeClean.Replace("wxHelpEvent::", "");
5934cda1
FM
110}
111
112bool 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
658d9804 118 return !m_strTypeClean.IsEmpty();
5934cda1
FM
119}
120
121bool wxType::operator==(const wxType& m) const
122{
123 // brain-dead comparison:
124
658d9804 125 if (m_strTypeClean == m.m_strTypeClean &&
5934cda1
FM
126 IsConst() == m.IsConst() &&
127 IsStatic() == m.IsStatic() &&
128 IsPointer() == m.IsPointer() &&
129 IsReference() == m.IsReference())
130 return true;
131
97f0dbd6 132 if (g_verbose)
fce3374f 133 {
97f0dbd6 134 LogMessage("Type '%s' does not match type '%s'", m_strType, m.m_strType);
fce3374f
FM
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 }
97f0dbd6 140
5934cda1
FM
141 return false;
142}
143
f270e1dd
FM
144
145// ----------------------------------------------------------------------------
146// wxArgumentType
147// ----------------------------------------------------------------------------
148
5570107a 149void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& defvalForCmp)
f270e1dd 150{
2cb3a02f 151 m_strDefaultValue = defval.Strip(wxString::both);
f3998820
FM
152 m_strDefaultValueForCmp = defvalForCmp.IsEmpty() ?
153 m_strDefaultValue : defvalForCmp.Strip(wxString::both);
154
155
156 // clean the default argument strings
157 // ----------------------------------
2cb3a02f 158
f3998820
FM
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
24f5cd42 161
f3998820
FM
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 }
8cd22478 178
673ae68a 179
f3998820
FM
180 // clean ONLY the default argument string specific for comparison
181 // --------------------------------------------------------------
8cd22478
FM
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
97f0dbd6
FM
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
b74134f6 196 m_strDefaultValueForCmp.Replace("wxHelpEvent::", ""); // same story for some other classes
b74134f6 197 m_strDefaultValueForCmp.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
f270e1dd 198
97f0dbd6 199 // ADHOC-FIX:
2cb3a02f
FM
200 if (m_strDefaultValueForCmp.Contains("wxGetTranslation"))
201 m_strDefaultValueForCmp = "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
f270e1dd
FM
202}
203
204bool wxArgumentType::operator==(const wxArgumentType& m) const
205{
206 if ((const wxType&)(*this) != (const wxType&)m)
207 return false;
208
97f0dbd6
FM
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" &&
2cb3a02f 213 (m_strDefaultValueForCmp.IsNumber() || m.m_strDefaultValueForCmp.IsNumber()))
97f0dbd6
FM
214 return true;
215
b74134f6
FM
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
2cb3a02f 231 if (m_strDefaultValueForCmp != m.m_strDefaultValueForCmp)
24f5cd42
FM
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;
2cb3a02f
FM
237 if (m_strDefaultValueForCmp.ToLong(&def1val, 0 /* auto-detect */) &&
238 m.m_strDefaultValueForCmp.ToLong(&def2val, 0 /* auto-detect */))
24f5cd42
FM
239 {
240 if (def1val == def2val)
241 return true; // the default values match
242 }
243
97f0dbd6
FM
244 if (g_verbose)
245 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
2cb3a02f 246 m_strType, m_strDefaultValueForCmp, m.m_strType, m.m_strDefaultValueForCmp);
f270e1dd 247 return false;
24f5cd42 248 }
f270e1dd
FM
249
250 // we deliberately avoid checks on the argument name
251
252 return true;
253}
254
255
5934cda1
FM
256// ----------------------------------------------------------------------------
257// wxMethod
258// ----------------------------------------------------------------------------
259
260bool 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!
a7be99c8 273 if ((m_bConst && m_bStatic) || ((m_bVirtual || m_bPureVirtual) && m_bStatic)) {
5934cda1
FM
274 LogError("'%s' method can't be both const/static or virtual/static", m_strName);
275 return false;
276 }
277
5570107a 278 wxASSERT(!m_bPureVirtual || (m_bPureVirtual && m_bVirtual));
d5978709 279
5934cda1
FM
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",
203ba76a 283 m_strName, i+1, m_args[i].GetAsString());
5934cda1
FM
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>)
203ba76a
FM
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 }
5934cda1
FM
305
306 return true;
307}
308
97f0dbd6 309bool wxMethod::MatchesExceptForAttributes(const wxMethod& m) const
5934cda1
FM
310{
311 if (GetReturnType() != m.GetReturnType() ||
97f0dbd6 312 GetName() != m.GetName())
fce3374f
FM
313 {
314 if (g_verbose)
315 LogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m.GetName());
5934cda1 316 return false;
fce3374f 317 }
5934cda1 318
97f0dbd6
FM
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());
5934cda1 323 return false;
97f0dbd6 324 }
5934cda1 325
97f0dbd6 326 // compare argument types
5934cda1 327 for (unsigned int i=0; i<m_args.GetCount(); i++)
f270e1dd 328 if (m_args[i] != m.m_args[i])
5934cda1
FM
329 return false;
330
331 return true;
332}
333
f3998820
FM
334bool 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
97f0dbd6
FM
346bool 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() ||
8cd22478
FM
353 IsDeprecated() != m.IsDeprecated() ||
354 GetAccessSpecifier() != m.GetAccessSpecifier())
fce3374f
FM
355 {
356 if (g_verbose)
357 LogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m.GetName());
358
97f0dbd6 359 return false;
fce3374f 360 }
97f0dbd6
FM
361
362 // check everything else
363 return MatchesExceptForAttributes(m);
364}
365
8cd22478
FM
366wxString wxMethod::GetAsString(bool bWithArgumentNames, bool bCleanDefaultValues,
367 bool bDeprecated, bool bAccessSpec) const
5934cda1
FM
368{
369 wxString ret;
370
2cb3a02f
FM
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
5934cda1 374 if (m_retType!=wxEmptyType)
2cb3a02f 375 ret += m_retType.GetAsString() + " ";
5934cda1
FM
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();
f270e1dd
FM
383
384 const wxString& name = m_args[i].GetArgumentName();
385 if (bWithArgumentNames && !name.IsEmpty())
386 ret += " " + name;
387
8cd22478 388 const wxString& def = bCleanDefaultValues ?
97f0dbd6 389 m_args[i].GetDefaultCleanValue() : m_args[i].GetDefaultValue();
f270e1dd
FM
390 if (!def.IsEmpty())
391 ret += " = " + def;
392
1f81aa36 393 ret += ", ";
5934cda1
FM
394 }
395
396 if (m_args.GetCount()>0)
1f81aa36 397 ret = ret.Left(ret.Len()-2);
5934cda1
FM
398
399 ret += ")";
400
401 if (m_bConst)
402 ret += " const";
403 if (m_bStatic)
404 ret = "static " + ret;
a7be99c8 405 if (m_bVirtual || m_bPureVirtual)
5934cda1 406 ret = "virtual " + ret;
a7be99c8 407 if (m_bPureVirtual)
97f0dbd6
FM
408 ret += " = 0";
409 if (m_bDeprecated && bDeprecated)
410 ret += " [deprecated]";
0403e5dc 411
8cd22478
FM
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
5934cda1
FM
428 return ret;
429}
430
431void 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++)
f270e1dd
FM
437 stream << "[" + m_args[i].GetAsString() + " " + m_args[i].GetArgumentName() +
438 "=" + m_args[i].GetDefaultValue() + "]";
5934cda1
FM
439
440 if (IsConst())
441 stream << " CONST";
442 if (IsStatic())
443 stream << " STATIC";
444 if (IsVirtual())
445 stream << " VIRTUAL";
a7be99c8
FM
446 if (IsPureVirtual())
447 stream << " PURE-VIRTUAL";
0403e5dc
FM
448 if (IsDeprecated())
449 stream << " DEPRECATED";
5934cda1
FM
450
451 // no final newline
452}
453
454// ----------------------------------------------------------------------------
455// wxClass
456// ----------------------------------------------------------------------------
457
458wxString 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
467bool 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
478bool wxClass::IsValidDtorForThisClass(const wxMethod& m) const
479{
480 if (m.IsDtor() && m.GetName() == "~" + GetNameWithoutTemplate())
481 return true;
482
483 return false;
484}
485
486void 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
501bool 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;
fce3374f
FM
510
511 // fix the problem?
512 //((wxClass*)this)->m_methods.RemoveAt(j);
513 //j--;
5934cda1
FM
514 }
515
516 return true;
517}
518
519const 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
673ae68a
FM
527const 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
723de43b 540 if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
673ae68a
FM
541 {
542 const wxClass *parent = allclasses->FindClass(m_parents[i]);
543 if (!parent) {
723de43b
FM
544 LogError("Could not find parent '%s' of class '%s'...",
545 m_parents[i], GetName());
673ae68a
FM
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
585a11d6 559wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const
5934cda1
FM
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
673ae68a
FM
571wxMethodPtrArray 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
723de43b 583 if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
673ae68a
FM
584 {
585 const wxClass *parent = allclasses->FindClass(m_parents[i]);
586 if (!parent) {
723de43b
FM
587 LogError("Could not find parent '%s' of class '%s'...",
588 m_parents[i], GetName());
673ae68a
FM
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
5934cda1
FM
602// ----------------------------------------------------------------------------
603// wxXmlInterface
604// ----------------------------------------------------------------------------
605
606WX_DEFINE_SORTED_ARRAY(wxClass*, wxSortedClassArray);
607
608int CompareWxClassObjects(wxClass *item1, wxClass *item2)
609{
610 // sort alphabetically
611 return item1->GetName().Cmp(item2->GetName());
612}
613
614void 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
630bool wxXmlInterface::CheckParseResults() const
631{
632 // this check can be quite slow, so do it only for debug releases:
5570107a 633//#ifdef __WXDEBUG__
5934cda1
FM
634 for (unsigned int i=0; i<m_classes.GetCount(); i++)
635 if (!m_classes[i].CheckConsistency())
636 return false;
5570107a 637//#endif
5934cda1
FM
638
639 return true;
640}
641
7fbadf87
FM
642wxClassPtrArray 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
5934cda1 654// ----------------------------------------------------------------------------
74bda203 655// wxXmlGccInterface helper declarations
5934cda1
FM
656// ----------------------------------------------------------------------------
657
585a11d6 658// or-able flags for a toResolveTypeItem->attrib:
5934cda1
FM
659#define ATTRIB_CONST 1
660#define ATTRIB_REFERENCE 2
661#define ATTRIB_POINTER 4
662#define ATTRIB_ARRAY 8
663
673ae68a
FM
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
fdd4a897
FM
668#define GCCXML_BASE 35
669
5934cda1
FM
670class toResolveTypeItem
671{
672public:
673 toResolveTypeItem() { attribs=0; }
fdd4a897
FM
674 toResolveTypeItem(unsigned int refID, unsigned int attribint)
675 : ref(refID), attribs(attribint) {}
5934cda1 676
585a11d6
FM
677 unsigned long ref, // the referenced type's ID
678 attribs; // the attributes of this reference
5934cda1
FM
679};
680
5ceba28d 681#if 1
74bda203
FM
682
683// for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
fdd4a897
FM
684WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem,
685 wxIntegerHash, wxIntegerEqual,
686 wxToResolveTypeHashMap );
74bda203
FM
687
688// for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
689WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
690 wxIntegerHash, wxIntegerEqual,
691 wxClassMemberIdHashMap );
673ae68a 692
5ceba28d 693#else
5934cda1 694#include <map>
fdd4a897 695typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap;
5ceba28d 696#endif
5934cda1 697
74bda203
FM
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
7a000734 702bool getID(unsigned long *id, const wxString& str)
74bda203 703{
fce3374f 704 const wxStringCharType * const start = str.wx_str()+1;
74bda203 705 wxStringCharType *end;
96202d07 706#if wxUSE_UNICODE_WCHAR
7a000734 707 unsigned long val = wcstoul(start, &end, GCCXML_BASE);
96202d07 708#else
7a000734 709 unsigned long val = strtoul(start, &end, GCCXML_BASE);
74bda203
FM
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:
7a000734 715 if ( *end != '\0' || end == start || errno == ERANGE || errno == EINVAL )
74bda203
FM
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
7a000734 724bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxString& str)
74bda203 725{
7a000734 726 const wxStringCharType * const start = str.wx_str();
96202d07 727#if wxUSE_UNICODE_WCHAR
7a000734 728 size_t len = wcslen(start);
96202d07 729#else
7a000734 730 size_t len = strlen(start);
74bda203
FM
731#endif
732
7a000734 733 if (len == 0 || start[0] != '_')
74bda203
FM
734 return false;
735
7a000734
VZ
736 const wxStringCharType *curpos = start,
737 *end = start + len;
74bda203
FM
738 wxStringCharType *nexttoken;
739
740 while (curpos < end)
741 {
742 // curpos always points to the underscore of the next token to parse:
9aa7bedb 743#if wxUSE_UNICODE_WCHAR
74bda203 744 unsigned long id = wcstoul(curpos+1, &nexttoken, GCCXML_BASE);
96202d07
VZ
745#else
746 unsigned long id = strtoul(curpos+1, &nexttoken, GCCXML_BASE);
74bda203
FM
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
5934cda1
FM
767bool 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
21864ff2
FM
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
5934cda1 811 wxToResolveTypeHashMap toResolveTypes;
74bda203 812 wxClassMemberIdHashMap members;
fdd4a897
FM
813 wxTypeIdHashMap types;
814 wxTypeIdHashMap files;
585a11d6 815 wxTypeIdHashMap typedefs;
5934cda1
FM
816
817 // prealloc quite a lot of memory!
818 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
5934cda1 819
5934cda1
FM
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();
74bda203 825
fdd4a897 826 unsigned long id = 0;
74bda203 827 if (!getID(&id, child->GetAttribute("id")) || (id == 0 && n != "File")) {
fdd4a897
FM
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 }
5934cda1 834
5934cda1
FM
835 if (n == "Class")
836 {
fdd4a897 837 wxString cname = child->GetAttribute("name");
5934cda1
FM
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)
74bda203
FM
844 if (cname.StartsWith("wx"))
845 {
5934cda1 846 // NB: "file" attribute contains an ID value that we'll resolve later
fdd4a897 847 m_classes.Add(wxClass(cname, child->GetAttribute("file")));
74bda203 848
673ae68a
FM
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
74bda203
FM
864 const wxString& ids = child->GetAttribute("members");
865 if (ids.IsEmpty())
866 {
867 if (child->GetAttribute("incomplete") != "1") {
7fbadf87 868 LogError("Invalid member IDs for '%s' class node: %s",
74bda203
FM
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:
673ae68a 878 if (!getMemberIDs(&members, newClass, ids)) {
7fbadf87 879 LogError("Invalid member IDs for '%s' class node: %s",
74bda203
FM
880 cname, child->GetAttribute("id"));
881 return false;
882 }
883 }
5934cda1
FM
884 }
885
886 // register this class also as possible return/argument type:
5934cda1
FM
887 types[id] = cname;
888 }
585a11d6
FM
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 }
5934cda1
FM
906 else if (n == "PointerType" || n == "ReferenceType" ||
907 n == "CvQualifiedType" || n == "ArrayType")
908 {
fdd4a897 909 unsigned long type = 0;
74bda203 910 if (!getID(&type, child->GetAttribute("type")) || type == 0) {
fdd4a897 911 LogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
5934cda1
FM
912 return false;
913 }
914
fdd4a897 915 unsigned long attr = 0;
5934cda1
FM
916 if (n == "PointerType")
917 attr = ATTRIB_POINTER;
918 else if (n == "ReferenceType")
919 attr = ATTRIB_REFERENCE;
fdd4a897 920 else if (n == "CvQualifiedType" && child->GetAttribute("const") == "1")
5934cda1
FM
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
5934cda1
FM
926 toResolveTypes[id] = toResolveTypeItem(type, attr);
927 }
928 else if (n == "FunctionType" || n == "MethodType")
929 {
0403e5dc
FM
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();
5934cda1
FM
942 }
943
0403e5dc 944 if (argstr.Len() > 0)
585a11d6 945 argstr = argstr.Left(argstr.Len()-2); // remove final comma
0403e5dc 946
5934cda1 947 // these nodes make reference to other types... we'll resolve them later
0403e5dc 948 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
585a11d6
FM
949 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
950
951 types[id] = "TOFIX"; // typically this type will be "fixed" thanks
952 // to a typedef later...
5934cda1
FM
953 }
954 else if (n == "File")
955 {
fdd4a897 956 if (!child->GetAttribute("id").StartsWith("f")) {
7fbadf87 957 LogError("Unexpected file ID: %s", child->GetAttribute("id"));
5934cda1
FM
958 return false;
959 }
960
961 // just ignore this node... all file IDs/names were already parsed
fdd4a897 962 files[id] = child->GetAttribute("name");
5934cda1
FM
963 }
964 else
965 {
966 // we register everything else as a possible return/argument type:
fdd4a897 967 const wxString& name = child->GetAttribute("name");
5934cda1
FM
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)
7fbadf87
FM
982 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
983 n, child->GetAttribute("id"));
5934cda1 984
5934cda1
FM
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
585a11d6 996 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
5934cda1
FM
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 {
fdd4a897
FM
1007 unsigned long id = i->first;
1008 unsigned long referenced = i->second.ref;
5934cda1 1009
fdd4a897 1010 wxTypeIdHashMap::iterator primary = types.find(referenced);
5934cda1
FM
1011 if (primary != types.end())
1012 {
1013 // this to-resolve-type references a "primary" type
1014
1f81aa36 1015 wxString newtype = primary->second;
5934cda1
FM
1016 int attribs = i->second.attribs;
1017
1f81aa36 1018 // attribs may contain a combination of ATTRIB_* flags:
5934cda1 1019 if (attribs & ATTRIB_CONST)
1f81aa36 1020 newtype = "const " + newtype;
5934cda1 1021 if (attribs & ATTRIB_REFERENCE)
1f81aa36 1022 newtype = newtype + "&";
5934cda1 1023 if (attribs & ATTRIB_POINTER)
1f81aa36 1024 newtype = newtype + "*";
5934cda1 1025 if (attribs & ATTRIB_ARRAY)
1f81aa36 1026 newtype = newtype + "[]";
5934cda1
FM
1027
1028 // add the resolved type to the list of "primary" types
585a11d6
FM
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;
5934cda1
FM
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 {
585a11d6 1055 LogError("Cannot solve '%d' reference type!", referenced);
5934cda1 1056 return false;
5934cda1
FM
1057 }
1058 }
1059 }
5934cda1
FM
1060 }
1061
1062 // resolve header names
1063 for (unsigned int i=0; i<m_classes.GetCount(); i++)
1064 {
fdd4a897 1065 unsigned long fileID = 0;
74bda203 1066 if (!getID(&fileID, m_classes[i].GetHeader()) || fileID == 0) {
fdd4a897
FM
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);
5934cda1
FM
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
673ae68a
FM
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
5934cda1
FM
1106 // build the list of the wx methods
1107 child = doc.GetRoot()->GetChildren();
1108 while (child)
1109 {
8cd22478 1110 wxString n = child->GetName(), acc = child->GetAttribute("access");
5934cda1 1111
8cd22478
FM
1112 // only register public&protected methods
1113 if ((acc == "public" || acc == "protected") &&
74bda203 1114 (n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod"))
5934cda1 1115 {
74bda203
FM
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 }
5934cda1 1121
74bda203
FM
1122 wxClassMemberIdHashMap::const_iterator it = members.find(id);
1123 if (it != members.end())
5934cda1 1124 {
74bda203 1125 wxClass *p = it->second;
5934cda1 1126
74bda203
FM
1127 // this <Method> node is a method of the i-th class!
1128 wxMethod newfunc;
203ba76a
FM
1129 if (!ParseMethod(child, types, newfunc)) {
1130 LogError("The method '%s' could not be added to class '%s'",
1131 child->GetAttribute("demangled"), p->GetName());
74bda203 1132 return false;
203ba76a 1133 }
74bda203 1134
8cd22478
FM
1135 // do some additional check that we can do only here:
1136
74bda203
FM
1137 if (newfunc.IsCtor() && !p->IsValidCtorForThisClass(newfunc)) {
1138 LogError("The method '%s' does not seem to be a ctor for '%s'",
7fbadf87 1139 newfunc.GetName(), p->GetName());
74bda203 1140 return false;
5934cda1 1141 }
74bda203
FM
1142 if (newfunc.IsDtor() && !p->IsValidDtorForThisClass(newfunc)) {
1143 LogError("The method '%s' does not seem to be a dtor for '%s'",
7fbadf87 1144 newfunc.GetName(), p->GetName());
74bda203
FM
1145 return false;
1146 }
1147
1148 p->AddMethod(newfunc);
5934cda1
FM
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
5934cda1
FM
1158 if (!CheckParseResults())
1159 return false;
1160
1161 return true;
1162}
1163
1164bool wxXmlGccInterface::ParseMethod(const wxXmlNode *p,
fdd4a897 1165 const wxTypeIdHashMap& types,
5934cda1
FM
1166 wxMethod& m)
1167{
1168 // get the real name
fdd4a897 1169 wxString name = p->GetAttribute("name").Strip(wxString::both);
5934cda1
FM
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;
fdd4a897 1177 unsigned long retid = 0;
74bda203 1178 if (!getID(&retid, p->GetAttribute("returns")) || retid == 0)
5934cda1
FM
1179 {
1180 if (p->GetName() != "Destructor" && p->GetName() != "Constructor") {
1181 LogError("Empty return ID for method '%s', with ID '%s'",
fdd4a897 1182 name, p->GetAttribute("id"));
5934cda1
FM
1183 return false;
1184 }
1185 }
1186 else
1187 {
fdd4a897 1188 wxTypeIdHashMap::const_iterator retidx = types.find(retid);
5934cda1
FM
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'",
fdd4a897 1197 retidx->second, name, p->GetAttribute("id"));
5934cda1
FM
1198 return false;
1199 }
1200 }
1201
1202 // resolve argument types
f270e1dd 1203 wxArgumentTypeArray argtypes;
5934cda1
FM
1204 wxXmlNode *arg = p->GetChildren();
1205 while (arg)
1206 {
1207 if (arg->GetName() == "Argument")
1208 {
fdd4a897 1209 unsigned long id = 0;
74bda203 1210 if (!getID(&id, arg->GetAttribute("type")) || id == 0) {
fdd4a897
FM
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);
5934cda1
FM
1217 if (idx == types.end()) {
1218 LogError("Could not find argument type ID '%s'", id);
1219 return false;
1220 }
1221
97f0dbd6
FM
1222 argtypes.Add(wxArgumentType(idx->second,
1223 arg->GetAttribute("default"),
1224 arg->GetAttribute("name")));
5934cda1
FM
1225 }
1226
1227 arg = arg->GetNext();
1228 }
1229
1230 m.SetReturnType(ret);
1231 m.SetName(name);
f270e1dd 1232 m.SetArgumentTypes(argtypes);
fdd4a897
FM
1233 m.SetConst(p->GetAttribute("const") == "1");
1234 m.SetStatic(p->GetAttribute("static") == "1");
95dfcbad
FM
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
fdd4a897 1241 m.SetVirtual(p->GetAttribute("virtual") == "1");
a7be99c8 1242 m.SetPureVirtual(p->GetAttribute("pure_virtual") == "1");
0403e5dc 1243 m.SetDeprecated(p->GetAttribute("attributes") == "deprecated");
5934cda1 1244
8cd22478
FM
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
5934cda1
FM
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
03d4f7b9
FM
1262
1263// ----------------------------------------------------------------------------
1264// wxXmlDoxygenInterface global helpers
1265// ----------------------------------------------------------------------------
1266
1267static 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
1300static 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
1332static 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
1354int 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
5934cda1
FM
1377// ----------------------------------------------------------------------------
1378// wxXmlDoxygenInterface
1379// ----------------------------------------------------------------------------
1380
1381bool 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
21864ff2
FM
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
5934cda1
FM
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" &&
fdd4a897 1413 compound->GetAttribute("kind") == "class")
5934cda1 1414 {
fdd4a897 1415 wxString refid = compound->GetAttribute("refid");
5934cda1
FM
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
1432bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
1433{
673ae68a 1434 wxClassMemberIdHashMap parents;
5934cda1
FM
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" &&
fdd4a897 1458 child->GetAttribute("kind") == "class")
5934cda1
FM
1459 {
1460 // parse this class
1461 wxClass klass;
1462 wxString absoluteFile, header;
1463
1464 wxXmlNode *subchild = child->GetChildren();
1465 while (subchild)
1466 {
8cd22478
FM
1467 wxString kind = subchild->GetAttribute("kind");
1468
1469 // parse only public&protected functions:
5934cda1 1470 if (subchild->GetName() == "sectiondef" &&
8cd22478 1471 (kind == "public-func" || kind == "protected-func"))
5934cda1
FM
1472 {
1473
1474 wxXmlNode *membernode = subchild->GetChildren();
1475 while (membernode)
1476 {
1477 if (membernode->GetName() == "memberdef" &&
fdd4a897 1478 membernode->GetAttribute("kind") == "function")
5934cda1
FM
1479 {
1480
1481 wxMethod m;
203ba76a
FM
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
8cd22478
FM
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
203ba76a
FM
1495 if (absoluteFile.IsEmpty())
1496 absoluteFile = header;
1497 else if (header != absoluteFile)
5934cda1 1498 {
203ba76a
FM
1499 LogError("The method '%s' is documented in a different "
1500 "file from others (which belong to '%s') ?",
1501 header, absoluteFile);
1502 return false;
5934cda1 1503 }
203ba76a
FM
1504
1505 klass.AddMethod(m);
5934cda1
FM
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 }*/
03d4f7b9
FM
1527 else if (subchild->GetName() == "detaileddescription")
1528 {
1529 // identify <onlyfor> custom XML tags
1530 klass.SetAvailability(GetAvailabilityFor(subchild));
1531 }
673ae68a
FM
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 }
5934cda1
FM
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
5934cda1
FM
1558bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode* p, wxMethod& m, wxString& header)
1559{
f270e1dd 1560 wxArgumentTypeArray args;
5934cda1
FM
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 {
f270e1dd 1572 wxString typestr, namestr, defstr, arrstr;
5934cda1
FM
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);
f270e1dd
FM
1580 else if (n->GetName() == "declname")
1581 namestr = GetTextFromChildren(n);
5934cda1 1582 else if (n->GetName() == "defval")
5570107a 1583 defstr = GetTextFromChildren(n).Strip(wxString::both);
5934cda1
FM
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
5570107a
FM
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);
5934cda1
FM
1604 }
1605 else if (child->GetName() == "location")
1606 {
1f81aa36 1607 line = -1;
fdd4a897 1608 if (child->GetAttribute("line").ToLong(&line))
5934cda1 1609 m.SetLocation((int)line);
fdd4a897 1610 header = child->GetAttribute("file");
5934cda1 1611 }
0403e5dc
FM
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"));
03d4f7b9
FM
1618
1619 // identify <onlyfor> custom XML tags
1620 m.SetAvailability(GetAvailabilityFor(child));
0403e5dc 1621 }
5934cda1
FM
1622
1623 child = child->GetNext();
1624 }
1625
f270e1dd 1626 m.SetArgumentTypes(args);
fdd4a897
FM
1627 m.SetConst(p->GetAttribute("const")=="yes");
1628 m.SetStatic(p->GetAttribute("static")=="yes");
95dfcbad
FM
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
fdd4a897 1635 m.SetVirtual(p->GetAttribute("virt")=="virtual");
a7be99c8 1636 m.SetPureVirtual(p->GetAttribute("virt")=="pure-virtual");
5934cda1
FM
1637
1638 if (!m.IsOk()) {
1639 LogError("The prototype '%s' is not valid!", m.GetAsString());
1640 return false;
1641 }
1642
1643 return true;
1644}