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