]> git.saurik.com Git - wxWidgets.git/blame - utils/ifacecheck/src/xmlparser.cpp
Added mingw-w64 builds (cross compile on i686-linux).
[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);
658d9804
FM
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);
5934cda1
FM
86}
87
88bool 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
658d9804 94 return !m_strTypeClean.IsEmpty();
5934cda1
FM
95}
96
97bool wxType::operator==(const wxType& m) const
98{
99 // brain-dead comparison:
100
658d9804 101 if (m_strTypeClean == m.m_strTypeClean &&
5934cda1
FM
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
f270e1dd
FM
111
112// ----------------------------------------------------------------------------
113// wxArgumentType
114// ----------------------------------------------------------------------------
115
5570107a 116void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& defvalForCmp)
f270e1dd 117{
203ba76a 118 m_strDefaultValue=defval.Strip(wxString::both);
5570107a 119 m_strDefaultValueForCmp=defvalForCmp.Strip(wxString::both);
24f5cd42 120
f270e1dd
FM
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:
24f5cd42
FM
124 if (m_strDefaultValue == "0u")
125 m_strDefaultValue = "0";
126/*
f270e1dd
FM
127 if (IsPointer())
128 m_strDefaultValue.Replace("0", "NULL");
129 else
130 m_strDefaultValue.Replace("NULL", "0");
5570107a 131*/
f270e1dd
FM
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
5570107a
FM
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)
24f5cd42
FM
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
f270e1dd 158 return false;
24f5cd42 159 }
f270e1dd
FM
160
161 // we deliberately avoid checks on the argument name
162
163 return true;
164}
165
166
5934cda1
FM
167// ----------------------------------------------------------------------------
168// wxMethod
169// ----------------------------------------------------------------------------
170
171bool 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!
a7be99c8 184 if ((m_bConst && m_bStatic) || ((m_bVirtual || m_bPureVirtual) && m_bStatic)) {
5934cda1
FM
185 LogError("'%s' method can't be both const/static or virtual/static", m_strName);
186 return false;
187 }
188
5570107a 189 wxASSERT(!m_bPureVirtual || (m_bPureVirtual && m_bVirtual));
d5978709 190
5934cda1
FM
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",
203ba76a 194 m_strName, i+1, m_args[i].GetAsString());
5934cda1
FM
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>)
203ba76a
FM
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 }
5934cda1
FM
216
217 return true;
218}
219
5934cda1
FM
220bool wxMethod::operator==(const wxMethod& m) const
221{
222 if (GetReturnType() != m.GetReturnType() ||
223 GetName() != m.GetName() ||
224 IsConst() != m.IsConst() ||
225 IsStatic() != m.IsStatic() ||
0403e5dc 226 IsVirtual() != m.IsVirtual() ||
a7be99c8 227 IsPureVirtual() != m.IsPureVirtual() ||
0403e5dc 228 IsDeprecated() != m.IsDeprecated())
5934cda1
FM
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++)
f270e1dd 235 if (m_args[i] != m.m_args[i])
5934cda1
FM
236 return false;
237
238 return true;
239}
240
f270e1dd 241wxString wxMethod::GetAsString(bool bWithArgumentNames) const
5934cda1
FM
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();
f270e1dd
FM
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
1f81aa36 263 ret += ", ";
5934cda1
FM
264 }
265
266 if (m_args.GetCount()>0)
1f81aa36 267 ret = ret.Left(ret.Len()-2);
5934cda1
FM
268
269 ret += ")";
270
271 if (m_bConst)
272 ret += " const";
273 if (m_bStatic)
274 ret = "static " + ret;
a7be99c8 275 if (m_bVirtual || m_bPureVirtual)
5934cda1 276 ret = "virtual " + ret;
a7be99c8
FM
277 if (m_bPureVirtual)
278 ret = ret + " = 0";
5934cda1 279
1f81aa36
FM
280 // in doxygen headers we don't need wxDEPRECATED:
281 //if (m_bDeprecated)
282 // ret = "wxDEPRECATED( " + ret + " )";
0403e5dc 283
5934cda1
FM
284 return ret;
285}
286
287void 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++)
f270e1dd
FM
293 stream << "[" + m_args[i].GetAsString() + " " + m_args[i].GetArgumentName() +
294 "=" + m_args[i].GetDefaultValue() + "]";
5934cda1
FM
295
296 if (IsConst())
297 stream << " CONST";
298 if (IsStatic())
299 stream << " STATIC";
300 if (IsVirtual())
301 stream << " VIRTUAL";
a7be99c8
FM
302 if (IsPureVirtual())
303 stream << " PURE-VIRTUAL";
0403e5dc
FM
304 if (IsDeprecated())
305 stream << " DEPRECATED";
5934cda1
FM
306
307 // no final newline
308}
309
310// ----------------------------------------------------------------------------
311// wxClass
312// ----------------------------------------------------------------------------
313
314wxString 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
323bool 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
334bool wxClass::IsValidDtorForThisClass(const wxMethod& m) const
335{
336 if (m.IsDtor() && m.GetName() == "~" + GetNameWithoutTemplate())
337 return true;
338
339 return false;
340}
341
342void 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
357bool 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
371const 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
379wxMethodPtrArray 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
395WX_DEFINE_SORTED_ARRAY(wxClass*, wxSortedClassArray);
396
397int CompareWxClassObjects(wxClass *item1, wxClass *item2)
398{
399 // sort alphabetically
400 return item1->GetName().Cmp(item2->GetName());
401}
402
403void 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
419bool wxXmlInterface::CheckParseResults() const
420{
421 // this check can be quite slow, so do it only for debug releases:
5570107a 422//#ifdef __WXDEBUG__
5934cda1
FM
423 for (unsigned int i=0; i<m_classes.GetCount(); i++)
424 if (!m_classes[i].CheckConsistency())
425 return false;
5570107a 426//#endif
5934cda1
FM
427
428 return true;
429}
430
7fbadf87
FM
431wxClassPtrArray 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
5934cda1 443// ----------------------------------------------------------------------------
74bda203 444// wxXmlGccInterface helper declarations
5934cda1
FM
445// ----------------------------------------------------------------------------
446
447#define ATTRIB_CONST 1
448#define ATTRIB_REFERENCE 2
449#define ATTRIB_POINTER 4
450#define ATTRIB_ARRAY 8
451
fdd4a897
FM
452#define GCCXML_BASE 35
453
5934cda1
FM
454class toResolveTypeItem
455{
456public:
457 toResolveTypeItem() { attribs=0; }
fdd4a897
FM
458 toResolveTypeItem(unsigned int refID, unsigned int attribint)
459 : ref(refID), attribs(attribint) {}
5934cda1 460
fdd4a897 461 unsigned long ref, attribs;
5934cda1
FM
462};
463
5ceba28d 464#if 1
74bda203
FM
465
466// for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
fdd4a897
FM
467WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem,
468 wxIntegerHash, wxIntegerEqual,
469 wxToResolveTypeHashMap );
74bda203
FM
470
471// for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
472WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
473 wxIntegerHash, wxIntegerEqual,
474 wxClassMemberIdHashMap );
5ceba28d 475#else
5934cda1 476#include <map>
fdd4a897 477typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap;
5ceba28d 478#endif
5934cda1 479
74bda203
FM
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
484bool getID(unsigned long *id, const wxStringCharType* str)
485{
486 wxStringCharType *end;
96202d07 487#if wxUSE_UNICODE_WCHAR
74bda203 488 unsigned long val = wcstoul(str+1, &end, GCCXML_BASE);
96202d07
VZ
489#else
490 unsigned long val = strtoul(str+1, &end, GCCXML_BASE);
74bda203
FM
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
505bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxStringCharType* str)
506{
96202d07 507#if wxUSE_UNICODE_WCHAR
74bda203 508 size_t len = wcslen(str);
96202d07
VZ
509#else
510 size_t len = strlen(str);
74bda203
FM
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_UTF8
74bda203 524 unsigned long id = wcstoul(curpos+1, &nexttoken, GCCXML_BASE);
96202d07
VZ
525#else
526 unsigned long id = strtoul(curpos+1, &nexttoken, GCCXML_BASE);
74bda203
FM
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
5934cda1
FM
547bool 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
21864ff2
FM
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
5934cda1 591 wxToResolveTypeHashMap toResolveTypes;
74bda203 592 wxClassMemberIdHashMap members;
fdd4a897
FM
593 wxTypeIdHashMap types;
594 wxTypeIdHashMap files;
5934cda1
FM
595
596 // prealloc quite a lot of memory!
597 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
5934cda1 598
5934cda1
FM
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();
74bda203 604
fdd4a897 605 unsigned long id = 0;
74bda203 606 if (!getID(&id, child->GetAttribute("id")) || (id == 0 && n != "File")) {
fdd4a897
FM
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 }
5934cda1 613
5934cda1
FM
614 if (n == "Class")
615 {
fdd4a897 616 wxString cname = child->GetAttribute("name");
5934cda1
FM
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)
74bda203
FM
623 if (cname.StartsWith("wx"))
624 {
5934cda1 625 // NB: "file" attribute contains an ID value that we'll resolve later
fdd4a897 626 m_classes.Add(wxClass(cname, child->GetAttribute("file")));
74bda203
FM
627
628 const wxString& ids = child->GetAttribute("members");
629 if (ids.IsEmpty())
630 {
631 if (child->GetAttribute("incomplete") != "1") {
7fbadf87 632 LogError("Invalid member IDs for '%s' class node: %s",
74bda203
FM
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)) {
7fbadf87 643 LogError("Invalid member IDs for '%s' class node: %s",
74bda203
FM
644 cname, child->GetAttribute("id"));
645 return false;
646 }
647 }
5934cda1
FM
648 }
649
650 // register this class also as possible return/argument type:
5934cda1
FM
651 types[id] = cname;
652 }
653 else if (n == "PointerType" || n == "ReferenceType" ||
654 n == "CvQualifiedType" || n == "ArrayType")
655 {
fdd4a897 656 unsigned long type = 0;
74bda203 657 if (!getID(&type, child->GetAttribute("type")) || type == 0) {
fdd4a897 658 LogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
5934cda1
FM
659 return false;
660 }
661
fdd4a897 662 unsigned long attr = 0;
5934cda1
FM
663 if (n == "PointerType")
664 attr = ATTRIB_POINTER;
665 else if (n == "ReferenceType")
666 attr = ATTRIB_REFERENCE;
fdd4a897 667 else if (n == "CvQualifiedType" && child->GetAttribute("const") == "1")
5934cda1
FM
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
5934cda1
FM
673 toResolveTypes[id] = toResolveTypeItem(type, attr);
674 }
675 else if (n == "FunctionType" || n == "MethodType")
676 {
0403e5dc
FM
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();
5934cda1
FM
689 }
690
0403e5dc
FM
691 if (argstr.Len() > 0)
692 argstr = argstr.Left(argstr.Len()-2);
693
5934cda1 694 // these nodes make reference to other types... we'll resolve them later
0403e5dc
FM
695 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
696 types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
5934cda1
FM
697 }
698 else if (n == "File")
699 {
fdd4a897 700 if (!child->GetAttribute("id").StartsWith("f")) {
7fbadf87 701 LogError("Unexpected file ID: %s", child->GetAttribute("id"));
5934cda1
FM
702 return false;
703 }
704
705 // just ignore this node... all file IDs/names were already parsed
fdd4a897 706 files[id] = child->GetAttribute("name");
5934cda1
FM
707 }
708 else
709 {
710 // we register everything else as a possible return/argument type:
fdd4a897 711 const wxString& name = child->GetAttribute("name");
5934cda1
FM
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)
7fbadf87
FM
726 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
727 n, child->GetAttribute("id"));
5934cda1 728
5934cda1
FM
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 {
fdd4a897
FM
751 unsigned long id = i->first;
752 unsigned long referenced = i->second.ref;
5934cda1 753
fdd4a897 754 wxTypeIdHashMap::iterator primary = types.find(referenced);
5934cda1
FM
755 if (primary != types.end())
756 {
757 // this to-resolve-type references a "primary" type
758
1f81aa36 759 wxString newtype = primary->second;
5934cda1
FM
760 int attribs = i->second.attribs;
761
1f81aa36 762 // attribs may contain a combination of ATTRIB_* flags:
5934cda1 763 if (attribs & ATTRIB_CONST)
1f81aa36 764 newtype = "const " + newtype;
5934cda1 765 if (attribs & ATTRIB_REFERENCE)
1f81aa36 766 newtype = newtype + "&";
5934cda1 767 if (attribs & ATTRIB_POINTER)
1f81aa36 768 newtype = newtype + "*";
5934cda1 769 if (attribs & ATTRIB_ARRAY)
1f81aa36 770 newtype = newtype + "[]";
5934cda1
FM
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 {
5934cda1
FM
796 LogError("Cannot solve '%s' reference type!", referenced);
797 return false;
5934cda1
FM
798 }
799 }
800 }
5934cda1
FM
801 }
802
803 // resolve header names
804 for (unsigned int i=0; i<m_classes.GetCount(); i++)
805 {
fdd4a897 806 unsigned long fileID = 0;
74bda203 807 if (!getID(&fileID, m_classes[i].GetHeader()) || fileID == 0) {
fdd4a897
FM
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);
5934cda1
FM
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
74bda203
FM
829 // only register public methods
830 if (child->GetAttribute("access") == "public" &&
831 (n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod"))
5934cda1 832 {
74bda203
FM
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 }
5934cda1 838
74bda203
FM
839 wxClassMemberIdHashMap::const_iterator it = members.find(id);
840 if (it != members.end())
5934cda1 841 {
74bda203 842 wxClass *p = it->second;
5934cda1 843
74bda203
FM
844 // this <Method> node is a method of the i-th class!
845 wxMethod newfunc;
203ba76a
FM
846 if (!ParseMethod(child, types, newfunc)) {
847 LogError("The method '%s' could not be added to class '%s'",
848 child->GetAttribute("demangled"), p->GetName());
74bda203 849 return false;
203ba76a 850 }
74bda203
FM
851
852 if (newfunc.IsCtor() && !p->IsValidCtorForThisClass(newfunc)) {
853 LogError("The method '%s' does not seem to be a ctor for '%s'",
7fbadf87 854 newfunc.GetName(), p->GetName());
74bda203 855 return false;
5934cda1 856 }
74bda203
FM
857 if (newfunc.IsDtor() && !p->IsValidDtorForThisClass(newfunc)) {
858 LogError("The method '%s' does not seem to be a dtor for '%s'",
7fbadf87 859 newfunc.GetName(), p->GetName());
74bda203
FM
860 return false;
861 }
862
863 p->AddMethod(newfunc);
5934cda1
FM
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
5934cda1
FM
873 if (!CheckParseResults())
874 return false;
875
876 return true;
877}
878
879bool wxXmlGccInterface::ParseMethod(const wxXmlNode *p,
fdd4a897 880 const wxTypeIdHashMap& types,
5934cda1
FM
881 wxMethod& m)
882{
883 // get the real name
fdd4a897 884 wxString name = p->GetAttribute("name").Strip(wxString::both);
5934cda1
FM
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;
fdd4a897 892 unsigned long retid = 0;
74bda203 893 if (!getID(&retid, p->GetAttribute("returns")) || retid == 0)
5934cda1
FM
894 {
895 if (p->GetName() != "Destructor" && p->GetName() != "Constructor") {
896 LogError("Empty return ID for method '%s', with ID '%s'",
fdd4a897 897 name, p->GetAttribute("id"));
5934cda1
FM
898 return false;
899 }
900 }
901 else
902 {
fdd4a897 903 wxTypeIdHashMap::const_iterator retidx = types.find(retid);
5934cda1
FM
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'",
fdd4a897 912 retidx->second, name, p->GetAttribute("id"));
5934cda1
FM
913 return false;
914 }
915 }
916
917 // resolve argument types
f270e1dd 918 wxArgumentTypeArray argtypes;
5934cda1
FM
919 wxXmlNode *arg = p->GetChildren();
920 while (arg)
921 {
922 if (arg->GetName() == "Argument")
923 {
fdd4a897 924 unsigned long id = 0;
74bda203 925 if (!getID(&id, arg->GetAttribute("type")) || id == 0) {
fdd4a897
FM
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);
5934cda1
FM
932 if (idx == types.end()) {
933 LogError("Could not find argument type ID '%s'", id);
934 return false;
935 }
936
f270e1dd 937 argtypes.Add(wxArgumentType(idx->second, arg->GetAttribute("default")));
5934cda1
FM
938 }
939
940 arg = arg->GetNext();
941 }
942
943 m.SetReturnType(ret);
944 m.SetName(name);
f270e1dd 945 m.SetArgumentTypes(argtypes);
fdd4a897
FM
946 m.SetConst(p->GetAttribute("const") == "1");
947 m.SetStatic(p->GetAttribute("static") == "1");
95dfcbad
FM
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
fdd4a897 954 m.SetVirtual(p->GetAttribute("virtual") == "1");
a7be99c8 955 m.SetPureVirtual(p->GetAttribute("pure_virtual") == "1");
0403e5dc 956 m.SetDeprecated(p->GetAttribute("attributes") == "deprecated");
5934cda1
FM
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
03d4f7b9
FM
967
968// ----------------------------------------------------------------------------
969// wxXmlDoxygenInterface global helpers
970// ----------------------------------------------------------------------------
971
972static 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
1005static 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
1037static 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
1059int 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
5934cda1
FM
1082// ----------------------------------------------------------------------------
1083// wxXmlDoxygenInterface
1084// ----------------------------------------------------------------------------
1085
1086bool 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
21864ff2
FM
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
5934cda1
FM
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" &&
fdd4a897 1118 compound->GetAttribute("kind") == "class")
5934cda1 1119 {
fdd4a897 1120 wxString refid = compound->GetAttribute("refid");
5934cda1
FM
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
1137bool 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" &&
fdd4a897 1162 child->GetAttribute("kind") == "class")
5934cda1
FM
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" &&
fdd4a897 1172 subchild->GetAttribute("kind") == "public-func")
5934cda1
FM
1173 {
1174
1175 wxXmlNode *membernode = subchild->GetChildren();
1176 while (membernode)
1177 {
1178 if (membernode->GetName() == "memberdef" &&
fdd4a897 1179 membernode->GetAttribute("kind") == "function")
5934cda1
FM
1180 {
1181
1182 wxMethod m;
203ba76a
FM
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)
5934cda1 1192 {
203ba76a
FM
1193 LogError("The method '%s' is documented in a different "
1194 "file from others (which belong to '%s') ?",
1195 header, absoluteFile);
1196 return false;
5934cda1 1197 }
203ba76a
FM
1198
1199 klass.AddMethod(m);
5934cda1
FM
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 }*/
03d4f7b9
FM
1221 else if (subchild->GetName() == "detaileddescription")
1222 {
1223 // identify <onlyfor> custom XML tags
1224 klass.SetAvailability(GetAvailabilityFor(subchild));
1225 }
5934cda1
FM
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
5934cda1
FM
1247bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode* p, wxMethod& m, wxString& header)
1248{
f270e1dd 1249 wxArgumentTypeArray args;
5934cda1
FM
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 {
f270e1dd 1261 wxString typestr, namestr, defstr, arrstr;
5934cda1
FM
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);
f270e1dd
FM
1269 else if (n->GetName() == "declname")
1270 namestr = GetTextFromChildren(n);
5934cda1 1271 else if (n->GetName() == "defval")
5570107a 1272 defstr = GetTextFromChildren(n).Strip(wxString::both);
5934cda1
FM
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
5570107a
FM
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);
5934cda1
FM
1293 }
1294 else if (child->GetName() == "location")
1295 {
1f81aa36 1296 line = -1;
fdd4a897 1297 if (child->GetAttribute("line").ToLong(&line))
5934cda1 1298 m.SetLocation((int)line);
fdd4a897 1299 header = child->GetAttribute("file");
5934cda1 1300 }
0403e5dc
FM
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"));
03d4f7b9
FM
1307
1308 // identify <onlyfor> custom XML tags
1309 m.SetAvailability(GetAvailabilityFor(child));
0403e5dc 1310 }
5934cda1
FM
1311
1312 child = child->GetNext();
1313 }
1314
f270e1dd 1315 m.SetArgumentTypes(args);
fdd4a897
FM
1316 m.SetConst(p->GetAttribute("const")=="yes");
1317 m.SetStatic(p->GetAttribute("static")=="yes");
95dfcbad
FM
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
fdd4a897 1324 m.SetVirtual(p->GetAttribute("virt")=="virtual");
a7be99c8 1325 m.SetPureVirtual(p->GetAttribute("virt")=="pure-virtual");
5934cda1
FM
1326
1327 if (!m.IsOk()) {
1328 LogError("The prototype '%s' is not valid!", m.GetAsString());
1329 return false;
1330 }
1331
1332 return true;
1333}