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