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