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