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