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