1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Parser of the API/interface XML files 
   4 // Author:      Francesco Montorsi 
   7 // Copyright:   (c) 2008 Francesco Montorsi 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // For compilers that support precompilation, includes "wx/wx.h". 
  12 #include "wx/wxprec.h" 
  18 // for all others, include the necessary headers 
  22 #include "wx/xml/xml.h" 
  23 #include "wx/wfstream.h" 
  24 #include "wx/hashmap.h" 
  25 #include "wx/filename.h" 
  26 #include "xmlparser.h" 
  29 #include <wx/arrimpl.cpp> 
  30 WX_DEFINE_OBJARRAY(wxTypeArray
) 
  31 WX_DEFINE_OBJARRAY(wxArgumentTypeArray
) 
  32 WX_DEFINE_OBJARRAY(wxMethodArray
) 
  33 WX_DEFINE_OBJARRAY(wxClassArray
) 
  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 
  40 // defined in ifacecheck.cpp 
  41 extern bool g_verbose
; 
  44 bool g_bLogEnabled 
= true; 
  48 // ---------------------------------------------------------------------------- 
  50 // ---------------------------------------------------------------------------- 
  54 void wxType::SetTypeFromString(const wxString
& t
) 
  57         TODO: optimize the following code writing a single function 
  58               which works at char-level and does everything in a single pass 
  63     // [] is the same as * for gccxml 
  64     m_strType
.Replace("[]", "*"); 
  65     m_strType
.Replace("long int", "long");      // in wx typically we never write "long int", just "long" 
  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(" &", "&"); 
  74     while (m_strType
.Contains("  ")) 
  75         m_strType
.Replace("  ", " ");       // do it once again 
  77     m_strType
.Replace(" ,", ","); 
  79     m_strType 
= m_strType
.Strip(wxString::both
); 
  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
); 
  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); 
  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 
 100     m_strTypeClean
.Replace("wxHelpEvent::", ""); 
 103 bool wxType::IsOk() const 
 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 
 109     return !m_strTypeClean
.IsEmpty(); 
 112 bool wxType::operator==(const wxType
& m
) const 
 114     // brain-dead comparison: 
 116     if (m_strTypeClean 
== m
.m_strTypeClean 
&& 
 117         IsConst() == m
.IsConst() && 
 118         IsStatic() == m
.IsStatic() && 
 119         IsPointer() == m
.IsPointer() && 
 120         IsReference() == m
.IsReference()) 
 124         LogMessage("Type '%s' does not match type '%s'", m_strType
, m
.m_strType
); 
 130 // ---------------------------------------------------------------------------- 
 132 // ---------------------------------------------------------------------------- 
 134 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
) 
 136     m_strDefaultValue 
= defval
.Strip(wxString::both
); 
 137     m_strDefaultValueForCmp 
= defvalForCmp
.IsEmpty() ? m_strDefaultValue 
: defvalForCmp
.Strip(wxString::both
); 
 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"; 
 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 
 147     if (m_strDefaultValueForCmp 
== "0u") 
 148         m_strDefaultValueForCmp 
= "0"; 
 150     m_strDefaultValue
.Replace("0x000000001", "1"); 
 151     m_strDefaultValueForCmp
.Replace("0x000000001", "1"); 
 153     // fix for unicode strings: 
 154     m_strDefaultValue
.Replace("\\000\\000\\000", ""); 
 155     m_strDefaultValueForCmp
.Replace("\\000\\000\\000", ""); 
 157     if (m_strDefaultValueForCmp
.StartsWith("wxT(") && 
 158         m_strDefaultValueForCmp
.EndsWith(")")) 
 160         // get rid of the wxT() part 
 161         unsigned int len 
= m_strDefaultValueForCmp
.Len(); 
 162         m_strDefaultValueForCmp 
= m_strDefaultValueForCmp
.Mid(4,len
-5); 
 167         m_strDefaultValueForCmp.Replace("0", "NULL"); 
 169         m_strDefaultValueForCmp.Replace("NULL", "0"); 
 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 
 176     m_strDefaultValueForCmp
.Replace("wxHelpEvent::", "");    // same story for some other classes 
 178     m_strDefaultValueForCmp
.Replace("wxGet_wxConvLocal()", "wxConvLocal"); 
 180     m_strDefaultValueForCmp
.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK"); 
 183     if (m_strDefaultValueForCmp
.Contains("wxGetTranslation")) 
 184         m_strDefaultValueForCmp 
