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