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