= "_(TOFIX)";     // TODO: wxGetTranslation gives problems to gccxml 
 187 bool wxArgumentType::operator==(const wxArgumentType
& m
) const 
 189     if ((const wxType
&)(*this) != (const wxType
&)m
) 
 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" && 
 196         (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber())) 
 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") 
 203         if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) || 
 204             (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx"))) 
 207                 LogMessage("Supposing '%s'  default value to be the same of '%s'...", 
 208                            m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
); 
 214     if (m_strDefaultValueForCmp 
!= m
.m_strDefaultValueForCmp
) 
 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
; 
 220         if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) && 
 221             m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */)) 
 223             if (def1val 
== def2val
) 
 224                 return true;        // the default values match 
 228             LogMessage("Argument type '%s = %s' has different default value from '%s = %s'", 
 229                        m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
); 
 233     // we deliberately avoid checks on the argument name 
 239 // ---------------------------------------------------------------------------- 
 241 // ---------------------------------------------------------------------------- 
 243 bool wxMethod::IsOk() const 
 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()); 
 252     if (m_strName
.IsEmpty()) 
 255     // a function can't be both const and static or virtual and static! 
 256     if ((m_bConst 
&& m_bStatic
) || ((m_bVirtual 
|| m_bPureVirtual
) && m_bStatic
)) { 
 257         LogError("'%s' method can't be both const/static or virtual/static", m_strName
); 
 261     wxASSERT(!m_bPureVirtual 
|| (m_bPureVirtual 
&& m_bVirtual
)); 
 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", 
 266                      m_strName
, i
+1, m_args
[i
].GetAsString()); 
 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>) 
 272     //     so we don't do any test on their contents 
 273     if (m_args
.GetCount()>0) 
 275         bool previousArgHasDefault 
= m_args
[0].HasDefaultValue(); 
 276         for (unsigned int i
=1; i
<m_args
.GetCount(); i
++) 
 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!)", 
 285             previousArgHasDefault 
= m_args
[i
].HasDefaultValue(); 
 292 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const 
 294     if (GetReturnType() != m
.GetReturnType() || 
 295         GetName() != m
.GetName()) 
 298     if (m_args
.GetCount()!=m
.m_args
.GetCount()) { 
 300             LogMessage("Method '%s' has %d arguments while '%s' has %d arguments", 
 301                        m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount()); 
 305     // compare argument types 
 306     for (unsigned int i
=0; i
<m_args
.GetCount(); i
++) 
 307         if (m_args
[i
] != m
.m_args
[i
]) 
 313 bool wxMethod::operator==(const wxMethod
& m
) const 
 316     if (IsConst() != m
.IsConst() || 
 317         IsStatic() != m
.IsStatic() || 
 318         IsVirtual() != m
.IsVirtual() || 
 319         IsPureVirtual() != m
.IsPureVirtual() || 
 320         IsDeprecated() != m
.IsDeprecated() || 
 321         GetAccessSpecifier() != m
.GetAccessSpecifier()) 
 324     // check everything else 
 325     return MatchesExceptForAttributes(m
); 
 328 wxString 
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
, 
 329                                bool bDeprecated
, bool bAccessSpec
) const 
 333     // NOTE: for return and argument types, never use wxType::GetAsCleanString 
 334     //       since in that way we'd miss important decorators like &,*,const etc 
 336     if (m_retType
!=wxEmptyType
) 
 337         ret 
+= m_retType
.GetAsString() + " "; 
 338     //else; this is a ctor or dtor 
 340     ret 
+= m_strName 
+ "("; 
 342     for (unsigned int i
=0; i
<m_args
.GetCount(); i
++) 
 344         ret 
+= m_args
[i
].GetAsString(); 
 346         const wxString
& name 
= m_args
[i
].GetArgumentName(); 
 347         if (bWithArgumentNames 
&& !name
.IsEmpty()) 
 350         const wxString
& def 
= bCleanDefaultValues 
? 
 351             m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue(); 
 358     if (m_args
.GetCount()>0) 
 359         ret 
= ret
.Left(ret
.Len()-2); 
 366         ret 
= "static " + ret
; 
 367     if (m_bVirtual 
|| m_bPureVirtual
) 
 368         ret 
= "virtual " + ret
; 
 371     if (m_bDeprecated 
&& bDeprecated
) 
 372         ret 
+= " [deprecated]"; 
 381         case wxMAS_PROTECTED
: 
 382             ret 
+= " [protected]"; 
 393 void wxMethod::Dump(wxTextOutputStream
& stream
) const 
 395     stream 
<< "[" + m_retType
.GetAsString() + "]"; 
 396     stream 
<< "[" + m_strName 
+ "]"; 
 398     for (unsigned int i
=0; i
<m_args
.GetCount(); i
++) 
 399         stream 
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() + 
 400                   "=" + m_args
[i
].GetDefaultValue() + "]"; 
 407         stream 
<< " VIRTUAL"; 
 409         stream 
<< " PURE-VIRTUAL"; 
 411         stream 
<< " DEPRECATED"; 
 416 // ---------------------------------------------------------------------------- 
 418 // ---------------------------------------------------------------------------- 
 420 wxString 
