1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Parser of the API/interface XML files
4 // Author: Francesco Montorsi
7 // Copyright: (c) 2008 Francesco Montorsi
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
18 // for all others, include the necessary headers
23 #include "wx/xml/xml.h"
24 #include "wx/wfstream.h"
25 #include "wx/hashmap.h"
26 #include "wx/filename.h"
27 #include "xmlparser.h"
30 #include <wx/arrimpl.cpp>
31 WX_DEFINE_OBJARRAY(wxTypeArray
)
32 WX_DEFINE_OBJARRAY(wxArgumentTypeArray
)
33 WX_DEFINE_OBJARRAY(wxMethodArray
)
34 WX_DEFINE_OBJARRAY(wxClassArray
)
37 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
38 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
41 // defined in ifacecheck.cpp
42 extern bool g_verbose
;
45 bool g_bLogEnabled
= true;
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
55 void wxType::SetTypeFromString(const wxString
& t
)
58 TODO: optimize the following code writing a single function
59 which works at char-level and does everything in a single pass
62 // clean the type string
63 // ---------------------
67 // [] is the same as * for gccxml
68 m_strType
.Replace("[]", "*");
69 m_strType
.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
70 m_strType
.Replace("long unsigned int", "unsigned long");
72 // make sure the * and & operator always use the same spacing rules
73 // (to make sure GetAsString() output is always consistent)
74 m_strType
.Replace("*", "* ");
75 m_strType
.Replace("&", "& ");
76 m_strType
.Replace(" *", "*");
77 m_strType
.Replace(" &", "&");
79 while (m_strType
.Contains(" "))
80 m_strType
.Replace(" ", " "); // do it once again
82 m_strType
.Replace(" ,", ",");
85 m_strType
.Replace("_wxArraywxArrayStringBase", "const wxString&");
87 m_strType
= m_strType
.Strip(wxString::both
);
91 // clean the type string (this time for the comparison)
92 // ----------------------------------------------------
94 m_strTypeClean
= m_strType
; // begin with the already-cleaned string
95 m_strTypeClean
.Replace("const", "");
96 m_strTypeClean
.Replace("static", "");
97 m_strTypeClean
.Replace("*", "");
98 m_strTypeClean
.Replace("&", "");
99 m_strTypeClean
.Replace("[]", "");
100 m_strTypeClean
= m_strTypeClean
.Strip(wxString::both
);
102 // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
103 // need to be considered as the same type
104 if (m_strTypeClean
.EndsWith("Base"))
105 m_strTypeClean
= m_strTypeClean
.Left(m_strTypeClean
.Len()-4);
108 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
109 // fix this to avoid false positives
110 m_strTypeClean
.Replace("wxDateTime::", "");
111 m_strTypeClean
.Replace("wxStockGDI::", ""); // same story for some other classes
112 m_strTypeClean
.Replace("wxHelpEvent::", "");
113 m_strTypeClean
.Replace("wxWindowID", "int");
116 bool wxType::IsOk() const
118 // NB: m_strType can contain the :: operator; think to e.g. the
119 // "reverse_iterator_impl<wxString::const_iterator>" type
120 // It can also contain commas, * and & operators etc
122 return !m_strTypeClean
.IsEmpty();
125 bool wxType::operator==(const wxType
& m
) const
127 // brain-dead comparison:
129 if (m_strTypeClean
== m
.m_strTypeClean
&&
130 IsConst() == m
.IsConst() &&
131 IsStatic() == m
.IsStatic() &&
132 IsPointer() == m
.IsPointer() &&
133 IsReference() == m
.IsReference())
138 LogMessage("Type '%s' does not match type '%s'", m_strType
, m
.m_strType
);
139 LogMessage(" => TypeClean %s / %s; IsConst %d / %d; IsStatic %d / %d; IsPointer %d / %d; IsReference %d / %d",
140 m_strTypeClean
, m
.m_strTypeClean
, IsConst(), m
.IsConst(),
141 IsStatic(), m
.IsStatic(), IsPointer(), m
.IsPointer(),
142 IsReference(), m
.IsReference());
149 // ----------------------------------------------------------------------------
151 // ----------------------------------------------------------------------------
153 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
155 m_strDefaultValue
= defval
.Strip(wxString::both
);
156 m_strDefaultValueForCmp
= defvalForCmp
.IsEmpty() ?
157 m_strDefaultValue
: defvalForCmp
.Strip(wxString::both
);
160 // clean the default argument strings
161 // ----------------------------------
163 // Note: we adjust the aesthetic form of the m_strDefaultValue string for the "modify mode"
164 // of ifacecheck: we may need to write it out in an interface header
167 for (int i
=0; i
<2; i
++) // to avoid copying&pasting the code!
169 if (i
== 0) p
= &m_strDefaultValue
;
170 if (i
== 1) p
= &m_strDefaultValueForCmp
;
172 if (*p
== "0u") *p
= "0";
174 p
->Replace("0x000000001", "1");
175 p
->Replace("\\000\\000\\000", ""); // fix for unicode strings:
176 p
->Replace("\\011", "\\t");
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");
185 // clean ONLY the default argument string specific for comparison
186 // --------------------------------------------------------------
188 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
189 m_strDefaultValueForCmp
.EndsWith(")"))
191 // get rid of the wxT() part
192 unsigned int len
= m_strDefaultValueForCmp
.Len();
193 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
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");
205 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
206 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
209 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
211 if ((const wxType
&)(*this) != (const wxType
&)m
)
215 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
216 // numbers; avoid false positives in this case!
217 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
218 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
221 // fix for default values which were replaced by gcc-xml with their numeric values
222 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
223 if (m_strTypeClean
== "long" || m_strTypeClean
== "int")
225 if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) ||
226 (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx")))
229 LogMessage("Supposing '%s' default value to be the same of '%s'...",
230 m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
);
236 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
238 // maybe the default values are numbers.
239 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
240 // To handle these cases, we try to convert the default value strings to numbers:
241 long def1val
, def2val
;
242 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
243 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
245 if (def1val
== def2val
)
246 return true; // the default values match
250 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
251 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
255 // we deliberately avoid checks on the argument name
261 // ----------------------------------------------------------------------------
263 // ----------------------------------------------------------------------------
265 bool wxMethod::IsOk() const
267 // NOTE: m_retType can be a wxEmptyType, and means that this method
268 // is a ctor or a dtor.
269 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
270 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
274 if (m_strName
.IsEmpty())
277 // a function can't be both const and static or virtual and static!
278 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
279 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
283 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
285 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
286 if (!m_args
[i
].IsOk()) {
287 LogError("'%s' method has invalid %d-th argument type: %s",
288 m_strName
, i
+1, m_args
[i
].GetAsString());
292 // NB: the default value of the arguments can contain pretty much everything
293 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
294 // so we don't do any test on their contents
295 if (m_args
.GetCount()>0)
297 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
298 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
300 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
301 LogError("'%s' method has %d-th argument which has no default value "
302 "(while the previous one had one!)",
307 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
314 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
316 if (GetReturnType() != m
.GetReturnType() ||
317 GetName() != m
.GetName())
320 LogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m
.GetName());
324 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
326 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
327 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
331 // compare argument types
332 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
333 if (m_args
[i
] != m
.m_args
[i
])
339 bool wxMethod::ActsAsDefaultCtor() const
344 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
345 if (!m_args
[i
].HasDefaultValue())
351 bool wxMethod::operator==(const wxMethod
& m
) const
354 if (IsConst() != m
.IsConst() ||
355 IsStatic() != m
.IsStatic() ||
356 IsVirtual() != m
.IsVirtual() ||
357 IsPureVirtual() != m
.IsPureVirtual() ||
358 IsDeprecated() != m
.IsDeprecated() ||
359 GetAccessSpecifier() != m
.GetAccessSpecifier())
362 LogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m
.GetName());
367 // check everything else
368 return MatchesExceptForAttributes(m
);
371 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
372 bool bDeprecated
, bool bAccessSpec
) const
376 // NOTE: for return and argument types, never use wxType::GetAsCleanString
377 // since in that way we'd miss important decorators like &,*,const etc
379 if (m_retType
!=wxEmptyType
)
380 ret
+= m_retType
.GetAsString() + " ";
381 //else; this is a ctor or dtor
383 ret
+= m_strName
+ "(";
385 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
387 ret
+= m_args
[i
].GetAsString();
389 const wxString
& name
= m_args
[i
].GetArgumentName();
390 if (bWithArgumentNames
&& !name
.IsEmpty())
393 const wxString
& def
= bCleanDefaultValues
?
394 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
401 if (m_args
.GetCount()>0)
402 ret
= ret
.Left(ret
.Len()-2);
409 ret
= "static " + ret
;
410 if (m_bVirtual
|| m_bPureVirtual
)
411 ret
= "virtual " + ret
;
414 if (m_bDeprecated
&& bDeprecated
)
415 ret
+= " [deprecated]";
424 case wxMAS_PROTECTED
:
425 ret
+= " [protected]";
436 void wxMethod::Dump(wxTextOutputStream
& stream
) const
438 stream
<< "[" + m_retType
.GetAsString() + "]";
439 stream
<< "[" + m_strName
+ "]";
441 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
442 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
443 "=" + m_args
[i
].GetDefaultValue() + "]";
450 stream
<< " VIRTUAL";
452 stream
<< " PURE-VIRTUAL";
454 stream
<< " DEPRECATED";
459 // ----------------------------------------------------------------------------
461 // ----------------------------------------------------------------------------
463 wxString
wxClass::GetNameWithoutTemplate() const
465 // NB: I'm not sure this is the right terminology for this function!
467 if (m_strName
.Contains("<"))
468 return m_strName
.Left(m_strName
.Find("<"));
472 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
474 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
475 // named wxWritableCharTypeBuffer, without the <...> part!
477 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
483 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
485 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
491 void wxClass::Dump(wxTextOutputStream
& out
) const
493 out
<< m_strName
+ "\n";
495 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
497 // dump all our methods
499 m_methods
[i
].Dump(out
);
506 bool wxClass::CheckConsistency() const
508 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
509 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
510 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
512 LogError("class %s has two methods with the same prototype: '%s'",
513 m_strName
, m_methods
[i
].GetAsString());
517 //((wxClass*)this)->m_methods.RemoveAt(j);
524 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
526 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
527 if (m_methods
[i
] == m
)
528 return &m_methods
[i
];
532 const wxMethod
* wxClass::RecursiveUpwardFindMethod(const wxMethod
& m
,
533 const wxXmlInterface
* allclasses
) const
535 // first, search into *this
536 const wxMethod
* ret
= FindMethod(m
);
540 // then, search into its parents
541 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
543 // ignore non-wx-classes parents
544 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
545 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
547 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
549 LogError("Could not find parent '%s' of class '%s'...",
550 m_parents
[i
], GetName());
554 const wxMethod
*parentMethod
= parent
->RecursiveUpwardFindMethod(m
, allclasses
);
560 // could not find anything even in parent classes...
564 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
566 wxMethodPtrArray ret
;
568 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
569 if (m_methods
[i
].GetName() == name
)
570 ret
.Add(&m_methods
[i
]);
576 wxMethodPtrArray
wxClass::RecursiveUpwardFindMethodsNamed(const wxString
& name
,
577 const wxXmlInterface
* allclasses
) const
579 // first, search into *this
580 wxMethodPtrArray ret
= FindMethodsNamed(name
);
581 if (ret
.GetCount()>0)
582 return ret
; // stop here, don't look upward in the parents
584 // then, search into parents of this class
585 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
587 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
588 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
590 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
592 LogError("Could not find parent '%s' of class '%s'...",
593 m_parents
[i
], GetName());
597 wxMethodPtrArray temp
= parent
->RecursiveUpwardFindMethodsNamed(name
, allclasses
);
598 WX_APPEND_ARRAY(ret
, temp
);
607 // ----------------------------------------------------------------------------
609 // ----------------------------------------------------------------------------
611 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
613 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
615 // sort alphabetically
616 return item1
->GetName().Cmp(item2
->GetName());
619 void wxXmlInterface::Dump(const wxString
& filename
)
621 wxFFileOutputStream
apioutput( filename
);
622 wxTextOutputStream
apiout( apioutput
);
624 // dump the classes in alphabetical order
625 wxSortedClassArray
sorted(CompareWxClassObjects
);
626 sorted
.Alloc(m_classes
.GetCount());
627 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
628 sorted
.Add(&m_classes
[i
]);
630 // now they have been sorted
631 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
632 sorted
[i
]->Dump(apiout
);
635 bool wxXmlInterface::CheckParseResults() const
637 // this check can be quite slow, so do it only for debug releases:
639 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
640 if (!m_classes
[i
].CheckConsistency())
647 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
651 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
652 if (m_classes
[i
].GetHeader() == headerfile
)
653 ret
.Add(&m_classes
[i
]);
659 // ----------------------------------------------------------------------------
660 // wxXmlGccInterface helper declarations
661 // ----------------------------------------------------------------------------
663 // or-able flags for a toResolveTypeItem->attrib:
664 #define ATTRIB_CONST 1
665 #define ATTRIB_REFERENCE 2
666 #define ATTRIB_POINTER 4
667 #define ATTRIB_ARRAY 8
669 // it may sound strange but gccxml, in order to produce shorter ID names
670 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
671 // in order to be able to translate such strings into numbers using strtoul()
672 // we use as base 10 (possible digits) + 25 (possible characters) = 35
673 #define GCCXML_BASE 35
675 class toResolveTypeItem
678 toResolveTypeItem() { attribs
=0; }
679 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
680 : ref(refID
), attribs(attribint
) {}
682 unsigned long ref
, // the referenced type's ID
683 attribs
; // the attributes of this reference
688 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
689 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
690 wxIntegerHash
, wxIntegerEqual
,
691 wxToResolveTypeHashMap
);
693 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
694 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
695 wxIntegerHash
, wxIntegerEqual
,
696 wxClassMemberIdHashMap
);
700 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
704 // utility to parse gccXML ID values;
705 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
706 // but is a little bit faster
707 bool getID(unsigned long *id
, const wxString
& str
)
709 const wxStringCharType
* const start
= str
.wx_str()+1;
710 wxStringCharType
*end
;
711 #if wxUSE_UNICODE_WCHAR
712 unsigned long val
= wcstoul(start
, &end
, GCCXML_BASE
);
714 unsigned long val
= strtoul(start
, &end
, GCCXML_BASE
);
717 // return true only if scan was stopped by the terminating NUL and
718 // if the string was not empty to start with and no under/overflow
720 if ( *end
!= '\0' || end
== start
|| errno
== ERANGE
|| errno
== EINVAL
)
727 // utility specialized to parse efficiently the gccXML list of IDs which occur
728 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
729 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxString
& str
)
731 const wxStringCharType
* const start
= str
.wx_str();
732 #if wxUSE_UNICODE_WCHAR
733 size_t len
= wcslen(start
);
735 size_t len
= strlen(start
);
738 if (len
== 0 || start
[0] != '_')
741 const wxStringCharType
*curpos
= start
,
743 wxStringCharType
*nexttoken
;
747 // curpos always points to the underscore of the next token to parse:
748 #if wxUSE_UNICODE_WCHAR
749 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
751 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
753 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
756 // advance current position
757 curpos
= nexttoken
+ 1;
759 // add this ID to the hashmap
760 wxClassMemberIdHashMap::value_type
v(id
, p
);
768 // ----------------------------------------------------------------------------
770 // ----------------------------------------------------------------------------
772 bool wxXmlGccInterface::Parse(const wxString
& filename
)
778 LogMessage("Parsing %s...", filename
);
780 if (!doc
.Load(filename
)) {
781 LogError("can't load %s", filename
);
785 // start processing the XML file
786 if (doc
.GetRoot()->GetName() != "GCC_XML") {
787 LogError("invalid root node for %s", filename
);
791 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
794 #define MIN_REVISION 120
796 if (!version
.StartsWith("1."))
800 unsigned long rev
= 0;
801 if (!version
.Mid(2).ToULong(&rev
))
804 if (rev
< MIN_REVISION
)
810 LogError("The version of GCC-XML used for the creation of %s is too old; "
811 "the cvs_revision attribute of the root node reports '%s', "
812 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
816 wxToResolveTypeHashMap toResolveTypes
;
817 wxClassMemberIdHashMap members
;
818 wxTypeIdHashMap types
;
819 wxTypeIdHashMap files
;
820 wxTypeIdHashMap typedefs
;
822 // prealloc quite a lot of memory!
823 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
825 // build a list of wx classes and in general of all existent types
826 child
= doc
.GetRoot()->GetChildren();
829 const wxString
& n
= child
->GetName();
831 unsigned long id
= 0;
832 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
834 // NOTE: <File> nodes can have an id == "f0"...
836 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
842 wxString cname
= child
->GetAttribute("name");
843 if (cname
.IsEmpty()) {
844 LogError("Invalid empty name for '%s' node", n
);
848 // only register wx classes (do remember also the IDs of their members)
849 if (cname
.StartsWith("wx"))
851 // NB: "file" attribute contains an ID value that we'll resolve later
852 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
854 // the just-inserted class:
855 wxClass
*newClass
= &m_classes
.Last();
857 // now get a list of the base classes:
858 wxXmlNode
*baseNode
= child
->GetChildren();
861 // for now we store as "parents" only the parent IDs...
862 // later we will resolve them into full class names
863 if (baseNode
->GetName() == "Base")
864 newClass
->AddParent(baseNode
->GetAttribute("type"));
866 baseNode
= baseNode
->GetNext();
869 const wxString
& ids
= child
->GetAttribute("members");
872 if (child
->GetAttribute("incomplete") != "1") {
873 LogError("Invalid member IDs for '%s' class node: %s",
874 cname
, child
->GetAttribute("id"));
877 //else: don't warn the user; it looks like "incomplete" classes
878 // never have any member...
882 // decode the non-empty list of IDs:
883 if (!getMemberIDs(&members
, newClass
, ids
)) {
884 LogError("Invalid member IDs for '%s' class node: %s",
885 cname
, child
->GetAttribute("id"));
891 // register this class also as possible return/argument type:
894 else if (n
== "Typedef")
896 unsigned long typeId
= 0;
897 if (!getID(&typeId
, child
->GetAttribute("type"))) {
898 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
902 // this typedef node tell us that every type referenced with the
903 // "typeId" ID should be called with another name:
904 wxString name
= child
->GetAttribute("name");
906 // save this typedef in a separate hashmap...
907 typedefs
[typeId
] = name
;
911 else if (n
== "PointerType" || n
== "ReferenceType" ||
912 n
== "CvQualifiedType" || n
== "ArrayType")
914 unsigned long type
= 0;
915 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
916 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
920 unsigned long attr
= 0;
921 if (n
== "PointerType")
922 attr
= ATTRIB_POINTER
;
923 else if (n
== "ReferenceType")
924 attr
= ATTRIB_REFERENCE
;
925 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
927 else if (n
== "ArrayType")
930 // these nodes make reference to other types... we'll resolve them later
931 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
933 else if (n
== "FunctionType" || n
== "MethodType")
936 TODO: parsing FunctionType and MethodType nodes is not as easy
937 as for other "simple" types.
941 wxXmlNode
*arg
= child
->GetChildren();
944 if (arg
->GetName() == "Argument")
945 argstr
+= arg
->GetAttribute("type") + ", ";
946 arg
= arg
->GetNext();
949 if (argstr
.Len() > 0)
950 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
952 // these nodes make reference to other types... we'll resolve them later
953 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
954 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
956 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
957 // to a typedef later...
959 else if (n
== "File")
961 if (!child
->GetAttribute("id").StartsWith("f")) {
962 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
966 // just ignore this node... all file IDs/names were already parsed
967 files
[id
] = child
->GetAttribute("name");
971 // we register everything else as a possible return/argument type:
972 const wxString
& name
= child
->GetAttribute("name");
977 //typeNames.Add(name);
982 // this may happen with unnamed structs/union, special ctors,
983 // or other exotic things which we are not interested to, since
984 // they're never used as return/argument types by wxWidgets methods
987 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
988 n
, child
->GetAttribute("id"));
994 child
= child
->GetNext();
996 // give feedback to the user about the progress...
997 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1000 // some nodes with IDs referenced by methods as return/argument types, do reference
1001 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
1002 // thus we need to resolve their name iteratively:
1003 while (toResolveTypes
.size()>0)
1006 LogMessage("%d types were collected; %d types need yet to be resolved...",
1007 types
.size(), toResolveTypes
.size());
1009 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
1010 i
!= toResolveTypes
.end();)
1012 unsigned long id
= i
->first
;
1013 unsigned long referenced
= i
->second
.ref
;
1015 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
1016 if (primary
!= types
.end())
1018 // this to-resolve-type references a "primary" type
1020 wxString newtype
= primary
->second
;
1021 int attribs
= i
->second
.attribs
;
1023 // attribs may contain a combination of ATTRIB_* flags:
1024 if (attribs
& ATTRIB_CONST
)
1025 newtype
= "const " + newtype
;
1026 if (attribs
& ATTRIB_REFERENCE
)
1027 newtype
= newtype
+ "&";
1028 if (attribs
& ATTRIB_POINTER
)
1029 newtype
= newtype
+ "*";
1030 if (attribs
& ATTRIB_ARRAY
)
1031 newtype
= newtype
+ "[]";
1033 // add the resolved type to the list of "primary" types
1034 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
1035 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
1037 types
[id
] = newtype
;
1039 // this one has been resolved; erase it through its iterator!
1040 toResolveTypes
.erase(i
);
1042 // now iterator i is invalid; assign it again to the beginning
1043 i
= toResolveTypes
.begin();
1047 // then search in the referenced types themselves:
1048 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
1049 if (idx2
!= toResolveTypes
.end())
1051 // merge this to-resolve-type with the idx2->second type
1052 i
->second
.ref
= idx2
->second
.ref
;
1053 i
->second
.attribs
|= idx2
->second
.attribs
;
1055 // this type will eventually be solved in the next while() iteration
1060 LogError("Cannot solve '%d' reference type!", referenced
);
1067 // resolve header names
1068 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1070 unsigned long fileID
= 0;
1071 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
1072 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
1077 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
1078 if (idx
== files
.end())
1080 // this is an error!
1081 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
1084 m_classes
[i
].SetHeader(idx
->second
);
1087 // resolve parent names
1088 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1090 for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++)
1094 if (!getID(&id
, m_classes
[i
].GetParent(k
))) {
1095 LogError("invalid parent class ID for '%s'", m_classes
[i
].GetName());
1099 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1100 if (idx
== types
.end())
1102 // this is an error!
1103 LogError("couldn't find parent class ID '%d'", id
);
1106 // replace k-th parent with its true name:
1107 m_classes
[i
].SetParent(k
, idx
->second
);
1111 // build the list of the wx methods
1112 child
= doc
.GetRoot()->GetChildren();
1115 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
1117 // only register public&protected methods
1118 if ((acc
== "public" || acc
== "protected") &&
1119 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
1121 unsigned long id
= 0;
1122 if (!getID(&id
, child
->GetAttribute("id"))) {
1123 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
1127 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
1128 if (it
!= members
.end())
1130 wxClass
*p
= it
->second
;
1132 // this <Method> node is a method of the i-th class!
1134 if (!ParseMethod(child
, types
, newfunc
)) {
1135 LogError("The method '%s' could not be added to class '%s'",
1136 child
->GetAttribute("demangled"), p
->GetName());
1140 // do some additional check that we can do only here:
1142 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
1143 LogError("The method '%s' does not seem to be a ctor for '%s'",
1144 newfunc
.GetName(), p
->GetName());
1147 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
1148 LogError("The method '%s' does not seem to be a dtor for '%s'",
1149 newfunc
.GetName(), p
->GetName());
1153 p
->AddMethod(newfunc
);
1157 child
= child
->GetNext();
1159 // give feedback to the user about the progress...
1160 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1163 if (!CheckParseResults())
1169 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1170 const wxTypeIdHashMap
& types
,
1173 // get the real name
1174 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1175 if (p
->GetName() == "Destructor")
1177 else if (p
->GetName() == "OperatorMethod")
1178 name
= "operator" + name
;
1180 // resolve return type
1182 unsigned long retid
= 0;
1183 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1185 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1186 LogError("Empty return ID for method '%s', with ID '%s'",
1187 name
, p
->GetAttribute("id"));
1193 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1194 if (retidx
== types
.end()) {
1195 LogError("Could not find return type ID '%s'", retid
);
1199 ret
= wxType(retidx
->second
);
1201 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1202 retidx
->second
, name
, p
->GetAttribute("id"));
1207 // resolve argument types
1208 wxArgumentTypeArray argtypes
;
1209 wxXmlNode
*arg
= p
->GetChildren();
1212 if (arg
->GetName() == "Argument")
1214 unsigned long id
= 0;
1215 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1216 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1217 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1221 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1222 if (idx
== types
.end()) {
1223 LogError("Could not find argument type ID '%s'", id
);
1227 argtypes
.Add(wxArgumentType(idx
->second
,
1228 arg
->GetAttribute("default"),
1229 arg
->GetAttribute("name")));
1232 arg
= arg
->GetNext();
1235 m
.SetReturnType(ret
);
1237 m
.SetArgumentTypes(argtypes
);
1238 m
.SetConst(p
->GetAttribute("const") == "1");
1239 m
.SetStatic(p
->GetAttribute("static") == "1");
1241 // NOTE: gccxml is smart enough to mark as virtual those functions
1242 // which are declared virtual in base classes but don't have
1243 // the "virtual" keyword explicitely indicated in the derived
1244 // classes... so we don't need any further logic for virtuals
1246 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1247 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1248 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1250 // decode access specifier
1251 if (p
->GetAttribute("access") == "public")
1252 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1253 else if (p
->GetAttribute("access") == "protected")
1254 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1255 else if (p
->GetAttribute("access") == "private")
1256 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1259 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1268 // ----------------------------------------------------------------------------
1269 // wxXmlDoxygenInterface global helpers
1270 // ----------------------------------------------------------------------------
1272 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1276 // consider the tree
1278 // <a><b>this</b> is a <b>string</b></a>
1287 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1288 // this function returns "this is a string"
1290 wxXmlNode
*ref
= n
->GetChildren();
1292 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1293 text
+= ref
->GetNodeContent();
1294 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1295 text
+= ref
->GetContent();
1297 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1299 ref
= ref
->GetNext();
1305 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1310 wxXmlNode
*p
= parent
->GetChildren();
1313 switch (p
->GetType())
1315 case wxXML_TEXT_NODE
:
1316 if (p
->GetContent() == name
)
1320 case wxXML_ELEMENT_NODE
:
1321 // recurse into this node...
1322 if (HasTextNodeContaining(p
, name
))
1337 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1342 const wxXmlNode
*p
= parent
->GetChildren();
1345 if (p
->GetName() == name
)
1348 // search recursively in the children of this node
1349 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1359 int GetAvailabilityFor(const wxXmlNode
*node
)
1361 // identify <onlyfor> custom XML tags
1362 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1364 return wxPORT_UNKNOWN
;
1366 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1367 int nAvail
= wxPORT_UNKNOWN
;
1368 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1370 if (!ports
[i
].StartsWith("wx")) {
1371 LogError("unexpected port ID '%s'", ports
[i
]);
1375 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1382 // ----------------------------------------------------------------------------
1383 // wxXmlDoxygenInterface
1384 // ----------------------------------------------------------------------------
1386 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1388 wxXmlDocument index
;
1389 wxXmlNode
*compound
;
1391 LogMessage("Parsing %s...", filename
);
1393 if (!index
.Load(filename
)) {
1394 LogError("can't load %s", filename
);
1398 // start processing the index:
1399 if (index
.GetRoot()->GetName() != "doxygenindex") {
1400 LogError("invalid root node for %s", filename
);
1405 NB: we may need in future to do a version-check here if the
1406 format of the XML generated by doxygen changes.
1407 For now (doxygen version 1.5.5), this check is not required
1408 since AFAIK the XML format never changed since it was introduced.
1411 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1413 // process files referenced by this index file
1414 compound
= index
.GetRoot()->GetChildren();
1417 if (compound
->GetName() == "compound" &&
1418 compound
->GetAttribute("kind") == "class")
1420 wxString refid
= compound
->GetAttribute("refid");
1422 wxFileName
fn(filename
);
1423 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1427 compound
= compound
->GetNext();
1431 if (!CheckParseResults())
1437 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1439 wxClassMemberIdHashMap parents
;
1445 LogMessage("Parsing %s...", filename
);
1447 if (!doc
.Load(filename
)) {
1448 LogError("can't load %s", filename
);
1452 // start processing this compound definition XML
1453 if (doc
.GetRoot()->GetName() != "doxygen") {
1454 LogError("invalid root node for %s", filename
);
1458 // build a list of wx classes
1459 child
= doc
.GetRoot()->GetChildren();
1462 if (child
->GetName() == "compounddef" &&
1463 child
->GetAttribute("kind") == "class")
1467 wxString absoluteFile
, header
;
1469 wxXmlNode
*subchild
= child
->GetChildren();
1472 wxString kind
= subchild
->GetAttribute("kind");
1474 // parse only public&protected functions:
1475 if (subchild
->GetName() == "sectiondef" &&
1476 (kind
== "public-func" || kind
== "protected-func"))
1479 wxXmlNode
*membernode
= subchild
->GetChildren();
1482 if (membernode
->GetName() == "memberdef" &&
1483 membernode
->GetAttribute("kind") == "function")
1487 if (!ParseMethod(membernode
, m
, header
)) {
1488 LogError("The method '%s' could not be added to class '%s'",
1489 m
.GetName(), klass
.GetName());
1493 if (kind
== "public-func")
1494 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1495 else if (kind
== "protected-func")
1496 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1497 else if (kind
== "private-func")
1498 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1500 if (absoluteFile
.IsEmpty())
1501 absoluteFile
= header
;
1502 else if (header
!= absoluteFile
)
1504 LogError("The method '%s' is documented in a different "
1505 "file from others (which belong to '%s') ?",
1506 header
, absoluteFile
);
1513 membernode
= membernode
->GetNext();
1516 // all methods of this class were taken from the header "absoluteFile":
1517 klass
.SetHeader(absoluteFile
);
1519 else if (subchild
->GetName() == "compoundname")
1521 klass
.SetName(subchild
->GetNodeContent());
1523 /*else if (subchild->GetName() == "includes")
1525 // NOTE: we'll get the header from the <location> tags
1526 // scattered inside <memberdef> tags instead of
1527 // this <includes> tag since it does not contain
1528 // the absolute path of the header
1530 klass.SetHeader(subchild->GetNodeContent());
1532 else if (subchild
->GetName() == "detaileddescription")
1534 // identify <onlyfor> custom XML tags
1535 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1537 else if (subchild
->GetName() == "basecompoundref")
1539 // add the name of this parent to the list of klass' parents
1540 klass
.AddParent(subchild
->GetNodeContent());
1543 subchild
= subchild
->GetNext();
1548 m_classes
.Add(klass
);
1550 LogWarning("discarding class '%s' with %d methods...",
1551 klass
.GetName(), klass
.GetMethodCount());
1554 child
= child
->GetNext();
1556 // give feedback to the user about the progress...
1557 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1563 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1565 wxArgumentTypeArray args
;
1568 wxXmlNode
*child
= p
->GetChildren();
1571 if (child
->GetName() == "name")
1572 m
.SetName(child
->GetNodeContent());
1573 else if (child
->GetName() == "type")
1574 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1575 else if (child
->GetName() == "param")
1577 wxString typestr
, namestr
, defstr
, arrstr
;
1578 wxXmlNode
*n
= child
->GetChildren();
1581 if (n
->GetName() == "type")
1582 // if the <type> node has children, they should be all TEXT and <ref> nodes
1583 // and we need to take the text they contain, in the order they appear
1584 typestr
= GetTextFromChildren(n
);
1585 else if (n
->GetName() == "declname")
1586 namestr
= GetTextFromChildren(n
);
1587 else if (n
->GetName() == "defval")
1588 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1589 else if (n
->GetName() == "array")
1590 arrstr
= GetTextFromChildren(n
);
1595 if (typestr
.IsEmpty()) {
1596 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1600 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1602 // can we use preprocessor output to transform the default value
1603 // into the same form which gets processed by wxXmlGccInterface?
1604 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1605 if (it
!= m_preproc
.end())
1606 newarg
.SetDefaultValue(defstr
, it
->second
);
1610 else if (child
->GetName() == "location")
1613 if (child
->GetAttribute("line").ToLong(&line
))
1614 m
.SetLocation((int)line
);
1615 header
= child
->GetAttribute("file");
1617 else if (child
->GetName() == "detaileddescription")
1619 // when a method has a @deprecated tag inside its description,
1620 // Doxygen outputs somewhere nested inside <detaileddescription>
1621 // a <xreftitle>Deprecated</xreftitle> tag.
1622 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1624 // identify <onlyfor> custom XML tags
1625 m
.SetAvailability(GetAvailabilityFor(child
));
1628 child
= child
->GetNext();
1631 m
.SetArgumentTypes(args
);
1632 m
.SetConst(p
->GetAttribute("const")=="yes");
1633 m
.SetStatic(p
->GetAttribute("static")=="yes");
1635 // NOTE: Doxygen is smart enough to mark as virtual those functions
1636 // which are declared virtual in base classes but don't have
1637 // the "virtual" keyword explicitely indicated in the derived
1638 // classes... so we don't need any further logic for virtuals
1640 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1641 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1644 LogError("The prototype '%s' is not valid!", m
.GetAsString());