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