wxClass::GetNameWithoutTemplate() const 
 422     // NB: I'm not sure this is the right terminology for this function! 
 424     if (m_strName
.Contains("<")) 
 425         return m_strName
.Left(m_strName
.Find("<")); 
 429 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const 
 431     // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is 
 432     // named wxWritableCharTypeBuffer, without the <...> part! 
 434     if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate()) 
 440 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const 
 442     if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate()) 
 448 void wxClass::Dump(wxTextOutputStream
& out
) const 
 450     out 
<< m_strName 
+ "\n"; 
 452     for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) { 
 454         // dump all our methods 
 456         m_methods
[i
].Dump(out
); 
 463 bool wxClass::CheckConsistency() const 
 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
]) 
 469                 LogError("class %s has two methods with the same prototype: '%s'", 
 470                          m_strName
, m_methods
[i
].GetAsString()); 
 472                 ((wxClass
*)this)->m_methods
.RemoveAt(j
); 
 479 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const 
 481     for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) 
 482         if (m_methods
[i
] == m
) 
 483             return &m_methods
[i
]; 
 487 const wxMethod
* wxClass::RecursiveUpwardFindMethod(const wxMethod
& m
, 
 488                                                    const wxXmlInterface
* allclasses
) const 
 490     // first, search into *this 
 491     const wxMethod
* ret 
= FindMethod(m
); 
 495     // then, search into its parents 
 496     for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++) 
 498         // ignore non-wx-classes parents 
 499         // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors 
 500         if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper") 
 502             const wxClass 
*parent 
= allclasses
->FindClass(m_parents
[i
]); 
 504                 LogError("Could not find parent '%s' of class '%s'...", 
 505                          m_parents
[i
], GetName()); 
 509             const wxMethod 
*parentMethod 
= parent
->RecursiveUpwardFindMethod(m
, allclasses
); 
 515     // could not find anything even in parent classes... 
 519 wxMethodPtrArray 
wxClass::FindMethodsNamed(const wxString
& name
) const 
 521     wxMethodPtrArray ret
; 
 523     for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) 
 524         if (m_methods
[i
].GetName() == name
) 
 525             ret
.Add(&m_methods
[i
]); 
 531 wxMethodPtrArray 
wxClass::RecursiveUpwardFindMethodsNamed(const wxString
& name
, 
 532                                                           const wxXmlInterface
* allclasses
) const 
 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 
 539     // then, search into parents of this class 
 540     for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++) 
 542         // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors 
 543         if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper") 
 545             const wxClass 
*parent 
= allclasses
->FindClass(m_parents
[i
]); 
 547                 LogError("Could not find parent '%s' of class '%s'...", 
 548                          m_parents
[i
], GetName()); 
 552             wxMethodPtrArray temp 
= parent
->RecursiveUpwardFindMethodsNamed(name
, allclasses
); 
 553             WX_APPEND_ARRAY(ret
, temp
); 
 562 // ---------------------------------------------------------------------------- 
 564 // ---------------------------------------------------------------------------- 
 566 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
); 
 568 int CompareWxClassObjects(wxClass 
*item1
, wxClass 
*item2
) 
 570     // sort alphabetically 
 571     return item1
->GetName().Cmp(item2
->GetName()); 
 574 void wxXmlInterface::Dump(const wxString
& filename
) 
 576     wxFFileOutputStream 
apioutput( filename 
); 
 577     wxTextOutputStream 
apiout( apioutput 
); 
 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
]); 
 585     // now they have been sorted 
 586     for (unsigned int i
=0; i
<sorted
.GetCount(); i
++) 
 587         sorted
[i
]->Dump(apiout
); 
 590 bool wxXmlInterface::CheckParseResults() const 
 592     // this check can be quite slow, so do it only for debug releases: 
 594     for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++) 
 595         if (!m_classes
[i
].CheckConsistency()) 
 602 wxClassPtrArray 
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const 
 606     for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++) 
 607         if (m_classes
[i
].GetHeader() == headerfile
) 
 608             ret
.Add(&m_classes
[i
]); 
 614 // ---------------------------------------------------------------------------- 
 615 // wxXmlGccInterface helper declarations 
 616 // ---------------------------------------------------------------------------- 
 618 // or-able flags for a toResolveTypeItem->attrib: 
 619 #define ATTRIB_CONST        1 
 620 #define ATTRIB_REFERENCE    2 
 621 #define ATTRIB_POINTER      4 
 622 #define ATTRIB_ARRAY        8 
 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 
 628 #define GCCXML_BASE         35 
 630 class toResolveTypeItem
 
 633     toResolveTypeItem() { attribs
=0; } 
 634     toResolveTypeItem(unsigned int refID
, unsigned int attribint
) 
 635         : ref(refID
), attribs(attribint
) {} 
 637     unsigned long ref
,       // the referenced type's ID 
 638                   attribs
