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