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