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