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