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