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