]> git.saurik.com Git - wxWidgets.git/blob - utils/ifacecheck/src/xmlparser.cpp
8ce5cf383aa9417527520b61971abfefc40bf62b
[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 // global variable:
44 bool g_bLogEnabled = true;
45
46
47
48 // ----------------------------------------------------------------------------
49 // wxType
50 // ----------------------------------------------------------------------------
51
52 wxType wxEmptyType;
53
54 void wxType::SetTypeFromString(const wxString& t)
55 {
56 /*
57 TODO: optimize the following code writing a single function
58 which works at char-level and does everything in a single pass
59 */
60
61 m_strType = t;
62
63 // [] is the same as * for gccxml
64 m_strType.Replace("[]", "*");
65 m_strType.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
66
67 // make sure the * and & operator always use the same spacing rules
68 // (to make sure GetAsString() output is always consistent)
69 m_strType.Replace("*", "* ");
70 m_strType.Replace("&", "& ");
71 m_strType.Replace(" *", "*");
72 m_strType.Replace(" &", "&");
73
74 while (m_strType.Contains(" "))
75 m_strType.Replace(" ", " "); // do it once again
76
77 m_strType.Replace(" ,", ",");
78
79 m_strType = m_strType.Strip(wxString::both);
80
81 // now set the clean version
82 m_strTypeClean = m_strType;
83 m_strTypeClean.Replace("const", "");
84 m_strTypeClean.Replace("static", "");
85 m_strTypeClean.Replace("*", "");
86 m_strTypeClean.Replace("&", "");
87 m_strTypeClean.Replace("[]", "");
88 m_strTypeClean = m_strTypeClean.Strip(wxString::both);
89
90 // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
91 // need to be considered as the same type
92 if (m_strTypeClean.EndsWith("Base"))
93 m_strTypeClean = m_strTypeClean.Left(m_strTypeClean.Len()-4);
94
95 // ADHOC-FIX:
96 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
97 // fix this to avoid false positives
98 m_strTypeClean.Replace("wxDateTime::", "");
99 m_strTypeClean.Replace("wxStockGDI::", ""); // same story for some other classes
100 m_strTypeClean.Replace("wxHelpEvent::", "");
101 }
102
103 bool wxType::IsOk() const
104 {
105 // NB: m_strType can contain the :: operator; think to e.g. the
106 // "reverse_iterator_impl<wxString::const_iterator>" type
107 // It can also contain commas, * and & operators etc
108
109 return !m_strTypeClean.IsEmpty();
110 }
111
112 bool wxType::operator==(const wxType& m) const
113 {
114 // brain-dead comparison:
115
116 if (m_strTypeClean == m.m_strTypeClean &&
117 IsConst() == m.IsConst() &&
118 IsStatic() == m.IsStatic() &&
119 IsPointer() == m.IsPointer() &&
120 IsReference() == m.IsReference())
121 return true;
122
123 if (g_verbose)
124 LogMessage("Type '%s' does not match type '%s'", m_strType, m.m_strType);
125
126 return false;
127 }
128
129
130 // ----------------------------------------------------------------------------
131 // wxArgumentType
132 // ----------------------------------------------------------------------------
133
134 void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& defvalForCmp)
135 {
136 m_strDefaultValue = defval.Strip(wxString::both);
137 m_strDefaultValueForCmp = defvalForCmp.IsEmpty() ? m_strDefaultValue : defvalForCmp.Strip(wxString::both);
138
139 // adjust aesthetic form of DefaultValue for the modify mode of ifacecheck:
140 // we may need to write it out in an interface header
141 if (m_strDefaultValue == "0u")
142 m_strDefaultValue = "0";
143
144 // in order to make valid&simple comparison on argument defaults,
145 // we reduce some of the multiple forms in which the same things may appear
146 // to a single form:
147 if (m_strDefaultValueForCmp == "0u")
148 m_strDefaultValueForCmp = "0";
149
150 m_strDefaultValue.Replace("0x000000001", "1");
151 m_strDefaultValueForCmp.Replace("0x000000001", "1");
152
153 // fix for unicode strings:
154 m_strDefaultValue.Replace("\\000\\000\\000", "");
155 m_strDefaultValueForCmp.Replace("\\000\\000\\000", "");
156
157 if (m_strDefaultValueForCmp.StartsWith("wxT(") &&
158 m_strDefaultValueForCmp.EndsWith(")"))
159 {
160 // get rid of the wxT() part
161 unsigned int len = m_strDefaultValueForCmp.Len();
162 m_strDefaultValueForCmp = m_strDefaultValueForCmp.Mid(4,len-5);
163 }
164
165 /*
166 if (IsPointer())
167 m_strDefaultValueForCmp.Replace("0", "NULL");
168 else
169 m_strDefaultValueForCmp.Replace("NULL", "0");
170 */
171 // ADHOC-FIX:
172 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
173 // fix this to avoid false positives
174 m_strDefaultValueForCmp.Replace("wxDateTime::", "");
175 m_strDefaultValueForCmp.Replace("wxStockGDI::", ""); // same story for some other classes
176 m_strDefaultValueForCmp.Replace("wxHelpEvent::", ""); // same story for some other classes
177
178 m_strDefaultValueForCmp.Replace("wxGet_wxConvLocal()", "wxConvLocal");
179
180 m_strDefaultValueForCmp.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
181
182 // ADHOC-FIX:
183 if (m_strDefaultValueForCmp.Contains("wxGetTranslation"))
184 m_strDefaultValueForCmp = "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
185 }
186
187 bool wxArgumentType::operator==(const wxArgumentType& m) const
188 {
189 if ((const wxType&)(*this) != (const wxType&)m)
190 return false;
191
192 // ADHOC-FIX:
193 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
194 // numbers; avoid false positives in this case!
195 if (m_strArgName == m.m_strArgName && m_strArgName == "style" &&
196 (m_strDefaultValueForCmp.IsNumber() || m.m_strDefaultValueForCmp.IsNumber()))
197 return true;
198
199 // fix for default values which were replaced by gcc-xml with their numeric values
200 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
201 if (m_strTypeClean == "long" || m_strTypeClean == "int")
202 {
203 if ((m_strDefaultValueForCmp.IsNumber() && m.m_strDefaultValueForCmp.StartsWith("wx")) ||
204 (m.m_strDefaultValueForCmp.IsNumber() && m_strDefaultValueForCmp.StartsWith("wx")))
205 {
206 if (g_verbose)
207 LogMessage("Supposing '%s' default value to be the same of '%s'...",
208 m_strDefaultValueForCmp, m.m_strDefaultValueForCmp);
209
210 return true;
211 }
212 }
213
214 if (m_strDefaultValueForCmp != m.m_strDefaultValueForCmp)
215 {
216 // maybe the default values are numbers.
217 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
218 // To handle these cases, we try to convert the default value strings to numbers:
219 long def1val, def2val;
220 if (m_strDefaultValueForCmp.ToLong(&def1val, 0 /* auto-detect */) &&
221 m.m_strDefaultValueForCmp.ToLong(&def2val, 0 /* auto-detect */))
222 {
223 if (def1val == def2val)
224 return true; // the default values match
225 }
226
227 if (g_verbose)
228 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
229 m_strType, m_strDefaultValueForCmp, m.m_strType, m.m_strDefaultValueForCmp);
230 return false;
231 }
232
233 // we deliberately avoid checks on the argument name
234
235 return true;
236 }
237
238
239 // ----------------------------------------------------------------------------
240 // wxMethod
241 // ----------------------------------------------------------------------------
242
243 bool wxMethod::IsOk() const
244 {
245 // NOTE: m_retType can be a wxEmptyType, and means that this method
246 // is a ctor or a dtor.
247 if (!m_retType.IsOk() && m_retType!=wxEmptyType) {
248 LogError("'%s' method has invalid return type: %s", m_retType.GetAsString());
249 return false;
250 }
251
252 if (m_strName.IsEmpty())
253 return false;
254
255 // a function can't be both const and static or virtual and static!
256 if ((m_bConst && m_bStatic) || ((m_bVirtual || m_bPureVirtual) && m_bStatic)) {
257 LogError("'%s' method can't be both const/static or virtual/static", m_strName);
258 return false;
259 }
260
261 wxASSERT(!m_bPureVirtual || (m_bPureVirtual && m_bVirtual));
262
263 for (unsigned int i=0; i<m_args.GetCount(); i++)
264 if (!m_args[i].IsOk()) {
265 LogError("'%s' method has invalid %d-th argument type: %s",
266 m_strName, i+1, m_args[i].GetAsString());
267 return false;
268 }
269
270 // NB: the default value of the arguments can contain pretty much everything
271 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
272 // so we don't do any test on their contents
273 if (m_args.GetCount()>0)
274 {
275 bool previousArgHasDefault = m_args[0].HasDefaultValue();
276 for (unsigned int i=1; i<m_args.GetCount(); i++)
277 {
278 if (previousArgHasDefault && !m_args[i].HasDefaultValue()) {
279 LogError("'%s' method has %d-th argument which has no default value "
280 "(while the previous one had one!)",
281 m_strName, i+1);
282 return false;
283 }
284
285 previousArgHasDefault = m_args[i].HasDefaultValue();
286 }
287 }
288
289 return true;
290 }
291
292 bool wxMethod::MatchesExceptForAttributes(const wxMethod& m) const
293 {
294 if (GetReturnType() != m.GetReturnType() ||
295 GetName() != m.GetName())
296 return false;
297
298 if (m_args.GetCount()!=m.m_args.GetCount()) {
299 if (g_verbose)
300 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
301 m_strName, m_args.GetCount(), m_strName, m.m_args.GetCount());
302 return false;
303 }
304
305 // compare argument types
306 for (unsigned int i=0; i<m_args.GetCount(); i++)
307 if (m_args[i] != m.m_args[i])
308 return false;
309
310 return true;
311 }
312
313 bool wxMethod::operator==(const wxMethod& m) const
314 {
315 // check attributes
316 if (IsConst() != m.IsConst() ||
317 IsStatic() != m.IsStatic() ||
318 IsVirtual() != m.IsVirtual() ||
319 IsPureVirtual() != m.IsPureVirtual() ||
320 IsDeprecated() != m.IsDeprecated() ||
321 GetAccessSpecifier() != m.GetAccessSpecifier())
322 return false;
323
324 // check everything else
325 return MatchesExceptForAttributes(m);
326 }
327
328 wxString wxMethod::GetAsString(bool bWithArgumentNames, bool bCleanDefaultValues,
329 bool bDeprecated, bool bAccessSpec) const
330 {
331 wxString ret;
332
333 // NOTE: for return and argument types, never use wxType::GetAsCleanString
334 // since in that way we'd miss important decorators like &,*,const etc
335
336 if (m_retType!=wxEmptyType)
337 ret += m_retType.GetAsString() + " ";
338 //else; this is a ctor or dtor
339
340 ret += m_strName + "(";
341
342 for (unsigned int i=0; i<m_args.GetCount(); i++)
343 {
344 ret += m_args[i].GetAsString();
345
346 const wxString& name = m_args[i].GetArgumentName();
347 if (bWithArgumentNames && !name.IsEmpty())
348 ret += " " + name;
349
350 const wxString& def = bCleanDefaultValues ?
351 m_args[i].GetDefaultCleanValue() : m_args[i].GetDefaultValue();
352 if (!def.IsEmpty())
353 ret += " = " + def;
354
355 ret += ", ";
356 }
357
358 if (m_args.GetCount()>0)
359 ret = ret.Left(ret.Len()-2);
360
361 ret += ")";
362
363 if (m_bConst)
364 ret += " const";
365 if (m_bStatic)
366 ret = "static " + ret;
367 if (m_bVirtual || m_bPureVirtual)
368 ret = "virtual " + ret;
369 if (m_bPureVirtual)
370 ret += " = 0";
371 if (m_bDeprecated && bDeprecated)
372 ret += " [deprecated]";
373
374 if (bAccessSpec)
375 {
376 switch (m_access)
377 {
378 case wxMAS_PUBLIC:
379 ret += " [public]";
380 break;
381 case wxMAS_PROTECTED:
382 ret += " [protected]";
383 break;
384 case wxMAS_PRIVATE:
385 ret += " [private]";
386 break;
387 }
388 }
389
390 return ret;
391 }
392
393 void wxMethod::Dump(wxTextOutputStream& stream) const
394 {
395 stream << "[" + m_retType.GetAsString() + "]";
396 stream << "[" + m_strName + "]";
397
398 for (unsigned int i=0; i<m_args.GetCount(); i++)
399 stream << "[" + m_args[i].GetAsString() + " " + m_args[i].GetArgumentName() +
400 "=" + m_args[i].GetDefaultValue() + "]";
401
402 if (IsConst())
403 stream << " CONST";
404 if (IsStatic())
405 stream << " STATIC";
406 if (IsVirtual())
407 stream << " VIRTUAL";
408 if (IsPureVirtual())
409 stream << " PURE-VIRTUAL";
410 if (IsDeprecated())
411 stream << " DEPRECATED";
412
413 // no final newline
414 }
415
416 // ----------------------------------------------------------------------------
417 // wxClass
418 // ----------------------------------------------------------------------------
419
420 wxString wxClass::GetNameWithoutTemplate() const
421 {
422 // NB: I'm not sure this is the right terminology for this function!
423
424 if (m_strName.Contains("<"))
425 return m_strName.Left(m_strName.Find("<"));
426 return m_strName;
427 }
428
429 bool wxClass::IsValidCtorForThisClass(const wxMethod& m) const
430 {
431 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
432 // named wxWritableCharTypeBuffer, without the <...> part!
433
434 if (m.IsCtor() && m.GetName() == GetNameWithoutTemplate())
435 return true;
436
437 return false;
438 }
439
440 bool wxClass::IsValidDtorForThisClass(const wxMethod& m) const
441 {
442 if (m.IsDtor() && m.GetName() == "~" + GetNameWithoutTemplate())
443 return true;
444
445 return false;
446 }
447
448 void wxClass::Dump(wxTextOutputStream& out) const
449 {
450 out << m_strName + "\n";
451
452 for (unsigned int i=0; i<m_methods.GetCount(); i++) {
453
454 // dump all our methods
455 out << "|- ";
456 m_methods[i].Dump(out);
457 out << "\n";
458 }
459
460 out << "\n";
461 }
462
463 bool wxClass::CheckConsistency() const
464 {
465 for (unsigned int i=0; i<m_methods.GetCount(); i++)
466 for (unsigned int j=0; j<m_methods.GetCount(); j++)
467 if (i!=j && m_methods[i] == m_methods[j])
468 {
469 LogError("class %s has two methods with the same prototype: '%s'",
470 m_strName, m_methods[i].GetAsString());
471 return false;
472 ((wxClass*)this)->m_methods.RemoveAt(j);
473 j--;
474 }
475
476 return true;
477 }
478
479 const wxMethod* wxClass::FindMethod(const wxMethod& m) const
480 {
481 for (unsigned int i=0; i<m_methods.GetCount(); i++)
482 if (m_methods[i] == m)
483 return &m_methods[i];
484 return NULL;
485 }
486
487 const wxMethod* wxClass::RecursiveUpwardFindMethod(const wxMethod& m,
488 const wxXmlInterface* allclasses) const
489 {
490 // first, search into *this
491 const wxMethod* ret = FindMethod(m);
492 if (ret)
493 return ret;
494
495 // then, search into its parents
496 for (unsigned int i=0; i<m_parents.GetCount(); i++)
497 {
498 // ignore non-wx-classes parents
499 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
500 if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
501 {
502 const wxClass *parent = allclasses->FindClass(m_parents[i]);
503 if (!parent) {
504 LogError("Could not find parent '%s' of class '%s'...",
505 m_parents[i], GetName());
506 return false;
507 }
508
509 const wxMethod *parentMethod = parent->RecursiveUpwardFindMethod(m, allclasses);
510 if (parentMethod)
511 return parentMethod;
512 }
513 }
514
515 // could not find anything even in parent classes...
516 return NULL;
517 }
518
519 wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const
520 {
521 wxMethodPtrArray ret;
522
523 for (unsigned int i=0; i<m_methods.GetCount(); i++)
524 if (m_methods[i].GetName() == name)
525 ret.Add(&m_methods[i]);
526
527 return ret;
528 }
529
530
531 wxMethodPtrArray wxClass::RecursiveUpwardFindMethodsNamed(const wxString& name,
532 const wxXmlInterface* allclasses) const
533 {
534 // first, search into *this
535 wxMethodPtrArray ret = FindMethodsNamed(name);
536 if (ret.GetCount()>0)
537 return ret; // stop here, don't look upward in the parents
538
539 // then, search into parents of this class
540 for (unsigned int i=0; i<m_parents.GetCount(); i++)
541 {
542 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
543 if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
544 {
545 const wxClass *parent = allclasses->FindClass(m_parents[i]);
546 if (!parent) {
547 LogError("Could not find parent '%s' of class '%s'...",
548 m_parents[i], GetName());
549 return false;
550 }
551
552 wxMethodPtrArray temp = parent->RecursiveUpwardFindMethodsNamed(name, allclasses);
553 WX_APPEND_ARRAY(ret, temp);
554 }
555 }
556
557 return ret;
558 }
559
560
561
562 // ----------------------------------------------------------------------------
563 // wxXmlInterface
564 // ----------------------------------------------------------------------------
565
566 WX_DEFINE_SORTED_ARRAY(wxClass*, wxSortedClassArray);
567
568 int CompareWxClassObjects(wxClass *item1, wxClass *item2)
569 {
570 // sort alphabetically
571 return item1->GetName().Cmp(item2->GetName());
572 }
573
574 void wxXmlInterface::Dump(const wxString& filename)
575 {
576 wxFFileOutputStream apioutput( filename );
577 wxTextOutputStream apiout( apioutput );
578
579 // dump the classes in alphabetical order
580 wxSortedClassArray sorted(CompareWxClassObjects);
581 sorted.Alloc(m_classes.GetCount());
582 for (unsigned int i=0; i<m_classes.GetCount(); i++)
583 sorted.Add(&m_classes[i]);
584
585 // now they have been sorted
586 for (unsigned int i=0; i<sorted.GetCount(); i++)
587 sorted[i]->Dump(apiout);
588 }
589
590 bool wxXmlInterface::CheckParseResults() const
591 {
592 // this check can be quite slow, so do it only for debug releases:
593 //#ifdef __WXDEBUG__
594 for (unsigned int i=0; i<m_classes.GetCount(); i++)
595 if (!m_classes[i].CheckConsistency())
596 return false;
597 //#endif
598
599 return true;
600 }
601
602 wxClassPtrArray wxXmlInterface::FindClassesDefinedIn(const wxString& headerfile) const
603 {
604 wxClassPtrArray ret;
605
606 for (unsigned int i=0; i<m_classes.GetCount(); i++)
607 if (m_classes[i].GetHeader() == headerfile)
608 ret.Add(&m_classes[i]);
609
610 return ret;
611 }
612
613
614 // ----------------------------------------------------------------------------
615 // wxXmlGccInterface helper declarations
616 // ----------------------------------------------------------------------------
617
618 // or-able flags for a toResolveTypeItem->attrib:
619 #define ATTRIB_CONST 1
620 #define ATTRIB_REFERENCE 2
621 #define ATTRIB_POINTER 4
622 #define ATTRIB_ARRAY 8
623
624 // it may sound strange but gccxml, in order to produce shorter ID names
625 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
626 // in order to be able to translate such strings into numbers using strtoul()
627 // we use as base 10 (possible digits) + 25 (possible characters) = 35
628 #define GCCXML_BASE 35
629
630 class toResolveTypeItem
631 {
632 public:
633 toResolveTypeItem() { attribs=0; }
634 toResolveTypeItem(unsigned int refID, unsigned int attribint)
635 : ref(refID), attribs(attribint) {}
636
637 unsigned long ref, // the referenced type's ID
638 attribs; // the attributes of this reference
639 };
640
641 #if 1
642
643 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
644 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem,
645 wxIntegerHash, wxIntegerEqual,
646 wxToResolveTypeHashMap );
647
648 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
649 WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
650 wxIntegerHash, wxIntegerEqual,
651 wxClassMemberIdHashMap );
652
653 #else
654 #include <map>
655 typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap;
656 #endif
657
658
659 // utility to parse gccXML ID values;
660 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
661 // but is a little bit faster
662 bool getID(unsigned long *id, const wxString& str)
663 {
664 const wxStringCharType * const start = str.wx_str();
665 wxStringCharType *end;
666 #if wxUSE_UNICODE_WCHAR
667 unsigned long val = wcstoul(start, &end, GCCXML_BASE);
668 #else
669 unsigned long val = strtoul(start, &end, GCCXML_BASE);
670 #endif
671
672 // return true only if scan was stopped by the terminating NUL and
673 // if the string was not empty to start with and no under/overflow
674 // occurred:
675 if ( *end != '\0' || end == start || errno == ERANGE || errno == EINVAL )
676 return false;
677
678 *id = val;
679 return true;
680 }
681
682 // utility specialized to parse efficiently the gccXML list of IDs which occur
683 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
684 bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxString& str)
685 {
686 const wxStringCharType * const start = str.wx_str();
687 #if wxUSE_UNICODE_WCHAR
688 size_t len = wcslen(start);
689 #else
690 size_t len = strlen(start);
691 #endif
692
693 if (len == 0 || start[0] != '_')
694 return false;
695
696 const wxStringCharType *curpos = start,
697 *end = start + len;
698 wxStringCharType *nexttoken;
699
700 while (curpos < end)
701 {
702 // curpos always points to the underscore of the next token to parse:
703 #if wxUSE_UNICODE_WCHAR
704 unsigned long id = wcstoul(curpos+1, &nexttoken, GCCXML_BASE);
705 #else
706 unsigned long id = strtoul(curpos+1, &nexttoken, GCCXML_BASE);
707 #endif
708 if ( *nexttoken != ' ' || errno == ERANGE || errno == EINVAL )
709 return false;
710
711 // advance current position
712 curpos = nexttoken + 1;
713
714 // add this ID to the hashmap
715 wxClassMemberIdHashMap::value_type v(id, p);
716 map->insert(v);
717 }
718
719 return true;
720 }
721
722
723 // ----------------------------------------------------------------------------
724 // wxXmlGccInterface
725 // ----------------------------------------------------------------------------
726
727 bool wxXmlGccInterface::Parse(const wxString& filename)
728 {
729 wxXmlDocument doc;
730 wxXmlNode *child;
731 int nodes = 0;
732
733 LogMessage("Parsing %s...", filename);
734
735 if (!doc.Load(filename)) {
736 LogError("can't load %s", filename);
737 return false;
738 }
739
740 // start processing the XML file
741 if (doc.GetRoot()->GetName() != "GCC_XML") {
742 LogError("invalid root node for %s", filename);
743 return false;
744 }
745
746 wxString version = doc.GetRoot()->GetAttribute("cvs_revision");
747 bool old = false;
748
749 #define MIN_REVISION 120
750
751 if (!version.StartsWith("1."))
752 old = true;
753 if (!old)
754 {
755 unsigned long rev = 0;
756 if (!version.Mid(2).ToULong(&rev))
757 old = true;
758 else
759 if (rev < MIN_REVISION)
760 old = true;
761 }
762
763 if (old)
764 {
765 LogError("The version of GCC-XML used for the creation of %s is too old; "
766 "the cvs_revision attribute of the root node reports '%s', "
767 "minimal required is 1.%d.", filename, version, MIN_REVISION);
768 return false;
769 }
770
771 wxToResolveTypeHashMap toResolveTypes;
772 wxClassMemberIdHashMap members;
773 wxTypeIdHashMap types;
774 wxTypeIdHashMap files;
775 wxTypeIdHashMap typedefs;
776
777 // prealloc quite a lot of memory!
778 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
779
780 // build a list of wx classes and in general of all existent types
781 child = doc.GetRoot()->GetChildren();
782 while (child)
783 {
784 const wxString& n = child->GetName();
785
786 unsigned long id = 0;
787 if (!getID(&id, child->GetAttribute("id")) || (id == 0 && n != "File")) {
788
789 // NOTE: <File> nodes can have an id == "f0"...
790
791 LogError("Invalid id for node %s: %s", n, child->GetAttribute("id"));
792 return false;
793 }
794
795 if (n == "Class")
796 {
797 wxString cname = child->GetAttribute("name");
798 if (cname.IsEmpty()) {
799 LogError("Invalid empty name for '%s' node", n);
800 return false;
801 }
802
803 // only register wx classes (do remember also the IDs of their members)
804 if (cname.StartsWith("wx"))
805 {
806 // NB: "file" attribute contains an ID value that we'll resolve later
807 m_classes.Add(wxClass(cname, child->GetAttribute("file")));
808
809 // the just-inserted class:
810 wxClass *newClass = &m_classes.Last();
811
812 // now get a list of the base classes:
813 wxXmlNode *baseNode = child->GetChildren();
814 while (baseNode)
815 {
816 // for now we store as "parents" only the parent IDs...
817 // later we will resolve them into full class names
818 if (baseNode->GetName() == "Base")
819 newClass->AddParent(baseNode->GetAttribute("type"));
820
821 baseNode = baseNode->GetNext();
822 }
823
824 const wxString& ids = child->GetAttribute("members");
825 if (ids.IsEmpty())
826 {
827 if (child->GetAttribute("incomplete") != "1") {
828 LogError("Invalid member IDs for '%s' class node: %s",
829 cname, child->GetAttribute("id"));
830 return false;
831 }
832 //else: don't warn the user; it looks like "incomplete" classes
833 // never have any member...
834 }
835 else
836 {
837 // decode the non-empty list of IDs:
838 if (!getMemberIDs(&members, newClass, ids)) {
839 LogError("Invalid member IDs for '%s' class node: %s",
840 cname, child->GetAttribute("id"));
841 return false;
842 }
843 }
844 }
845
846 // register this class also as possible return/argument type:
847 types[id] = cname;
848 }
849 else if (n == "Typedef")
850 {
851 unsigned long typeId = 0;
852 if (!getID(&typeId, child->GetAttribute("type"))) {
853 LogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
854 return false;
855 }
856
857 // this typedef node tell us that every type referenced with the
858 // "typeId" ID should be called with another name:
859 wxString name = child->GetAttribute("name");
860
861 // save this typedef in a separate hashmap...
862 typedefs[typeId] = name;
863
864 types[id] = name;
865 }
866 else if (n == "PointerType" || n == "ReferenceType" ||
867 n == "CvQualifiedType" || n == "ArrayType")
868 {
869 unsigned long type = 0;
870 if (!getID(&type, child->GetAttribute("type")) || type == 0) {
871 LogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
872 return false;
873 }
874
875 unsigned long attr = 0;
876 if (n == "PointerType")
877 attr = ATTRIB_POINTER;
878 else if (n == "ReferenceType")
879 attr = ATTRIB_REFERENCE;
880 else if (n == "CvQualifiedType" && child->GetAttribute("const") == "1")
881 attr = ATTRIB_CONST;
882 else if (n == "ArrayType")
883 attr = ATTRIB_ARRAY;
884
885 // these nodes make reference to other types... we'll resolve them later
886 toResolveTypes[id] = toResolveTypeItem(type, attr);
887 }
888 else if (n == "FunctionType" || n == "MethodType")
889 {
890 /*
891 TODO: parsing FunctionType and MethodType nodes is not as easy
892 as for other "simple" types.
893 */
894
895 wxString argstr;
896 wxXmlNode *arg = child->GetChildren();
897 while (arg)
898 {
899 if (arg->GetName() == "Argument")
900 argstr += arg->GetAttribute("type") + ", ";
901 arg = arg->GetNext();
902 }
903
904 if (argstr.Len() > 0)
905 argstr = argstr.Left(argstr.Len()-2); // remove final comma
906
907 // these nodes make reference to other types... we'll resolve them later
908 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
909 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
910
911 types[id] = "TOFIX"; // typically this type will be "fixed" thanks
912 // to a typedef later...
913 }
914 else if (n == "File")
915 {
916 if (!child->GetAttribute("id").StartsWith("f")) {
917 LogError("Unexpected file ID: %s", child->GetAttribute("id"));
918 return false;
919 }
920
921 // just ignore this node... all file IDs/names were already parsed
922 files[id] = child->GetAttribute("name");
923 }
924 else
925 {
926 // we register everything else as a possible return/argument type:
927 const wxString& name = child->GetAttribute("name");
928
929 if (!name.IsEmpty())
930 {
931 //typeIds.Add(id);
932 //typeNames.Add(name);
933 types[id] = name;
934 }
935 else
936 {
937 // this may happen with unnamed structs/union, special ctors,
938 // or other exotic things which we are not interested to, since
939 // they're never used as return/argument types by wxWidgets methods
940
941 if (g_verbose)
942 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
943 n, child->GetAttribute("id"));
944
945 types[id] = "TOFIX";
946 }
947 }
948
949 child = child->GetNext();
950
951 // give feedback to the user about the progress...
952 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
953 }
954
955 // some nodes with IDs referenced by methods as return/argument types, do reference
956 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
957 // thus we need to resolve their name iteratively:
958 while (toResolveTypes.size()>0)
959 {
960 if (g_verbose)
961 LogMessage("%d types were collected; %d types need yet to be resolved...",
962 types.size(), toResolveTypes.size());
963
964 for (wxToResolveTypeHashMap::iterator i = toResolveTypes.begin();
965 i != toResolveTypes.end();)
966 {
967 unsigned long id = i->first;
968 unsigned long referenced = i->second.ref;
969
970 wxTypeIdHashMap::iterator primary = types.find(referenced);
971 if (primary != types.end())
972 {
973 // this to-resolve-type references a "primary" type
974
975 wxString newtype = primary->second;
976 int attribs = i->second.attribs;
977
978 // attribs may contain a combination of ATTRIB_* flags:
979 if (attribs & ATTRIB_CONST)
980 newtype = "const " + newtype;
981 if (attribs & ATTRIB_REFERENCE)
982 newtype = newtype + "&";
983 if (attribs & ATTRIB_POINTER)
984 newtype = newtype + "*";
985 if (attribs & ATTRIB_ARRAY)
986 newtype = newtype + "[]";
987
988 // add the resolved type to the list of "primary" types
989 if (newtype.Contains("TOFIX") && typedefs[id] != "")
990 types[id] = typedefs[id]; // better use a typedef for this type!
991 else
992 types[id] = newtype;
993
994 // this one has been resolved; erase it through its iterator!
995 toResolveTypes.erase(i);
996
997 // now iterator i is invalid; assign it again to the beginning
998 i = toResolveTypes.begin();
999 }
1000 else
1001 {
1002 // then search in the referenced types themselves:
1003 wxToResolveTypeHashMap::iterator idx2 = toResolveTypes.find(referenced);
1004 if (idx2 != toResolveTypes.end())
1005 {
1006 // merge this to-resolve-type with the idx2->second type
1007 i->second.ref = idx2->second.ref;
1008 i->second.attribs |= idx2->second.attribs;
1009
1010 // this type will eventually be solved in the next while() iteration
1011 i++;
1012 }
1013 else
1014 {
1015 LogError("Cannot solve '%d' reference type!", referenced);
1016 return false;
1017 }
1018 }
1019 }
1020 }
1021
1022 // resolve header names
1023 for (unsigned int i=0; i<m_classes.GetCount(); i++)
1024 {
1025 unsigned long fileID = 0;
1026 if (!getID(&fileID, m_classes[i].GetHeader()) || fileID == 0) {
1027 LogError("invalid header id: %s", m_classes[i].GetHeader());
1028 return false;
1029 }
1030
1031 // search this file
1032 wxTypeIdHashMap::const_iterator idx = files.find(fileID);
1033 if (idx == files.end())
1034 {
1035 // this is an error!
1036 LogError("couldn't find file ID '%s'", m_classes[i].GetHeader());
1037 }
1038 else
1039 m_classes[i].SetHeader(idx->second);
1040 }
1041
1042 // resolve parent names
1043 for (unsigned int i=0; i<m_classes.GetCount(); i++)
1044 {
1045 for (unsigned int k=0; k<m_classes[i].GetParentCount(); k++)
1046 {
1047 unsigned long id;
1048
1049 if (!getID(&id, m_classes[i].GetParent(k))) {
1050 LogError("invalid parent class ID for '%s'", m_classes[i].GetName());
1051 return false;
1052 }
1053
1054 wxTypeIdHashMap::const_iterator idx = types.find(id);
1055 if (idx == types.end())
1056 {
1057 // this is an error!
1058 LogError("couldn't find parent class ID '%d'", id);
1059 }
1060 else
1061 // replace k-th parent with its true name:
1062 m_classes[i].SetParent(k, idx->second);
1063 }
1064 }
1065
1066 // build the list of the wx methods
1067 child = doc.GetRoot()->GetChildren();
1068 while (child)
1069 {
1070 wxString n = child->GetName(), acc = child->GetAttribute("access");
1071
1072 // only register public&protected methods
1073 if ((acc == "public" || acc == "protected") &&
1074 (n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod"))
1075 {
1076 unsigned long id = 0;
1077 if (!getID(&id, child->GetAttribute("id"))) {
1078 LogError("invalid ID for node '%s' with ID '%s'", n, child->GetAttribute("id"));
1079 return false;
1080 }
1081
1082 wxClassMemberIdHashMap::const_iterator it = members.find(id);
1083 if (it != members.end())
1084 {
1085 wxClass *p = it->second;
1086
1087 // this <Method> node is a method of the i-th class!
1088 wxMethod newfunc;
1089 if (!ParseMethod(child, types, newfunc)) {
1090 LogError("The method '%s' could not be added to class '%s'",
1091 child->GetAttribute("demangled"), p->GetName());
1092 return false;
1093 }
1094
1095 // do some additional check that we can do only here:
1096
1097 if (newfunc.IsCtor() && !p->IsValidCtorForThisClass(newfunc)) {
1098 LogError("The method '%s' does not seem to be a ctor for '%s'",
1099 newfunc.GetName(), p->GetName());
1100 return false;
1101 }
1102 if (newfunc.IsDtor() && !p->IsValidDtorForThisClass(newfunc)) {
1103 LogError("The method '%s' does not seem to be a dtor for '%s'",
1104 newfunc.GetName(), p->GetName());
1105 return false;
1106 }
1107
1108 p->AddMethod(newfunc);
1109 }
1110 }
1111
1112 child = child->GetNext();
1113
1114 // give feedback to the user about the progress...
1115 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
1116 }
1117
1118 if (!CheckParseResults())
1119 return false;
1120
1121 return true;
1122 }
1123
1124 bool wxXmlGccInterface::ParseMethod(const wxXmlNode *p,
1125 const wxTypeIdHashMap& types,
1126 wxMethod& m)
1127 {
1128 // get the real name
1129 wxString name = p->GetAttribute("name").Strip(wxString::both);
1130 if (p->GetName() == "Destructor")
1131 name = "~" + name;
1132 else if (p->GetName() == "OperatorMethod")
1133 name = "operator" + name;
1134
1135 // resolve return type
1136 wxType ret;
1137 unsigned long retid = 0;
1138 if (!getID(&retid, p->GetAttribute("returns")) || retid == 0)
1139 {
1140 if (p->GetName() != "Destructor" && p->GetName() != "Constructor") {
1141 LogError("Empty return ID for method '%s', with ID '%s'",
1142 name, p->GetAttribute("id"));
1143 return false;
1144 }
1145 }
1146 else
1147 {
1148 wxTypeIdHashMap::const_iterator retidx = types.find(retid);
1149 if (retidx == types.end()) {
1150 LogError("Could not find return type ID '%s'", retid);
1151 return false;
1152 }
1153
1154 ret = wxType(retidx->second);
1155 if (!ret.IsOk()) {
1156 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1157 retidx->second, name, p->GetAttribute("id"));
1158 return false;
1159 }
1160 }
1161
1162 // resolve argument types
1163 wxArgumentTypeArray argtypes;
1164 wxXmlNode *arg = p->GetChildren();
1165 while (arg)
1166 {
1167 if (arg->GetName() == "Argument")
1168 {
1169 unsigned long id = 0;
1170 if (!getID(&id, arg->GetAttribute("type")) || id == 0) {
1171 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1172 arg->GetAttribute("type"), name, p->GetAttribute("id"));
1173 return false;
1174 }
1175
1176 wxTypeIdHashMap::const_iterator idx = types.find(id);
1177 if (idx == types.end()) {
1178 LogError("Could not find argument type ID '%s'", id);
1179 return false;
1180 }
1181
1182 argtypes.Add(wxArgumentType(idx->second,
1183 arg->GetAttribute("default"),
1184 arg->GetAttribute("name")));
1185 }
1186
1187 arg = arg->GetNext();
1188 }
1189
1190 m.SetReturnType(ret);
1191 m.SetName(name);
1192 m.SetArgumentTypes(argtypes);
1193 m.SetConst(p->GetAttribute("const") == "1");
1194 m.SetStatic(p->GetAttribute("static") == "1");
1195
1196 // NOTE: gccxml is smart enough to mark as virtual those functions
1197 // which are declared virtual in base classes but don't have
1198 // the "virtual" keyword explicitely indicated in the derived
1199 // classes... so we don't need any further logic for virtuals
1200
1201 m.SetVirtual(p->GetAttribute("virtual") == "1");
1202 m.SetPureVirtual(p->GetAttribute("pure_virtual") == "1");
1203 m.SetDeprecated(p->GetAttribute("attributes") == "deprecated");
1204
1205 // decode access specifier
1206 if (p->GetAttribute("access") == "public")
1207 m.SetAccessSpecifier(wxMAS_PUBLIC);
1208 else if (p->GetAttribute("access") == "protected")
1209 m.SetAccessSpecifier(wxMAS_PROTECTED);
1210 else if (p->GetAttribute("access") == "private")
1211 m.SetAccessSpecifier(wxMAS_PRIVATE);
1212
1213 if (!m.IsOk()) {
1214 LogError("The prototype '%s' is not valid!", m.GetAsString());
1215 return false;
1216 }
1217
1218 return true;
1219 }
1220
1221
1222
1223 // ----------------------------------------------------------------------------
1224 // wxXmlDoxygenInterface global helpers
1225 // ----------------------------------------------------------------------------
1226
1227 static wxString GetTextFromChildren(const wxXmlNode *n)
1228 {
1229 wxString text;
1230
1231 // consider the tree
1232 //
1233 // <a><b>this</b> is a <b>string</b></a>
1234 //
1235 // <a>
1236 // |- <b>
1237 // | |- this
1238 // |- is a
1239 // |- <b>
1240 // |- string
1241 //
1242 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1243 // this function returns "this is a string"
1244
1245 wxXmlNode *ref = n->GetChildren();
1246 while (ref) {
1247 if (ref->GetType() == wxXML_ELEMENT_NODE)
1248 text += ref->GetNodeContent();
1249 else if (ref->GetType() == wxXML_TEXT_NODE)
1250 text += ref->GetContent();
1251 else
1252 LogWarning("Unexpected node type while getting text from '%s' node", n->GetName());
1253
1254 ref = ref->GetNext();
1255 }
1256
1257 return text;
1258 }
1259
1260 static bool HasTextNodeContaining(const wxXmlNode *parent, const wxString& name)
1261 {
1262 if (!parent)
1263 return false;
1264
1265 wxXmlNode *p = parent->GetChildren();
1266 while (p)
1267 {
1268 switch (p->GetType())
1269 {
1270 case wxXML_TEXT_NODE:
1271 if (p->GetContent() == name)
1272 return true;
1273 break;
1274
1275 case wxXML_ELEMENT_NODE:
1276 // recurse into this node...
1277 if (HasTextNodeContaining(p, name))
1278 return true;
1279 break;
1280
1281 default:
1282 // skip it
1283 break;
1284 }
1285
1286 p = p->GetNext();
1287 }
1288
1289 return false;
1290 }
1291
1292 static const wxXmlNode* FindNodeNamed(const wxXmlNode* parent, const wxString& name)
1293 {
1294 if (!parent)
1295 return NULL;
1296
1297 const wxXmlNode *p = parent->GetChildren();
1298 while (p)
1299 {
1300 if (p->GetName() == name)
1301 return p; // found!
1302
1303 // search recursively in the children of this node
1304 const wxXmlNode *ret = FindNodeNamed(p, name);
1305 if (ret)
1306 return ret;
1307
1308 p = p->GetNext();
1309 }
1310
1311 return NULL;
1312 }
1313
1314 int GetAvailabilityFor(const wxXmlNode *node)
1315 {
1316 // identify <onlyfor> custom XML tags
1317 const wxXmlNode* onlyfor = FindNodeNamed(node, "onlyfor");
1318 if (!onlyfor)
1319 return wxPORT_UNKNOWN;
1320
1321 wxArrayString ports = wxSplit(onlyfor->GetNodeContent(), ',');
1322 int nAvail = wxPORT_UNKNOWN;
1323 for (unsigned int i=0; i < ports.GetCount(); i++)
1324 {
1325 if (!ports[i].StartsWith("wx")) {
1326 LogError("unexpected port ID '%s'", ports[i]);
1327 return false;
1328 }
1329
1330 nAvail |= wxPlatformInfo::GetPortId(ports[i].Mid(2));
1331 }
1332
1333 return nAvail;
1334 }
1335
1336
1337 // ----------------------------------------------------------------------------
1338 // wxXmlDoxygenInterface
1339 // ----------------------------------------------------------------------------
1340
1341 bool wxXmlDoxygenInterface::Parse(const wxString& filename)
1342 {
1343 wxXmlDocument index;
1344 wxXmlNode *compound;
1345
1346 LogMessage("Parsing %s...", filename);
1347
1348 if (!index.Load(filename)) {
1349 LogError("can't load %s", filename);
1350 return false;
1351 }
1352
1353 // start processing the index:
1354 if (index.GetRoot()->GetName() != "doxygenindex") {
1355 LogError("invalid root node for %s", filename);
1356 return false;
1357 }
1358
1359 /*
1360 NB: we may need in future to do a version-check here if the
1361 format of the XML generated by doxygen changes.
1362 For now (doxygen version 1.5.5), this check is not required
1363 since AFAIK the XML format never changed since it was introduced.
1364 */
1365
1366 m_classes.Alloc(ESTIMATED_NUM_CLASSES);
1367
1368 // process files referenced by this index file
1369 compound = index.GetRoot()->GetChildren();
1370 while (compound)
1371 {
1372 if (compound->GetName() == "compound" &&
1373 compound->GetAttribute("kind") == "class")
1374 {
1375 wxString refid = compound->GetAttribute("refid");
1376
1377 wxFileName fn(filename);
1378 if (!ParseCompoundDefinition(fn.GetPath(wxPATH_GET_SEPARATOR) + refid + ".xml"))
1379 return false;
1380 }
1381
1382 compound = compound->GetNext();
1383 }
1384 //wxPrint("\n");
1385
1386 if (!CheckParseResults())
1387 return false;
1388
1389 return true;
1390 }
1391
1392 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
1393 {
1394 wxClassMemberIdHashMap parents;
1395 wxXmlDocument doc;
1396 wxXmlNode *child;
1397 int nodes = 0;
1398
1399 if (g_verbose)
1400 LogMessage("Parsing %s...", filename);
1401
1402 if (!doc.Load(filename)) {
1403 LogError("can't load %s", filename);
1404 return false;
1405 }
1406
1407 // start processing this compound definition XML
1408 if (doc.GetRoot()->GetName() != "doxygen") {
1409 LogError("invalid root node for %s", filename);
1410 return false;
1411 }
1412
1413 // build a list of wx classes
1414 child = doc.GetRoot()->GetChildren();
1415 while (child)
1416 {
1417 if (child->GetName() == "compounddef" &&
1418 child->GetAttribute("kind") == "class")
1419 {
1420 // parse this class
1421 wxClass klass;
1422 wxString absoluteFile, header;
1423
1424 wxXmlNode *subchild = child->GetChildren();
1425 while (subchild)
1426 {
1427 wxString kind = subchild->GetAttribute("kind");
1428
1429 // parse only public&protected functions:
1430 if (subchild->GetName() == "sectiondef" &&
1431 (kind == "public-func" || kind == "protected-func"))
1432 {
1433
1434 wxXmlNode *membernode = subchild->GetChildren();
1435 while (membernode)
1436 {
1437 if (membernode->GetName() == "memberdef" &&
1438 membernode->GetAttribute("kind") == "function")
1439 {
1440
1441 wxMethod m;
1442 if (!ParseMethod(membernode, m, header)) {
1443 LogError("The method '%s' could not be added to class '%s'",
1444 m.GetName(), klass.GetName());
1445 return false;
1446 }
1447
1448 if (kind == "public-func")
1449 m.SetAccessSpecifier(wxMAS_PUBLIC);
1450 else if (kind == "protected-func")
1451 m.SetAccessSpecifier(wxMAS_PROTECTED);
1452 else if (kind == "private-func")
1453 m.SetAccessSpecifier(wxMAS_PRIVATE);
1454
1455 if (absoluteFile.IsEmpty())
1456 absoluteFile = header;
1457 else if (header != absoluteFile)
1458 {
1459 LogError("The method '%s' is documented in a different "
1460 "file from others (which belong to '%s') ?",
1461 header, absoluteFile);
1462 return false;
1463 }
1464
1465 klass.AddMethod(m);
1466 }
1467
1468 membernode = membernode->GetNext();
1469 }
1470
1471 // all methods of this class were taken from the header "absoluteFile":
1472 klass.SetHeader(absoluteFile);
1473 }
1474 else if (subchild->GetName() == "compoundname")
1475 {
1476 klass.SetName(subchild->GetNodeContent());
1477 }
1478 /*else if (subchild->GetName() == "includes")
1479 {
1480 // NOTE: we'll get the header from the <location> tags
1481 // scattered inside <memberdef> tags instead of
1482 // this <includes> tag since it does not contain
1483 // the absolute path of the header
1484
1485 klass.SetHeader(subchild->GetNodeContent());
1486 }*/
1487 else if (subchild->GetName() == "detaileddescription")
1488 {
1489 // identify <onlyfor> custom XML tags
1490 klass.SetAvailability(GetAvailabilityFor(subchild));
1491 }
1492 else if (subchild->GetName() == "basecompoundref")
1493 {
1494 // add the name of this parent to the list of klass' parents
1495 klass.AddParent(subchild->GetNodeContent());
1496 }
1497
1498 subchild = subchild->GetNext();
1499 }
1500
1501 // add a new class
1502 if (klass.IsOk())
1503 m_classes.Add(klass);
1504 else if (g_verbose)
1505 LogWarning("discarding class '%s' with %d methods...",
1506 klass.GetName(), klass.GetMethodCount());
1507 }
1508
1509 child = child->GetNext();
1510
1511 // give feedback to the user about the progress...
1512 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
1513 }
1514
1515 return true;
1516 }
1517
1518 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode* p, wxMethod& m, wxString& header)
1519 {
1520 wxArgumentTypeArray args;
1521 long line;
1522
1523 wxXmlNode *child = p->GetChildren();
1524 while (child)
1525 {
1526 if (child->GetName() == "name")
1527 m.SetName(child->GetNodeContent());
1528 else if (child->GetName() == "type")
1529 m.SetReturnType(wxType(GetTextFromChildren(child)));
1530 else if (child->GetName() == "param")
1531 {
1532 wxString typestr, namestr, defstr, arrstr;
1533 wxXmlNode *n = child->GetChildren();
1534 while (n)
1535 {
1536 if (n->GetName() == "type")
1537 // if the <type> node has children, they should be all TEXT and <ref> nodes
1538 // and we need to take the text they contain, in the order they appear
1539 typestr = GetTextFromChildren(n);
1540 else if (n->GetName() == "declname")
1541 namestr = GetTextFromChildren(n);
1542 else if (n->GetName() == "defval")
1543 defstr = GetTextFromChildren(n).Strip(wxString::both);
1544 else if (n->GetName() == "array")
1545 arrstr = GetTextFromChildren(n);
1546
1547 n = n->GetNext();
1548 }
1549
1550 if (typestr.IsEmpty()) {
1551 LogError("cannot find type node for a param in method '%s'", m.GetName());
1552 return false;
1553 }
1554
1555 wxArgumentType newarg(typestr + arrstr, defstr, namestr);
1556
1557 // can we use preprocessor output to transform the default value
1558 // into the same form which gets processed by wxXmlGccInterface?
1559 wxStringHashMap::const_iterator it = m_preproc.find(defstr);
1560 if (it != m_preproc.end())
1561 newarg.SetDefaultValue(defstr, it->second);
1562
1563 args.Add(newarg);
1564 }
1565 else if (child->GetName() == "location")
1566 {
1567 line = -1;
1568 if (child->GetAttribute("line").ToLong(&line))
1569 m.SetLocation((int)line);
1570 header = child->GetAttribute("file");
1571 }
1572 else if (child->GetName() == "detaileddescription")
1573 {
1574 // when a method has a @deprecated tag inside its description,
1575 // Doxygen outputs somewhere nested inside <detaileddescription>
1576 // a <xreftitle>Deprecated</xreftitle> tag.
1577 m.SetDeprecated(HasTextNodeContaining(child, "Deprecated"));
1578
1579 // identify <onlyfor> custom XML tags
1580 m.SetAvailability(GetAvailabilityFor(child));
1581 }
1582
1583 child = child->GetNext();
1584 }
1585
1586 m.SetArgumentTypes(args);
1587 m.SetConst(p->GetAttribute("const")=="yes");
1588 m.SetStatic(p->GetAttribute("static")=="yes");
1589
1590 // NOTE: Doxygen is smart enough to mark as virtual those functions
1591 // which are declared virtual in base classes but don't have
1592 // the "virtual" keyword explicitely indicated in the derived
1593 // classes... so we don't need any further logic for virtuals
1594
1595 m.SetVirtual(p->GetAttribute("virt")=="virtual");
1596 m.SetPureVirtual(p->GetAttribute("virt")=="pure-virtual");
1597
1598 if (!m.IsOk()) {
1599 LogError("The prototype '%s' is not valid!", m.GetAsString());
1600 return false;
1601 }
1602
1603 return true;
1604 }