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