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