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