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