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