;   // the attributes of this reference 
 643 // for wxToResolveTypeHashMap, keys == gccXML IDs  and  values == toResolveTypeItem 
 644 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
, 
 645                      wxIntegerHash
, wxIntegerEqual
, 
 646                      wxToResolveTypeHashMap 
); 
 648 // for wxClassMemberIdHashMap, keys == gccXML IDs  and  values == wxClass which owns that member ID 
 649 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*, 
 650                      wxIntegerHash
, wxIntegerEqual
, 
 651                      wxClassMemberIdHashMap 
); 
 655 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
; 
 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 
 662 bool getID(unsigned long *id
, const wxStringCharType
* str
) 
 664     wxStringCharType 
*end
; 
 665 #if wxUSE_UNICODE_WCHAR 
 666     unsigned long val 
= wcstoul(str
+1, &end
, GCCXML_BASE
); 
 668     unsigned long val 
= strtoul(str
+1, &end
, GCCXML_BASE
); 
 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 
 674     if ( *end 
!= '\0' || end 
== str
+1 || errno 
== ERANGE 
|| errno 
== EINVAL 
) 
 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 
 683 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
) 
 685 #if wxUSE_UNICODE_WCHAR 
 686     size_t len 
= wcslen(str
); 
 688     size_t len 
= strlen(str
); 
 691     if (len 
== 0 || str
[0] != '_') 
 694     const wxStringCharType 
*curpos 
= str
, 
 696     wxStringCharType 
*nexttoken
; 
 700         // curpos always points to the underscore of the next token to parse: 
 701 #if wxUSE_UNICODE_WCHAR 
 702         unsigned long id 
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
); 
 704         unsigned long id 
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
); 
 706         if ( *nexttoken 
!= ' ' || errno 
== ERANGE 
|| errno 
== EINVAL 
) 
 709         // advance current position 
 710         curpos 
= nexttoken 
+ 1; 
 712         // add this ID to the hashmap 
 713         wxClassMemberIdHashMap::value_type 
