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