]> git.saurik.com Git - wxWidgets.git/blob - utils/ifacecheck/src/xmlparser.cpp
don't build ifacecheck on WinCE since it's a console app
[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 bool wxMethod::operator==(const wxMethod& m) const
146 {
147 if (GetReturnType() != m.GetReturnType() ||
148 GetName() != m.GetName() ||
149 IsConst() != m.IsConst() ||
150 IsStatic() != m.IsStatic() ||
151 IsVirtual() != m.IsVirtual())
152 return false;
153
154 if (m_args.GetCount()!=m.m_args.GetCount())
155 return false;
156
157 for (unsigned int i=0; i<m_args.GetCount(); i++)
158 if (m_args[i] != m.m_args[i] || m_argDefaults[i] != m.m_argDefaults[i])
159 return false;
160
161 return true;
162 }
163
164 wxString wxMethod::GetAsString() const
165 {
166 wxString ret;
167
168 if (m_retType!=wxEmptyType)
169 ret += m_retType.GetAsString() + " ";
170 //else; this is a ctor or dtor
171
172 ret += m_strName + "(";
173
174 for (unsigned int i=0; i<m_args.GetCount(); i++)
175 {
176 ret += m_args[i].GetAsString();
177 if (!m_argDefaults[i].IsEmpty())
178 ret += " = " + m_argDefaults[i];
179 ret += ",";
180 }
181
182 if (m_args.GetCount()>0)
183 ret.RemoveLast();
184
185 ret += ")";
186
187 if (m_bConst)
188 ret += " const";
189 if (m_bStatic)
190 ret = "static " + ret;
191 if (m_bVirtual)
192 ret = "virtual " + ret;
193
194 return ret;
195 }
196
197 void wxMethod::Dump(wxTextOutputStream& stream) const
198 {
199 stream << "[" + m_retType.GetAsString() + "]";
200 stream << "[" + m_strName + "]";
201
202 for (unsigned int i=0; i<m_args.GetCount(); i++)
203 stream << "[" + m_args[i].GetAsString() + "=" + m_argDefaults[i] + "]";
204
205 if (IsConst())
206 stream << " CONST";
207 if (IsStatic())
208 stream << " STATIC";
209 if (IsVirtual())
210 stream << " VIRTUAL";
211
212 // no final newline
213 }
214
215 // ----------------------------------------------------------------------------
216 // wxClass
217 // ----------------------------------------------------------------------------
218
219 wxString wxClass::GetNameWithoutTemplate() const
220 {
221 // NB: I'm not sure this is the right terminology for this function!
222
223 if (m_strName.Contains("<"))
224 return m_strName.Left(m_strName.Find("<"));
225 return m_strName;
226 }
227
228 bool wxClass::IsValidCtorForThisClass(const wxMethod& m) const
229 {
230 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
231 // named wxWritableCharTypeBuffer, without the <...> part!
232
233 if (m.IsCtor() && m.GetName() == GetNameWithoutTemplate())
234 return true;
235
236 return false;
237 }
238
239 bool wxClass::IsValidDtorForThisClass(const wxMethod& m) const
240 {
241 if (m.IsDtor() && m.GetName() == "~" + GetNameWithoutTemplate())
242 return true;
243
244 return false;
245 }
246
247 void wxClass::Dump(wxTextOutputStream& out) const
248 {
249 out << m_strName + "\n";
250
251 for (unsigned int i=0; i<m_methods.GetCount(); i++) {
252
253 // dump all our methods
254 out << "|- ";
255 m_methods[i].Dump(out);
256 out << "\n";
257 }
258
259 out << "\n";
260 }
261
262 bool wxClass::CheckConsistency() const
263 {
264 for (unsigned int i=0; i<m_methods.GetCount(); i++)
265 for (unsigned int j=0; j<m_methods.GetCount(); j++)
266 if (i!=j && m_methods[i] == m_methods[j])
267 {
268 LogError("class %s has two methods with the same prototype: '%s'",
269 m_strName, m_methods[i].GetAsString());
270 return false;
271 }
272
273 return true;
274 }
275
276 const wxMethod* wxClass::FindMethod(const wxMethod& m) const
277 {
278 for (unsigned int i=0; i<m_methods.GetCount(); i++)
279 if (m_methods[i] == m)
280 return &m_methods[i];
281 return NULL;
282 }
283
284 wxMethodPtrArray wxClass::FindMethodNamed(const wxString& name) const
285 {
286 wxMethodPtrArray ret;
287
288 for (unsigned int i=0; i<m_methods.GetCount(); i++)
289 if (m_methods[i].GetName() == name)
290 ret.Add(&m_methods[i]);
291
292 return ret;
293 }
294
295
296 // ----------------------------------------------------------------------------
297 // wxXmlInterface
298 // ----------------------------------------------------------------------------
299
300 WX_DEFINE_SORTED_ARRAY(wxClass*, wxSortedClassArray);
301
302 int CompareWxClassObjects(wxClass *item1, wxClass *item2)
303 {
304 // sort alphabetically
305 return item1->GetName().Cmp(item2->GetName());
306 }
307
308 void wxXmlInterface::Dump(const wxString& filename)
309 {
310 wxFFileOutputStream apioutput( filename );
311 wxTextOutputStream apiout( apioutput );
312
313 // dump the classes in alphabetical order
314 wxSortedClassArray sorted(CompareWxClassObjects);
315 sorted.Alloc(m_classes.GetCount());
316 for (unsigned int i=0; i<m_classes.GetCount(); i++)
317 sorted.Add(&m_classes[i]);
318
319 // now they have been sorted
320 for (unsigned int i=0; i<sorted.GetCount(); i++)
321 sorted[i]->Dump(apiout);
322 }
323
324 bool wxXmlInterface::CheckParseResults() const
325 {
326 // this check can be quite slow, so do it only for debug releases:
327 #ifdef __WXDEBUG__
328 for (unsigned int i=0; i<m_classes.GetCount(); i++)
329 if (!m_classes[i].CheckConsistency())
330 return false;
331 #endif
332
333 return true;
334 }
335
336 // ----------------------------------------------------------------------------
337 // wxXmlGccInterface
338 // ----------------------------------------------------------------------------
339
340 #define ATTRIB_CONST 1
341 #define ATTRIB_REFERENCE 2
342 #define ATTRIB_POINTER 4
343 #define ATTRIB_ARRAY 8
344
345 class toResolveTypeItem
346 {
347 public:
348 toResolveTypeItem() { attribs=0; }
349 toResolveTypeItem(const wxString& namestr, int attribint)
350 : ref(namestr), attribs(attribint) {}
351
352 wxString ref;
353 int attribs;
354 };
355
356 #if 1
357 WX_DECLARE_STRING_HASH_MAP( toResolveTypeItem, wxToResolveTypeHashMap );
358 #else
359 #include <map>
360 typedef std::map<wxString, toResolveTypeItem> wxToResolveTypeHashMap;
361 #endif
362
363 bool wxXmlGccInterface::Parse(const wxString& filename)
364 {
365 wxXmlDocument doc;
366 wxXmlNode *child;
367 int nodes = 0;
368
369 LogMessage("Parsing %s...", filename);
370
371 if (!doc.Load(filename)) {
372 LogError("can't load %s", filename);
373 return false;
374 }
375
376 // start processing the XML file
377 if (doc.GetRoot()->GetName() != "GCC_XML") {
378 LogError("invalid root node for %s", filename);
379 return false;
380 }
381
382 wxToResolveTypeHashMap toResolveTypes;
383 wxArrayString arrMemberIds;
384 wxStringHashMap types;
385 wxStringHashMap files;
386
387 // prealloc quite a lot of memory!
388 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
389 arrMemberIds.Alloc(ESTIMATED_NUM_TYPES);
390
391 // build a list of wx classes and in general of all existent types
392 child = doc.GetRoot()->GetChildren();
393 while (child)
394 {
395 const wxString& n = child->GetName();
396 const wxString& id = child->GetAttribute("id", wxEmptyString);
397
398 if (n == "Class")
399 {
400 wxString cname = child->GetAttribute("name", wxEmptyString);
401 if (cname.IsEmpty()) {
402 LogError("Invalid empty name for '%s' node", n);
403 return false;
404 }
405
406 // only register wx classes (do remember also the IDs of their members)
407 if (cname.StartsWith("wx")) {
408 arrMemberIds.Add(child->GetAttribute("members", wxEmptyString));
409
410 // NB: "file" attribute contains an ID value that we'll resolve later
411 m_classes.Add(wxClass(cname, child->GetAttribute("file", wxEmptyString)));
412 }
413
414 // register this class also as possible return/argument type:
415 types[id] = cname;
416 }
417 else if (n == "PointerType" || n == "ReferenceType" ||
418 n == "CvQualifiedType" || n == "ArrayType")
419 {
420 const wxString& type = child->GetAttribute("type", wxEmptyString);
421 if (id.IsEmpty() || type.IsEmpty()) {
422 LogError("Invalid empty type/id for '%s' node", n);
423 return false;
424 }
425
426 int attr = 0;
427 if (n == "PointerType")
428 attr = ATTRIB_POINTER;
429 else if (n == "ReferenceType")
430 attr = ATTRIB_REFERENCE;
431 else if (n == "CvQualifiedType" && child->GetAttribute("const", "") == "1")
432 attr = ATTRIB_CONST;
433 else if (n == "ArrayType")
434 attr = ATTRIB_ARRAY;
435
436 // these nodes make reference to other types... we'll resolve them later
437 toResolveTypes[id] = toResolveTypeItem(type, attr);
438 }
439 else if (n == "FunctionType" || n == "MethodType")
440 {
441 /* TODO: incomplete */
442
443 const wxString& ret = child->GetAttribute("returns", wxEmptyString);
444 if (id.IsEmpty() || ret.IsEmpty()) {
445 LogError("Invalid empty ret/id for '%s' node", n);
446 return false;
447 }
448
449 // these nodes make reference to other types... we'll resolve them later
450 toResolveTypes[id] = toResolveTypeItem(ret, 0);
451 }
452 else if (n == "File")
453 {
454 if (!id.StartsWith("f")) {
455 LogError("Unexpected file ID: %s", id);
456 return false;
457 }
458
459 // just ignore this node... all file IDs/names were already parsed
460 files[id] = child->GetAttribute("name", "");
461 }
462 else
463 {
464 // we register everything else as a possible return/argument type:
465 const wxString& name = child->GetAttribute("name", wxEmptyString);
466
467 if (!name.IsEmpty())
468 {
469 //typeIds.Add(id);
470 //typeNames.Add(name);
471 types[id] = name;
472 }
473 else
474 {
475 // this may happen with unnamed structs/union, special ctors,
476 // or other exotic things which we are not interested to, since
477 // they're never used as return/argument types by wxWidgets methods
478
479 if (g_verbose)
480 LogWarning("Type '%s' with ID '%s' does not have name attribute", n, id);
481
482 types[id] = "TOFIX";
483 }
484 }
485
486 child = child->GetNext();
487
488 // give feedback to the user about the progress...
489 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
490 }
491
492 // some nodes with IDs referenced by methods as return/argument types, do reference
493 // in turn o ther nodes (see PointerType, ReferenceType and CvQualifierType above);
494 // thus we need to resolve their name iteratively:
495 while (toResolveTypes.size()>0)
496 {
497 if (g_verbose)
498 LogMessage("%d types were collected; %d types need yet to be resolved...",
499 types.size(), toResolveTypes.size());
500
501 for (wxToResolveTypeHashMap::iterator i = toResolveTypes.begin();
502 i != toResolveTypes.end();)
503 {
504 const wxString& id = i->first;
505 const wxString& referenced = i->second.ref;
506
507 wxStringHashMap::iterator primary = types.find(referenced);
508 if (primary != types.end())
509 {
510 // this to-resolve-type references a "primary" type
511
512 wxString newtype;
513 int attribs = i->second.attribs;
514
515 if (attribs & ATTRIB_CONST)
516 newtype = "const " + primary->second;
517 if (attribs & ATTRIB_REFERENCE)
518 newtype = primary->second + "&";
519 if (attribs & ATTRIB_POINTER)
520 newtype = primary->second + "*";
521 if (attribs & ATTRIB_ARRAY)
522 newtype = primary->second + "[]";
523
524 // add the resolved type to the list of "primary" types
525 types[id] = newtype;
526
527 // this one has been resolved; erase it through its iterator!
528 toResolveTypes.erase(i);
529
530 // now iterator i is invalid; assign it again to the beginning
531 i = toResolveTypes.begin();
532 }
533 else
534 {
535 // then search in the referenced types themselves:
536 wxToResolveTypeHashMap::iterator idx2 = toResolveTypes.find(referenced);
537 if (idx2 != toResolveTypes.end())
538 {
539 // merge this to-resolve-type with the idx2->second type
540 i->second.ref = idx2->second.ref;
541 i->second.attribs |= idx2->second.attribs;
542
543 // this type will eventually be solved in the next while() iteration
544 i++;
545 }
546 else
547 {
548 #if 1
549 LogError("Cannot solve '%s' reference type!", referenced);
550 return false;
551 #else
552 typeIds.Add(toResolveTypeIds[i]);
553 typeNames.Add("TOFIX");
554
555 // this one has been resolved!
556 toResolveTypeIds.RemoveAt(i);
557 toResolveRefType.RemoveAt(i);
558 toResolveAttrib.RemoveAt(i);
559 n--;
560 #endif
561 }
562 }
563 }
564 }
565
566 // resolve header names
567 for (unsigned int i=0; i<m_classes.GetCount(); i++)
568 {
569 wxStringHashMap::const_iterator idx = files.find(m_classes[i].GetHeader());
570 if (idx == files.end())
571 {
572 // this is an error!
573 LogError("couldn't find file ID '%s'", m_classes[i].GetHeader());
574 }
575 else
576 m_classes[i].SetHeader(idx->second);
577 }
578
579 // build the list of the wx methods
580 child = doc.GetRoot()->GetChildren();
581 while (child)
582 {
583 wxString n = child->GetName();
584
585 if (n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod")
586 {
587 wxString id = child->GetAttribute("id", wxEmptyString);
588
589 // only register public methods
590 if (child->GetAttribute("access", wxEmptyString) == "public")
591 {
592 wxASSERT(arrMemberIds.GetCount()==m_classes.GetCount());
593
594 for (unsigned int i=0; i<m_classes.GetCount(); i++)
595 {
596 if (arrMemberIds[i].Contains(id))
597 {
598 // this <Method> node is a method of the i-th class!
599 wxMethod newfunc;
600 if (!ParseMethod(child, types, newfunc))
601 return false;
602
603 if (newfunc.IsCtor() && !m_classes[i].IsValidCtorForThisClass(newfunc)) {
604 LogError("The method '%s' does not seem to be a ctor for '%s'",
605 newfunc.GetName(), m_classes[i].GetName());
606 return false;
607 }
608 if (newfunc.IsDtor() && !m_classes[i].IsValidDtorForThisClass(newfunc)) {
609 LogError("The method '%s' does not seem to be a dtor for '%s'",
610 newfunc.GetName(), m_classes[i].GetName());
611 return false;
612 }
613
614 m_classes[i].AddMethod(newfunc);
615 }
616 }
617 }
618 }
619
620 child = child->GetNext();
621
622 // give feedback to the user about the progress...
623 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
624 }
625
626 //wxPrint("\n");
627 if (!CheckParseResults())
628 return false;
629
630 return true;
631 }
632
633 bool wxXmlGccInterface::ParseMethod(const wxXmlNode *p,
634 const wxStringHashMap& types,
635 wxMethod& m)
636 {
637 // get the real name
638 wxString name = p->GetAttribute("name", wxEmptyString).Strip(wxString::both);
639 if (p->GetName() == "Destructor")
640 name = "~" + name;
641 else if (p->GetName() == "OperatorMethod")
642 name = "operator" + name;
643
644 // resolve return type
645 wxType ret;
646 wxString retid = p->GetAttribute("returns", wxEmptyString);
647 if (retid.IsEmpty())
648 {
649 if (p->GetName() != "Destructor" && p->GetName() != "Constructor") {
650 LogError("Empty return ID for method '%s', with ID '%s'",
651 name, p->GetAttribute("id", ""));
652 return false;
653 }
654 }
655 else
656 {
657 wxStringHashMap::const_iterator retidx = types.find(retid);
658 if (retidx == types.end()) {
659 LogError("Could not find return type ID '%s'", retid);
660 return false;
661 }
662
663 ret = wxType(retidx->second);
664 if (!ret.IsOk()) {
665 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
666 retidx->second, name, p->GetAttribute("id", ""));
667 return false;
668 }
669 }
670
671 // resolve argument types
672 wxTypeArray argtypes;
673 wxArrayString argdefs;
674 wxXmlNode *arg = p->GetChildren();
675 while (arg)
676 {
677 if (arg->GetName() == "Argument")
678 {
679 wxString id = arg->GetAttribute("type", wxEmptyString);
680 wxStringHashMap::const_iterator idx = types.find(id);
681 if (idx == types.end()) {
682 LogError("Could not find argument type ID '%s'", id);
683 return false;
684 }
685
686 argtypes.Add(wxType(idx->second));
687
688 wxString def = arg->GetAttribute("default", "");
689 if (def.Contains("wxGetTranslation"))
690 argdefs.Add(wxEmptyString); // TODO: wxGetTranslation gives problems to gccxml
691 else
692 argdefs.Add(def);
693 }
694
695 arg = arg->GetNext();
696 }
697
698 m.SetReturnType(ret);
699 m.SetName(name);
700 m.SetArgumentTypes(argtypes, argdefs);
701 m.SetConst(p->GetAttribute("const", "") == "1");
702 m.SetStatic(p->GetAttribute("static", "") == "1");
703 m.SetVirtual(p->GetAttribute("virtual", "") == "1");
704
705 if (!m.IsOk()) {
706 LogError("The prototype '%s' is not valid!", m.GetAsString());
707 return false;
708 }
709
710 return true;
711 }
712
713
714 // ----------------------------------------------------------------------------
715 // wxXmlDoxygenInterface
716 // ----------------------------------------------------------------------------
717
718 bool wxXmlDoxygenInterface::Parse(const wxString& filename)
719 {
720 wxXmlDocument index;
721 wxXmlNode *compound;
722
723 LogMessage("Parsing %s...", filename);
724
725 if (!index.Load(filename)) {
726 LogError("can't load %s", filename);
727 return false;
728 }
729
730 // start processing the index:
731 if (index.GetRoot()->GetName() != "doxygenindex") {
732 LogError("invalid root node for %s", filename);
733 return false;
734 }
735
736 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
737
738 // process files referenced by this index file
739 compound = index.GetRoot()->GetChildren();
740 while (compound)
741 {
742 if (compound->GetName() == "compound" &&
743 compound->GetAttribute("kind", "") == "class")
744 {
745 wxString refid = compound->GetAttribute("refid", "");
746
747 wxFileName fn(filename);
748 if (!ParseCompoundDefinition(fn.GetPath(wxPATH_GET_SEPARATOR) + refid + ".xml"))
749 return false;
750 }
751
752 compound = compound->GetNext();
753 }
754 //wxPrint("\n");
755
756 if (!CheckParseResults())
757 return false;
758
759 return true;
760 }
761
762 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
763 {
764 wxXmlDocument doc;
765 wxXmlNode *child;
766 int nodes = 0;
767
768 if (g_verbose)
769 LogMessage("Parsing %s...", filename);
770
771 if (!doc.Load(filename)) {
772 LogError("can't load %s", filename);
773 return false;
774 }
775
776 // start processing this compound definition XML
777 if (doc.GetRoot()->GetName() != "doxygen") {
778 LogError("invalid root node for %s", filename);
779 return false;
780 }
781
782 // build a list of wx classes
783 child = doc.GetRoot()->GetChildren();
784 while (child)
785 {
786 if (child->GetName() == "compounddef" &&
787 child->GetAttribute("kind", wxEmptyString) == "class")
788 {
789 // parse this class
790 wxClass klass;
791 wxString absoluteFile, header;
792
793 wxXmlNode *subchild = child->GetChildren();
794 while (subchild)
795 {
796 if (subchild->GetName() == "sectiondef" &&
797 subchild->GetAttribute("kind", wxEmptyString) == "public-func")
798 {
799
800 wxXmlNode *membernode = subchild->GetChildren();
801 while (membernode)
802 {
803 if (membernode->GetName() == "memberdef" &&
804 membernode->GetAttribute("kind", wxEmptyString) == "function")
805 {
806
807 wxMethod m;
808 if (ParseMethod(membernode, m, header))
809 {
810 if (absoluteFile.IsEmpty())
811 absoluteFile = header;
812 else if (header != absoluteFile)
813 {
814 LogError("The method '%s' is documented in a different "
815 "file from others (which belong to '%s') ?",
816 header, absoluteFile);
817 return false;
818 }
819
820 klass.AddMethod(m);
821 }
822 }
823
824 membernode = membernode->GetNext();
825 }
826
827 // all methods of this class were taken from the header "absoluteFile":
828 klass.SetHeader(absoluteFile);
829 }
830 else if (subchild->GetName() == "compoundname")
831 {
832 klass.SetName(subchild->GetNodeContent());
833 }
834 /*else if (subchild->GetName() == "includes")
835 {
836 // NOTE: we'll get the header from the <location> tags
837 // scattered inside <memberdef> tags instead of
838 // this <includes> tag since it does not contain
839 // the absolute path of the header
840
841 klass.SetHeader(subchild->GetNodeContent());
842 }*/
843
844 subchild = subchild->GetNext();
845 }
846
847 // add a new class
848 if (klass.IsOk())
849 m_classes.Add(klass);
850 else if (g_verbose)
851 LogWarning("discarding class '%s' with %d methods...",
852 klass.GetName(), klass.GetMethodCount());
853 }
854
855 child = child->GetNext();
856
857 // give feedback to the user about the progress...
858 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
859 }
860
861 return true;
862 }
863
864 static wxString GetTextFromChildren(const wxXmlNode *n)
865 {
866 wxString text;
867
868 // consider the tree
869 //
870 // <a><b>this</b> is a <b>string</b></a>
871 //
872 // <a>
873 // |- <b>
874 // | |- this
875 // |- is a
876 // |- <b>
877 // |- string
878 //
879 // unlike wxXmlNode::GetNodeContent() which would return " is a "
880 // this function returns "this is a string"
881
882 wxXmlNode *ref = n->GetChildren();
883 while (ref) {
884 if (ref->GetType() == wxXML_ELEMENT_NODE)
885 text += ref->GetNodeContent();
886 else if (ref->GetType() == wxXML_TEXT_NODE)
887 text += ref->GetContent();
888 else
889 LogWarning("Unexpected node type while getting text from '%s' node", n->GetName());
890
891 ref = ref->GetNext();
892 }
893
894 return text;
895 }
896
897 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode* p, wxMethod& m, wxString& header)
898 {
899 wxTypeArray args;
900 wxArrayString defs;
901 long line;
902
903 wxXmlNode *child = p->GetChildren();
904 while (child)
905 {
906 if (child->GetName() == "name")
907 m.SetName(child->GetNodeContent());
908 else if (child->GetName() == "type")
909 m.SetReturnType(wxType(GetTextFromChildren(child)));
910 else if (child->GetName() == "param")
911 {
912 wxString typestr, defstr, arrstr;
913 wxXmlNode *n = child->GetChildren();
914 while (n)
915 {
916 if (n->GetName() == "type")
917 // if the <type> node has children, they should be all TEXT and <ref> nodes
918 // and we need to take the text they contain, in the order they appear
919 typestr = GetTextFromChildren(n);
920 else if (n->GetName() == "defval")
921 // same for the <defval> node
922 defstr = GetTextFromChildren(n);
923 else if (n->GetName() == "array")
924 arrstr = GetTextFromChildren(n);
925
926 n = n->GetNext();
927 }
928
929 if (typestr.IsEmpty()) {
930 LogError("cannot find type node for a param in method '%s'", m.GetName());
931 return false;
932 }
933
934 args.Add(wxType(typestr + arrstr));
935 defs.Add(defstr);
936 }
937 else if (child->GetName() == "location")
938 {
939 if (child->GetAttribute("line", "").ToLong(&line))
940 m.SetLocation((int)line);
941 header = child->GetAttribute("file", "");
942 }
943
944 child = child->GetNext();
945 }
946
947 m.SetArgumentTypes(args, defs);
948 m.SetConst(p->GetAttribute("const", "")=="yes");
949 m.SetStatic(p->GetAttribute("static", "")=="yes");
950 m.SetVirtual(p->GetAttribute("virt", "")=="virtual");
951
952 if (!m.IsOk()) {
953 LogError("The prototype '%s' is not valid!", m.GetAsString());
954 return false;
955 }
956
957 return true;
958 }