v(id
, p
); 
 721 // ---------------------------------------------------------------------------- 
 723 // ---------------------------------------------------------------------------- 
 725 bool wxXmlGccInterface::Parse(const wxString
& filename
) 
 731     LogMessage("Parsing %s...", filename
); 
 733     if (!doc
.Load(filename
)) { 
 734         LogError("can't load %s", filename
); 
 738     // start processing the XML file 
 739     if (doc
.GetRoot()->GetName() != "GCC_XML") { 
 740         LogError("invalid root node for %s", filename
); 
 744     wxString version 
= doc
.GetRoot()->GetAttribute("cvs_revision"); 
 747 #define MIN_REVISION  120 
 749     if (!version
.StartsWith("1.")) 
 753         unsigned long rev 
= 0; 
 754         if (!version
.Mid(2).ToULong(&rev
)) 
 757             if (rev 
< MIN_REVISION
) 
 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
); 
 769     wxToResolveTypeHashMap toResolveTypes
; 
 770     wxClassMemberIdHashMap members
; 
 771     wxTypeIdHashMap types
; 
 772     wxTypeIdHashMap files
; 
 773     wxTypeIdHashMap typedefs
; 
 775     // prealloc quite a lot of memory! 
 776     m_classes
.Alloc(ESTIMATED_NUM_CLASSES
); 
 778     // build a list of wx classes and in general of all existent types 
 779     child 
= doc
.GetRoot()->GetChildren(); 
 782         const wxString
& n 
= child
->GetName(); 
 784         unsigned long id 
= 0; 
 785         if (!getID(&id
, child
->GetAttribute("id")) || (id 
== 0 && n 
!= "File")) { 
 787             // NOTE: <File> nodes can have an id == "f0"... 
 789             LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id")); 
 795             wxString cname 
= child
->GetAttribute("name"); 
 796             if (cname
.IsEmpty()) { 
 797                 LogError("Invalid empty name for '%s' node", n
); 
 801             // only register wx classes (do remember also the IDs of their members) 
 802             if (cname
.StartsWith("wx")) 
 804                 // NB: "file" attribute contains an ID value that we'll resolve later 
 805                 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file"))); 
 807                 // the just-inserted class: 
 808                 wxClass 
*newClass 
= &m_classes
.Last(); 
 810                 // now get a list of the base classes: 
 811                 wxXmlNode 
*baseNode 
= child
->GetChildren(); 
 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")); 
 819                     baseNode 
= baseNode
->GetNext(); 
 822                 const wxString
& ids 
= child
->GetAttribute("members"); 
 825                     if (child
->GetAttribute("incomplete") != "1") { 
 826                         LogError("Invalid member IDs for '%s' class node: %s", 
 827                                 cname
, child
->GetAttribute("id")); 
 830                     //else: don't warn the user; it looks like "incomplete" classes 
 831                     //      never have any member... 
 835                     // decode the non-empty list of IDs: 
 836                     if (!getMemberIDs(&members
, newClass
, ids
)) { 
 837                         LogError("Invalid member IDs for '%s' class node: %s", 
 838                                 cname
, child
->GetAttribute("id")); 
 844             // register this class also as possible return/argument type: 
 847         else if (n 
== "Typedef") 
 849             unsigned long typeId 
= 0; 
 850             if (!getID(&typeId
, child
->GetAttribute("type"))) { 
 851                 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type")); 
 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"); 
 859             // save this typedef in a separate hashmap... 
 860             typedefs
[typeId
] = name
; 
 864         else if (n 
== "PointerType" || n 
== "ReferenceType" || 
 865                  n 
== "CvQualifiedType" || n 
== "ArrayType") 
 867             unsigned long type 
= 0; 
 868             if (!getID(&type
, child
->GetAttribute("type")) || type 
== 0) { 
 869                 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type")); 
 873             unsigned long attr 
= 0; 
 874             if (n 
== "PointerType") 
 875                 attr 
= ATTRIB_POINTER
; 
 876             else if (n 
== "ReferenceType") 
 877                 attr 
= ATTRIB_REFERENCE
; 
 878             else if (n 
== "CvQualifiedType" && child
->GetAttribute("const") == "1") 
 880             else if (n 
== "ArrayType") 
 883             // these nodes make reference to other types... we'll resolve them later 
 884             toResolveTypes
[id
] = toResolveTypeItem(type
, attr
); 
 886         else if (n 
== "FunctionType" || n 
== "MethodType") 
 889                  TODO: parsing FunctionType and MethodType nodes is not as easy 
 890                        as for other "simple" types. 
 894             wxXmlNode 
*arg 
= child
->GetChildren(); 
 897                 if (arg
->GetName() == "Argument") 
 898                     argstr 
+= arg
->GetAttribute("type") + ", "; 
 899                 arg 
= arg
->GetNext(); 
 902             if (argstr
.Len() > 0) 
 903                 argstr 
= argstr
.Left(argstr
.Len()-2);       // remove final comma 
 905             // these nodes make reference to other types... we'll resolve them later 
 906             //toResolveTypes[id] = toResolveTypeItem(ret, 0); 
 907             //types[id] = child->GetAttribute("returns") + "(" + argstr + ")"; 
 909             types
[id
] = "TOFIX";   // typically this type will be "fixed" thanks 
 910                                    // to a typedef later... 
 912         else if (n 
== "File") 
 914             if (!child
->GetAttribute("id").StartsWith("f")) { 
 915                 LogError("Unexpected file ID: %s", child
->GetAttribute("id")); 
 919             // just ignore this node... all file IDs/names were already parsed 
 920             files
[id
] = child
->GetAttribute("name"); 
 924             // we register everything else as a possible return/argument type: 
 925             const wxString
& name 
= child
->GetAttribute("name"); 
 930                 //typeNames.Add(name); 
 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 
 940                     LogWarning("Type node '%s' with ID '%s' does not have name attribute", 
 941                                n
, child
->GetAttribute("id")); 
 947         child 
= child
->GetNext(); 
 949         // give feedback to the user about the progress... 
 950         if ((++nodes%PROGRESS_RATE
)==0) ShowProgress(); 
 953     // some nodes with IDs referenced by methods as return/argument types, do reference 
 954     // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above); 
 955     // thus we need to resolve their name iteratively: 
 956     while (toResolveTypes
.size()>0) 
 959             LogMessage("%d types were collected; %d types need yet to be resolved...", 
 960                        types
.size(), toResolveTypes
.size()); 
 962         for (wxToResolveTypeHashMap::iterator i 
= toResolveTypes
.begin(); 
 963              i 
!= toResolveTypes
.end();) 
 965             unsigned long id 
= i
->first
; 
 966             unsigned long referenced 
= i
->second
.ref
; 
 968             wxTypeIdHashMap::iterator primary 
= types
.find(referenced
); 
 969             if (primary 
!= types
.end()) 
 971                 // this to-resolve-type references a "primary" type 
 973                 wxString newtype 
= primary
->second
; 
 974                 int attribs 
= i
->second
.attribs
; 
 976                 // attribs may contain a combination of ATTRIB_* flags: 
 977                 if (attribs 
& ATTRIB_CONST
) 
 978                     newtype 
= "const " + newtype
; 
 979                 if (attribs 
& ATTRIB_REFERENCE
) 
 980                     newtype 
= newtype 
+ "&"; 
 981                 if (attribs 
& ATTRIB_POINTER
) 
 982                     newtype 
= newtype 
+ "*"; 
 983                 if (attribs 
& ATTRIB_ARRAY
) 
 984                     newtype 
= newtype 
+ "[]"; 
 986                 // add the resolved type to the list of "primary" types 
 987                 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "") 
 988                     types
[id
] = typedefs
[id
];       // better use a typedef for this type! 
 992                 // this one has been resolved; erase it through its iterator! 
 993                 toResolveTypes
.erase(i
); 
 995                 // now iterator i is invalid; assign it again to the beginning 
 996                 i 
= toResolveTypes
.begin(); 
1000                 // then search in the referenced types themselves: 
1001                 wxToResolveTypeHashMap::iterator idx2 
= toResolveTypes
.find(referenced
); 
1002                 if (idx2 
!= toResolveTypes
.end()) 
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
; 
1008                     // this type will eventually be solved in the next while() iteration 
1013                     LogError("Cannot solve '%d' reference type!", referenced
); 
1020     // resolve header names 
1021     for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++) 
1023         unsigned long fileID 
= 0; 
1024         if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID 
== 0) { 
1025             LogError("invalid header id: %s", m_classes
[i
].GetHeader()); 
1030         wxTypeIdHashMap::const_iterator idx 
= files
.find(fileID
); 
1031         if (idx 
== files
.end()) 
1033             // this is an error! 
1034             LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader()); 
1037             m_classes
[i
].SetHeader(idx
->second
); 
1040     // resolve parent names 
1041     for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++) 
1043         for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++) 
1047             if (!getID(&id
, m_classes
[i
].GetParent(k
))) { 
1048                 LogError("invalid parent class ID for '%s'", m_classes
[i
].GetName()); 
1052             wxTypeIdHashMap::const_iterator idx 
= types
.find(id
); 
1053             if (idx 
== types
.end()) 
1055                 // this is an error! 
1056                 LogError("couldn't find parent class ID '%d'", id
); 
1059                 // replace k-th parent with its true name: 
1060                 m_classes
[i
].SetParent(k
, idx
->second
); 
1064     // build the list of the wx methods 
1065     child 
= doc
.GetRoot()->GetChildren(); 
1068         wxString n 
= child
->GetName(), acc 
= child
->GetAttribute("access"); 
1070         // only register public&protected methods 
1071         if ((acc 
== "public" || acc 
== "protected") && 
1072             (n 
== "Method" || n 
== "Constructor" || n 
== "Destructor" || n 
== "OperatorMethod")) 
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")); 
1080             wxClassMemberIdHashMap::const_iterator it 
= members
.find(id
); 
1081             if (it 
!= members
.end()) 
1083                 wxClass 
*p 
= it
->second
; 
1085                 // this <Method> node is a method of the i-th class! 
1087                 if (!ParseMethod(child
, types
, newfunc
)) { 
1088                     LogError("The method '%s' could not be added to class '%s'", 
1089                              child
->GetAttribute("demangled"), p
->GetName()); 
1093                 // do some additional check that we can do only here: 
1095                 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) { 
1096                     LogError("The method '%s' does not seem to be a ctor for '%s'", 
1097                              newfunc
.GetName(), p
->GetName()); 
1100                 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) { 
1101                     LogError("The method '%s' does not seem to be a dtor for '%s'", 
1102                              newfunc
.GetName(), p
->GetName()); 
1106                 p
->AddMethod(newfunc
); 
1110         child 
= child
->GetNext(); 
1112         // give feedback to the user about the progress... 
1113         if ((++nodes%PROGRESS_RATE
)==0) ShowProgress(); 
1116     if (!CheckParseResults()) 
1122 bool wxXmlGccInterface::ParseMethod(const wxXmlNode 
*p
, 
1123                                     const wxTypeIdHashMap
& types
, 
1126     // get the real name 
1127     wxString name 
= p
->GetAttribute("name").Strip(wxString::both
); 
1128     if (p
->GetName() == "Destructor") 
1130     else if (p
->GetName() == "OperatorMethod") 
1131         name 
= "operator" + name
; 
1133     // resolve return type 
1135     unsigned long retid 
= 0; 
1136     if (!getID(&retid
, p
->GetAttribute("returns")) || retid 
== 0) 
1138         if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") { 
1139             LogError("Empty return ID for method '%s', with ID '%s'", 
1140                      name
, p
->GetAttribute("id")); 
1146         wxTypeIdHashMap::const_iterator retidx 
= types
.find(retid
); 
1147         if (retidx 
== types
.end()) { 
1148             LogError("Could not find return type ID '%s'", retid
); 
1152         ret 
= wxType(retidx
->second
); 
1154             LogError("Invalid return type '%s' for method '%s', with ID '%s'", 
1155                      retidx
->second
, name
, p
->GetAttribute("id")); 
1160     // resolve argument types 
1161     wxArgumentTypeArray argtypes
; 
1162     wxXmlNode 
*arg 
= p
->GetChildren(); 
1165         if (arg
->GetName() == "Argument") 
1167             unsigned long id 
= 0; 
1168             if (!getID(&id
, arg
->GetAttribute("type")) || id 
== 0) { 
1169                 LogError("Invalid argument type ID '%s' for method '%s' with ID %s", 
1170                          arg
->GetAttribute("type"), name
, p
->GetAttribute("id")); 
1174             wxTypeIdHashMap::const_iterator idx 
= types
.find(id
); 
1175             if (idx 
== types
.end()) { 
1176                 LogError("Could not find argument type ID '%s'", id
); 
1180             argtypes
.Add(wxArgumentType(idx
->second
, 
1181                                         arg
->GetAttribute("default"), 
1182                                         arg
->GetAttribute("name"))); 
1185         arg 
= arg
->GetNext(); 
1188     m
.SetReturnType(ret
); 
1190     m
.SetArgumentTypes(argtypes
); 
1191     m
.SetConst(p
->GetAttribute("const") == "1"); 
1192     m
.SetStatic(p
->GetAttribute("static") == "1"); 
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 
1199     m
.SetVirtual(p
->GetAttribute("virtual") == "1"); 
1200     m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1"); 
1201     m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated"); 
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
); 
1212         LogError("The prototype '%s' is not valid!", m
.GetAsString()); 
1221 // ---------------------------------------------------------------------------- 
1222 // wxXmlDoxygenInterface global helpers 
1223 // ---------------------------------------------------------------------------- 
1225 static wxString 
GetTextFromChildren(const wxXmlNode 
*n
) 
1229     // consider the tree 
1231     //  <a><b>this</b> is a <b>string</b></a> 
1240     // unlike wxXmlNode::GetNodeContent() which would return " is a " 
1241     // this function returns "this is a string" 
1243     wxXmlNode 
*ref 
= n
->GetChildren(); 
1245         if (ref
->GetType() == wxXML_ELEMENT_NODE
) 
1246             text 
+= ref
->GetNodeContent(); 
1247         else if (ref
->GetType() == wxXML_TEXT_NODE
) 
1248             text 
+= ref
->GetContent(); 
1250             LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName()); 
1252         ref 
= ref
->GetNext(); 
1258 static bool HasTextNodeContaining(const wxXmlNode 
*parent
, const wxString
& name
) 
1263     wxXmlNode 
*p 
= parent
->GetChildren(); 
1266         switch (p
->GetType()) 
1268             case wxXML_TEXT_NODE
: 
1269                 if (p
->GetContent() == name
) 
1273             case wxXML_ELEMENT_NODE
: 
1274                 // recurse into this node... 
1275                 if (HasTextNodeContaining(p
, name
)) 
1290 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
) 
1295     const wxXmlNode 
*p 
= parent
->GetChildren(); 
1298         if (p
->GetName() == name
) 
1301         // search recursively in the children of this node 
1302         const wxXmlNode 
*ret 
= FindNodeNamed(p
, name
); 
1312 int GetAvailabilityFor(const wxXmlNode 
*node
) 
1314     // identify <onlyfor> custom XML tags 
1315     const wxXmlNode
* onlyfor 
= FindNodeNamed(node
, "onlyfor"); 
1317         return wxPORT_UNKNOWN
; 
1319     wxArrayString ports 
= wxSplit(onlyfor
->GetNodeContent(), ','); 
1320     int nAvail 
= wxPORT_UNKNOWN
; 
1321     for (unsigned int i
=0; i 
< ports
.GetCount(); i
++) 
1323         if (!ports
[i
].StartsWith("wx")) { 
1324             LogError("unexpected port ID '%s'", ports
[i
]); 
1328         nAvail 
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2)); 
1335 // ---------------------------------------------------------------------------- 
1336 // wxXmlDoxygenInterface 
1337 // ---------------------------------------------------------------------------- 
1339 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
) 
1341     wxXmlDocument index
; 
1342     wxXmlNode 
*compound
; 
1344     LogMessage("Parsing %s...", filename
); 
1346     if (!index
.Load(filename
)) { 
1347         LogError("can't load %s", filename
); 
1351     // start processing the index: 
1352     if (index
.GetRoot()->GetName() != "doxygenindex") { 
1353         LogError("invalid root node for %s", filename
); 
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. 
1364     m_classes
.Alloc(ESTIMATED_NUM_CLASSES
); 
1366     // process files referenced by this index file 
1367     compound 
= index
.GetRoot()->GetChildren(); 
1370         if (compound
->GetName() == "compound" && 
1371             compound
->GetAttribute("kind") == "class") 
1373             wxString refid 
= compound
->GetAttribute("refid"); 
1375             wxFileName 
fn(filename
); 
1376             if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid 
+ ".xml")) 
1380         compound 
= compound
->GetNext(); 
1384     if (!CheckParseResults()) 
1390 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
) 
1392     wxClassMemberIdHashMap parents
; 
1398         LogMessage("Parsing %s...", filename
); 
1400     if (!doc
.Load(filename
)) { 
1401         LogError("can't load %s", filename
); 
1405     // start processing this compound definition XML 
1406     if (doc
.GetRoot()->GetName() != "doxygen") { 
1407         LogError("invalid root node for %s", filename
); 
1411     // build a list of wx classes 
1412     child 
= doc
.GetRoot()->GetChildren(); 
1415         if (child
->GetName() == "compounddef" && 
1416             child
->GetAttribute("kind") == "class") 
1420             wxString absoluteFile
, header
; 
1422             wxXmlNode 
*subchild 
= child
->GetChildren(); 
1425                 wxString kind 
= subchild
->GetAttribute("kind"); 
1427                 // parse only public&protected functions: 
1428                 if (subchild
->GetName() == "sectiondef" && 
1429                     (kind 
== "public-func" || kind 
== "protected-func")) 
1432                     wxXmlNode 
*membernode 
= subchild
->GetChildren(); 
1435                         if (membernode
->GetName() == "memberdef" && 
1436                             membernode
->GetAttribute("kind") == "function") 
1440                             if (!ParseMethod(membernode
, m
, header
)) { 
1441                                 LogError("The method '%s' could not be added to class '%s'", 
1442                                          m
.GetName(), klass
.GetName()); 
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
); 
1453                             if (absoluteFile
.IsEmpty()) 
1454                                 absoluteFile 
= header
; 
1455                             else if (header 
!= absoluteFile
) 
1457                                 LogError("The method '%s' is documented in a different " 
1458                                             "file from others (which belong to '%s') ?", 
1459                                             header
, absoluteFile
); 
1466                         membernode 
= membernode
->GetNext(); 
1469                     // all methods of this class were taken from the header "absoluteFile": 
1470                     klass
.SetHeader(absoluteFile
); 
1472                 else if (subchild
->GetName() == "compoundname") 
1474                     klass
.SetName(subchild
->GetNodeContent()); 
1476                 /*else if (subchild->GetName() == "includes") 
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 
1483                     klass.SetHeader(subchild->GetNodeContent()); 
1485                 else if (subchild
->GetName() == "detaileddescription") 
1487                     // identify <onlyfor> custom XML tags 
1488                     klass
.SetAvailability(GetAvailabilityFor(subchild
)); 
1490                 else if (subchild
->GetName() == "basecompoundref") 
1492                     // add the name of this parent to the list of klass' parents 
1493                     klass
.AddParent(subchild
->GetNodeContent()); 
1496                 subchild 
= subchild
->GetNext(); 
1501                 m_classes
.Add(klass
); 
1503                 LogWarning("discarding class '%s' with %d methods...", 
1504                         klass
.GetName(), klass
.GetMethodCount()); 
1507         child 
= child
->GetNext(); 
1509         // give feedback to the user about the progress... 
1510         if ((++nodes%PROGRESS_RATE
)==0) ShowProgress(); 
1516 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
) 
1518     wxArgumentTypeArray args
; 
1521     wxXmlNode 
*child 
= p
->GetChildren(); 
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") 
1530             wxString typestr
, namestr
, defstr
, arrstr
; 
1531             wxXmlNode 
*n 
= child
->GetChildren(); 
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
); 
1538                 else if (n
->GetName() == "declname") 
1539                     namestr 
= GetTextFromChildren(n
); 
1540                 else if (n
->GetName() == "defval") 
1541                     defstr 
= GetTextFromChildren(n
).Strip(wxString::both
); 
1542                 else if (n
->GetName() == "array") 
1543                     arrstr 
= GetTextFromChildren(n
); 
1548             if (typestr
.IsEmpty()) { 
1549                 LogError("cannot find type node for a param in method '%s'", m
.GetName()); 
1553             wxArgumentType 
newarg(typestr 
+ arrstr
, defstr
, namestr
); 
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
); 
1563         else if (child
->GetName() == "location") 
1566             if (child
->GetAttribute("line").ToLong(&line
)) 
1567                 m
.SetLocation((int)line
); 
1568             header 
= child
->GetAttribute("file"); 
1570         else if (child
->GetName() == "detaileddescription") 
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")); 
1577             // identify <onlyfor> custom XML tags 
1578             m
.SetAvailability(GetAvailabilityFor(child
)); 
1581         child 
= child
->GetNext(); 
1584     m
.SetArgumentTypes(args
); 
1585     m
.SetConst(p
->GetAttribute("const")=="yes"); 
1586     m
.SetStatic(p
->GetAttribute("static")=="yes"); 
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 
1593     m
.SetVirtual(p
->GetAttribute("virt")=="virtual"); 
1594     m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual"); 
1597         LogError("The prototype '%s' is not valid!", m
.GetAsString());