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