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