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