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