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