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