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