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