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