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