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