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:
177 // ADHOC-FIX: for wxConv* default values
178 p
->Replace("wxConvAuto(wxFONTENCODING_DEFAULT)", "wxConvAuto()");
179 p
->Replace("wxGet_wxConvUTF8()", "wxConvUTF8");
180 p
->Replace("wxGet_wxConvLocal()", "wxConvLocal");
184 // clean ONLY the default argument string specific for comparison
185 // --------------------------------------------------------------
187 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
188 m_strDefaultValueForCmp
.EndsWith(")"))
190 // get rid of the wxT() part
191 unsigned int len
= m_strDefaultValueForCmp
.Len();
192 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
196 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
197 // fix this to avoid false positives
198 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
199 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
200 m_strDefaultValueForCmp
.Replace("wxHelpEvent::", ""); // same story for some other classes
201 m_strDefaultValueForCmp
.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
204 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
205 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
208 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
210 if ((const wxType
&)(*this) != (const wxType
&)m
)
214 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
215 // numbers; avoid false positives in this case!
216 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
217 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
220 // fix for default values which were replaced by gcc-xml with their numeric values
221 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
222 if (m_strTypeClean
== "long" || m_strTypeClean
== "int")
224 if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) ||
225 (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx")))
228 LogMessage("Supposing '%s' default value to be the same of '%s'...",
229 m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
);
235 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
237 // maybe the default values are numbers.
238 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
239 // To handle these cases, we try to convert the default value strings to numbers:
240 long def1val
, def2val
;
241 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
242 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
244 if (def1val
== def2val
)
245 return true; // the default values match
249 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
250 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
254 // we deliberately avoid checks on the argument name
260 // ----------------------------------------------------------------------------
262 // ----------------------------------------------------------------------------
264 bool wxMethod::IsOk() const
266 // NOTE: m_retType can be a wxEmptyType, and means that this method
267 // is a ctor or a dtor.
268 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
269 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
273 if (m_strName
.IsEmpty())
276 // a function can't be both const and static or virtual and static!
277 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
278 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
282 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
284 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
285 if (!m_args
[i
].IsOk()) {
286 LogError("'%s' method has invalid %d-th argument type: %s",
287 m_strName
, i
+1, m_args
[i
].GetAsString());
291 // NB: the default value of the arguments can contain pretty much everything
292 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
293 // so we don't do any test on their contents
294 if (m_args
.GetCount()>0)
296 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
297 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
299 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
300 LogError("'%s' method has %d-th argument which has no default value "
301 "(while the previous one had one!)",
306 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
313 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
315 if (GetReturnType() != m
.GetReturnType() ||
316 GetName() != m
.GetName())
319 LogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m
.GetName());
323 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
325 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
326 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
330 // compare argument types
331 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
332 if (m_args
[i
] != m
.m_args
[i
])
338 bool wxMethod::ActsAsDefaultCtor() const
343 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
344 if (!m_args
[i
].HasDefaultValue())
350 bool wxMethod::operator==(const wxMethod
& m
) const
353 if (IsConst() != m
.IsConst() ||
354 IsStatic() != m
.IsStatic() ||
355 IsVirtual() != m
.IsVirtual() ||
356 IsPureVirtual() != m
.IsPureVirtual() ||
357 IsDeprecated() != m
.IsDeprecated() ||
358 GetAccessSpecifier() != m
.GetAccessSpecifier())
361 LogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m
.GetName());
366 // check everything else
367 return MatchesExceptForAttributes(m
);
370 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
371 bool bDeprecated
, bool bAccessSpec
) const
375 // NOTE: for return and argument types, never use wxType::GetAsCleanString
376 // since in that way we'd miss important decorators like &,*,const etc
378 if (m_retType
!=wxEmptyType
)
379 ret
+= m_retType
.GetAsString() + " ";
380 //else; this is a ctor or dtor
382 ret
+= m_strName
+ "(";
384 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
386 ret
+= m_args
[i
].GetAsString();
388 const wxString
& name
= m_args
[i
].GetArgumentName();
389 if (bWithArgumentNames
&& !name
.IsEmpty())
392 const wxString
& def
= bCleanDefaultValues
?
393 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
400 if (m_args
.GetCount()>0)
401 ret
= ret
.Left(ret
.Len()-2);
408 ret
= "static " + ret
;
409 if (m_bVirtual
|| m_bPureVirtual
)
410 ret
= "virtual " + ret
;
413 if (m_bDeprecated
&& bDeprecated
)
414 ret
+= " [deprecated]";
423 case wxMAS_PROTECTED
:
424 ret
+= " [protected]";
435 void wxMethod::Dump(wxTextOutputStream
& stream
) const
437 stream
<< "[" + m_retType
.GetAsString() + "]";
438 stream
<< "[" + m_strName
+ "]";
440 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
441 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
442 "=" + m_args
[i
].GetDefaultValue() + "]";
449 stream
<< " VIRTUAL";
451 stream
<< " PURE-VIRTUAL";
453 stream
<< " DEPRECATED";
458 // ----------------------------------------------------------------------------
460 // ----------------------------------------------------------------------------
462 wxString
wxClass::GetNameWithoutTemplate() const
464 // NB: I'm not sure this is the right terminology for this function!
466 if (m_strName
.Contains("<"))
467 return m_strName
.Left(m_strName
.Find("<"));
471 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
473 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
474 // named wxWritableCharTypeBuffer, without the <...> part!
476 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
482 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
484 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
490 void wxClass::Dump(wxTextOutputStream
& out
) const
492 out
<< m_strName
+ "\n";
494 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
496 // dump all our methods
498 m_methods
[i
].Dump(out
);
505 bool wxClass::CheckConsistency() const
507 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
508 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
509 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
511 LogError("class %s has two methods with the same prototype: '%s'",
512 m_strName
, m_methods
[i
].GetAsString());
516 //((wxClass*)this)->m_methods.RemoveAt(j);
523 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
525 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
526 if (m_methods
[i
] == m
)
527 return &m_methods
[i
];
531 const wxMethod
* wxClass::RecursiveUpwardFindMethod(const wxMethod
& m
,
532 const wxXmlInterface
* allclasses
) const
534 // first, search into *this
535 const wxMethod
* ret
= FindMethod(m
);
539 // then, search into its parents
540 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
542 // ignore non-wx-classes parents
543 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
544 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
546 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
548 LogError("Could not find parent '%s' of class '%s'...",
549 m_parents
[i
], GetName());
553 const wxMethod
*parentMethod
= parent
->RecursiveUpwardFindMethod(m
, allclasses
);
559 // could not find anything even in parent classes...
563 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
565 wxMethodPtrArray ret
;
567 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
568 if (m_methods
[i
].GetName() == name
)
569 ret
.Add(&m_methods
[i
]);
575 wxMethodPtrArray
wxClass::RecursiveUpwardFindMethodsNamed(const wxString
& name
,
576 const wxXmlInterface
* allclasses
) const
578 // first, search into *this
579 wxMethodPtrArray ret
= FindMethodsNamed(name
);
580 if (ret
.GetCount()>0)
581 return ret
; // stop here, don't look upward in the parents
583 // then, search into parents of this class
584 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
586 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
587 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
589 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
591 LogError("Could not find parent '%s' of class '%s'...",
592 m_parents
[i
], GetName());
596 wxMethodPtrArray temp
= parent
->RecursiveUpwardFindMethodsNamed(name
, allclasses
);
597 WX_APPEND_ARRAY(ret
, temp
);
606 // ----------------------------------------------------------------------------
608 // ----------------------------------------------------------------------------
610 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
612 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
614 // sort alphabetically
615 return item1
->GetName().Cmp(item2
->GetName());
618 void wxXmlInterface::Dump(const wxString
& filename
)
620 wxFFileOutputStream
apioutput( filename
);
621 wxTextOutputStream
apiout( apioutput
);
623 // dump the classes in alphabetical order
624 wxSortedClassArray
sorted(CompareWxClassObjects
);
625 sorted
.Alloc(m_classes
.GetCount());
626 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
627 sorted
.Add(&m_classes
[i
]);
629 // now they have been sorted
630 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
631 sorted
[i
]->Dump(apiout
);
634 bool wxXmlInterface::CheckParseResults() const
636 // this check can be quite slow, so do it only for debug releases:
638 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
639 if (!m_classes
[i
].CheckConsistency())
646 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
650 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
651 if (m_classes
[i
].GetHeader() == headerfile
)
652 ret
.Add(&m_classes
[i
]);
658 // ----------------------------------------------------------------------------
659 // wxXmlGccInterface helper declarations
660 // ----------------------------------------------------------------------------
662 // or-able flags for a toResolveTypeItem->attrib:
663 #define ATTRIB_CONST 1
664 #define ATTRIB_REFERENCE 2
665 #define ATTRIB_POINTER 4
666 #define ATTRIB_ARRAY 8
668 // it may sound strange but gccxml, in order to produce shorter ID names
669 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
670 // in order to be able to translate such strings into numbers using strtoul()
671 // we use as base 10 (possible digits) + 25 (possible characters) = 35
672 #define GCCXML_BASE 35
674 class toResolveTypeItem
677 toResolveTypeItem() { attribs
=0; }
678 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
679 : ref(refID
), attribs(attribint
) {}
681 unsigned long ref
, // the referenced type's ID
682 attribs
; // the attributes of this reference
687 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
688 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
689 wxIntegerHash
, wxIntegerEqual
,
690 wxToResolveTypeHashMap
);
692 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
693 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
694 wxIntegerHash
, wxIntegerEqual
,
695 wxClassMemberIdHashMap
);
699 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
703 // utility to parse gccXML ID values;
704 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
705 // but is a little bit faster
706 bool getID(unsigned long *id
, const wxString
& str
)
708 const wxStringCharType
* const start
= str
.wx_str()+1;
709 wxStringCharType
*end
;
710 #if wxUSE_UNICODE_WCHAR
711 unsigned long val
= wcstoul(start
, &end
, GCCXML_BASE
);
713 unsigned long val
= strtoul(start
, &end
, GCCXML_BASE
);
716 // return true only if scan was stopped by the terminating NUL and
717 // if the string was not empty to start with and no under/overflow
719 if ( *end
!= '\0' || end
== start
|| errno
== ERANGE
|| errno
== EINVAL
)
726 // utility specialized to parse efficiently the gccXML list of IDs which occur
727 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
728 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxString
& str
)
730 const wxStringCharType
* const start
= str
.wx_str();
731 #if wxUSE_UNICODE_WCHAR
732 size_t len
= wcslen(start
);
734 size_t len
= strlen(start
);
737 if (len
== 0 || start
[0] != '_')
740 const wxStringCharType
*curpos
= start
,
742 wxStringCharType
*nexttoken
;
746 // curpos always points to the underscore of the next token to parse:
747 #if wxUSE_UNICODE_WCHAR
748 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
750 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
752 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
755 // advance current position
756 curpos
= nexttoken
+ 1;
758 // add this ID to the hashmap
759 wxClassMemberIdHashMap::value_type
v(id
, p
);
767 // ----------------------------------------------------------------------------
769 // ----------------------------------------------------------------------------
771 bool wxXmlGccInterface::Parse(const wxString
& filename
)
777 LogMessage("Parsing %s...", filename
);
779 if (!doc
.Load(filename
)) {
780 LogError("can't load %s", filename
);
784 // start processing the XML file
785 if (doc
.GetRoot()->GetName() != "GCC_XML") {
786 LogError("invalid root node for %s", filename
);
790 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
793 #define MIN_REVISION 120
795 if (!version
.StartsWith("1."))
799 unsigned long rev
= 0;
800 if (!version
.Mid(2).ToULong(&rev
))
803 if (rev
< MIN_REVISION
)
809 LogError("The version of GCC-XML used for the creation of %s is too old; "
810 "the cvs_revision attribute of the root node reports '%s', "
811 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
815 wxToResolveTypeHashMap toResolveTypes
;
816 wxClassMemberIdHashMap members
;
817 wxTypeIdHashMap types
;
818 wxTypeIdHashMap files
;
819 wxTypeIdHashMap typedefs
;
821 // prealloc quite a lot of memory!
822 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
824 // build a list of wx classes and in general of all existent types
825 child
= doc
.GetRoot()->GetChildren();
828 const wxString
& n
= child
->GetName();
830 unsigned long id
= 0;
831 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
833 // NOTE: <File> nodes can have an id == "f0"...
835 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
841 wxString cname
= child
->GetAttribute("name");
842 if (cname
.IsEmpty()) {
843 LogError("Invalid empty name for '%s' node", n
);
847 // only register wx classes (do remember also the IDs of their members)
848 if (cname
.StartsWith("wx"))
850 // NB: "file" attribute contains an ID value that we'll resolve later
851 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
853 // the just-inserted class:
854 wxClass
*newClass
= &m_classes
.Last();
856 // now get a list of the base classes:
857 wxXmlNode
*baseNode
= child
->GetChildren();
860 // for now we store as "parents" only the parent IDs...
861 // later we will resolve them into full class names
862 if (baseNode
->GetName() == "Base")
863 newClass
->AddParent(baseNode
->GetAttribute("type"));
865 baseNode
= baseNode
->GetNext();
868 const wxString
& ids
= child
->GetAttribute("members");
871 if (child
->GetAttribute("incomplete") != "1") {
872 LogError("Invalid member IDs for '%s' class node: %s",
873 cname
, child
->GetAttribute("id"));
876 //else: don't warn the user; it looks like "incomplete" classes
877 // never have any member...
881 // decode the non-empty list of IDs:
882 if (!getMemberIDs(&members
, newClass
, ids
)) {
883 LogError("Invalid member IDs for '%s' class node: %s",
884 cname
, child
->GetAttribute("id"));
890 // register this class also as possible return/argument type:
893 else if (n
== "Typedef")
895 unsigned long typeId
= 0;
896 if (!getID(&typeId
, child
->GetAttribute("type"))) {
897 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
901 // this typedef node tell us that every type referenced with the
902 // "typeId" ID should be called with another name:
903 wxString name
= child
->GetAttribute("name");
905 // save this typedef in a separate hashmap...
906 typedefs
[typeId
] = name
;
910 else if (n
== "PointerType" || n
== "ReferenceType" ||
911 n
== "CvQualifiedType" || n
== "ArrayType")
913 unsigned long type
= 0;
914 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
915 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
919 unsigned long attr
= 0;
920 if (n
== "PointerType")
921 attr
= ATTRIB_POINTER
;
922 else if (n
== "ReferenceType")
923 attr
= ATTRIB_REFERENCE
;
924 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
926 else if (n
== "ArrayType")
929 // these nodes make reference to other types... we'll resolve them later
930 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
932 else if (n
== "FunctionType" || n
== "MethodType")
935 TODO: parsing FunctionType and MethodType nodes is not as easy
936 as for other "simple" types.
940 wxXmlNode
*arg
= child
->GetChildren();
943 if (arg
->GetName() == "Argument")
944 argstr
+= arg
->GetAttribute("type") + ", ";
945 arg
= arg
->GetNext();
948 if (argstr
.Len() > 0)
949 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
951 // these nodes make reference to other types... we'll resolve them later
952 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
953 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
955 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
956 // to a typedef later...
958 else if (n
== "File")
960 if (!child
->GetAttribute("id").StartsWith("f")) {
961 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
965 // just ignore this node... all file IDs/names were already parsed
966 files
[id
] = child
->GetAttribute("name");
970 // we register everything else as a possible return/argument type:
971 const wxString
& name
= child
->GetAttribute("name");
976 //typeNames.Add(name);
981 // this may happen with unnamed structs/union, special ctors,
982 // or other exotic things which we are not interested to, since
983 // they're never used as return/argument types by wxWidgets methods
986 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
987 n
, child
->GetAttribute("id"));
993 child
= child
->GetNext();
995 // give feedback to the user about the progress...
996 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
999 // some nodes with IDs referenced by methods as return/argument types, do reference
1000 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
1001 // thus we need to resolve their name iteratively:
1002 while (toResolveTypes
.size()>0)
1005 LogMessage("%d types were collected; %d types need yet to be resolved...",
1006 types
.size(), toResolveTypes
.size());
1008 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
1009 i
!= toResolveTypes
.end();)
1011 unsigned long id
= i
->first
;
1012 unsigned long referenced
= i
->second
.ref
;
1014 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
1015 if (primary
!= types
.end())
1017 // this to-resolve-type references a "primary" type
1019 wxString newtype
= primary
->second
;
1020 int attribs
= i
->second
.attribs
;
1022 // attribs may contain a combination of ATTRIB_* flags:
1023 if (attribs
& ATTRIB_CONST
)
1024 newtype
= "const " + newtype
;
1025 if (attribs
& ATTRIB_REFERENCE
)
1026 newtype
= newtype
+ "&";
1027 if (attribs
& ATTRIB_POINTER
)
1028 newtype
= newtype
+ "*";
1029 if (attribs
& ATTRIB_ARRAY
)
1030 newtype
= newtype
+ "[]";
1032 // add the resolved type to the list of "primary" types
1033 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
1034 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
1036 types
[id
] = newtype
;
1038 // this one has been resolved; erase it through its iterator!
1039 toResolveTypes
.erase(i
);
1041 // now iterator i is invalid; assign it again to the beginning
1042 i
= toResolveTypes
.begin();
1046 // then search in the referenced types themselves:
1047 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
1048 if (idx2
!= toResolveTypes
.end())
1050 // merge this to-resolve-type with the idx2->second type
1051 i
->second
.ref
= idx2
->second
.ref
;
1052 i
->second
.attribs
|= idx2
->second
.attribs
;
1054 // this type will eventually be solved in the next while() iteration
1059 LogError("Cannot solve '%d' reference type!", referenced
);
1066 // resolve header names
1067 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1069 unsigned long fileID
= 0;
1070 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
1071 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
1076 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
1077 if (idx
== files
.end())
1079 // this is an error!
1080 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
1083 m_classes
[i
].SetHeader(idx
->second
);
1086 // resolve parent names
1087 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1089 for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++)
1093 if (!getID(&id
, m_classes
[i
].GetParent(k
))) {
1094 LogError("invalid parent class ID for '%s'", m_classes
[i
].GetName());
1098 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1099 if (idx
== types
.end())
1101 // this is an error!
1102 LogError("couldn't find parent class ID '%d'", id
);
1105 // replace k-th parent with its true name:
1106 m_classes
[i
].SetParent(k
, idx
->second
);
1110 // build the list of the wx methods
1111 child
= doc
.GetRoot()->GetChildren();
1114 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
1116 // only register public&protected methods
1117 if ((acc
== "public" || acc
== "protected") &&
1118 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
1120 unsigned long id
= 0;
1121 if (!getID(&id
, child
->GetAttribute("id"))) {
1122 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
1126 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
1127 if (it
!= members
.end())
1129 wxClass
*p
= it
->second
;
1131 // this <Method> node is a method of the i-th class!
1133 if (!ParseMethod(child
, types
, newfunc
)) {
1134 LogError("The method '%s' could not be added to class '%s'",
1135 child
->GetAttribute("demangled"), p
->GetName());
1139 // do some additional check that we can do only here:
1141 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
1142 LogError("The method '%s' does not seem to be a ctor for '%s'",
1143 newfunc
.GetName(), p
->GetName());
1146 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
1147 LogError("The method '%s' does not seem to be a dtor for '%s'",
1148 newfunc
.GetName(), p
->GetName());
1152 p
->AddMethod(newfunc
);
1156 child
= child
->GetNext();
1158 // give feedback to the user about the progress...
1159 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1162 if (!CheckParseResults())
1168 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1169 const wxTypeIdHashMap
& types
,
1172 // get the real name
1173 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1174 if (p
->GetName() == "Destructor")
1176 else if (p
->GetName() == "OperatorMethod")
1177 name
= "operator" + name
;
1179 // resolve return type
1181 unsigned long retid
= 0;
1182 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1184 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1185 LogError("Empty return ID for method '%s', with ID '%s'",
1186 name
, p
->GetAttribute("id"));
1192 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1193 if (retidx
== types
.end()) {
1194 LogError("Could not find return type ID '%s'", retid
);
1198 ret
= wxType(retidx
->second
);
1200 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1201 retidx
->second
, name
, p
->GetAttribute("id"));
1206 // resolve argument types
1207 wxArgumentTypeArray argtypes
;
1208 wxXmlNode
*arg
= p
->GetChildren();
1211 if (arg
->GetName() == "Argument")
1213 unsigned long id
= 0;
1214 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1215 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1216 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1220 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1221 if (idx
== types
.end()) {
1222 LogError("Could not find argument type ID '%s'", id
);
1226 argtypes
.Add(wxArgumentType(idx
->second
,
1227 arg
->GetAttribute("default"),
1228 arg
->GetAttribute("name")));
1231 arg
= arg
->GetNext();
1234 m
.SetReturnType(ret
);
1236 m
.SetArgumentTypes(argtypes
);
1237 m
.SetConst(p
->GetAttribute("const") == "1");
1238 m
.SetStatic(p
->GetAttribute("static") == "1");
1240 // NOTE: gccxml is smart enough to mark as virtual those functions
1241 // which are declared virtual in base classes but don't have
1242 // the "virtual" keyword explicitely indicated in the derived
1243 // classes... so we don't need any further logic for virtuals
1245 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1246 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1247 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1249 // decode access specifier
1250 if (p
->GetAttribute("access") == "public")
1251 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1252 else if (p
->GetAttribute("access") == "protected")
1253 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1254 else if (p
->GetAttribute("access") == "private")
1255 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1258 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1267 // ----------------------------------------------------------------------------
1268 // wxXmlDoxygenInterface global helpers
1269 // ----------------------------------------------------------------------------
1271 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1275 // consider the tree
1277 // <a><b>this</b> is a <b>string</b></a>
1286 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1287 // this function returns "this is a string"
1289 wxXmlNode
*ref
= n
->GetChildren();
1291 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1292 text
+= ref
->GetNodeContent();
1293 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1294 text
+= ref
->GetContent();
1296 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1298 ref
= ref
->GetNext();
1304 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1309 wxXmlNode
*p
= parent
->GetChildren();
1312 switch (p
->GetType())
1314 case wxXML_TEXT_NODE
:
1315 if (p
->GetContent() == name
)
1319 case wxXML_ELEMENT_NODE
:
1320 // recurse into this node...
1321 if (HasTextNodeContaining(p
, name
))
1336 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1341 const wxXmlNode
*p
= parent
->GetChildren();
1344 if (p
->GetName() == name
)
1347 // search recursively in the children of this node
1348 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1358 int GetAvailabilityFor(const wxXmlNode
*node
)
1360 // identify <onlyfor> custom XML tags
1361 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1363 return wxPORT_UNKNOWN
;
1365 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1366 int nAvail
= wxPORT_UNKNOWN
;
1367 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1369 if (!ports
[i
].StartsWith("wx")) {
1370 LogError("unexpected port ID '%s'", ports
[i
]);
1374 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1381 // ----------------------------------------------------------------------------
1382 // wxXmlDoxygenInterface
1383 // ----------------------------------------------------------------------------
1385 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1387 wxXmlDocument index
;
1388 wxXmlNode
*compound
;
1390 LogMessage("Parsing %s...", filename
);
1392 if (!index
.Load(filename
)) {
1393 LogError("can't load %s", filename
);
1397 // start processing the index:
1398 if (index
.GetRoot()->GetName() != "doxygenindex") {
1399 LogError("invalid root node for %s", filename
);
1404 NB: we may need in future to do a version-check here if the
1405 format of the XML generated by doxygen changes.
1406 For now (doxygen version 1.5.5), this check is not required
1407 since AFAIK the XML format never changed since it was introduced.
1410 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1412 // process files referenced by this index file
1413 compound
= index
.GetRoot()->GetChildren();
1416 if (compound
->GetName() == "compound" &&
1417 compound
->GetAttribute("kind") == "class")
1419 wxString refid
= compound
->GetAttribute("refid");
1421 wxFileName
fn(filename
);
1422 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1426 compound
= compound
->GetNext();
1430 if (!CheckParseResults())
1436 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1438 wxClassMemberIdHashMap parents
;
1444 LogMessage("Parsing %s...", filename
);
1446 if (!doc
.Load(filename
)) {
1447 LogError("can't load %s", filename
);
1451 // start processing this compound definition XML
1452 if (doc
.GetRoot()->GetName() != "doxygen") {
1453 LogError("invalid root node for %s", filename
);
1457 // build a list of wx classes
1458 child
= doc
.GetRoot()->GetChildren();
1461 if (child
->GetName() == "compounddef" &&
1462 child
->GetAttribute("kind") == "class")
1466 wxString absoluteFile
, header
;
1468 wxXmlNode
*subchild
= child
->GetChildren();
1471 wxString kind
= subchild
->GetAttribute("kind");
1473 // parse only public&protected functions:
1474 if (subchild
->GetName() == "sectiondef" &&
1475 (kind
== "public-func" || kind
== "protected-func"))
1478 wxXmlNode
*membernode
= subchild
->GetChildren();
1481 if (membernode
->GetName() == "memberdef" &&
1482 membernode
->GetAttribute("kind") == "function")
1486 if (!ParseMethod(membernode
, m
, header
)) {
1487 LogError("The method '%s' could not be added to class '%s'",
1488 m
.GetName(), klass
.GetName());
1492 if (kind
== "public-func")
1493 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1494 else if (kind
== "protected-func")
1495 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1496 else if (kind
== "private-func")
1497 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1499 if (absoluteFile
.IsEmpty())
1500 absoluteFile
= header
;
1501 else if (header
!= absoluteFile
)
1503 LogError("The method '%s' is documented in a different "
1504 "file from others (which belong to '%s') ?",
1505 header
, absoluteFile
);
1512 membernode
= membernode
->GetNext();
1515 // all methods of this class were taken from the header "absoluteFile":
1516 klass
.SetHeader(absoluteFile
);
1518 else if (subchild
->GetName() == "compoundname")
1520 klass
.SetName(subchild
->GetNodeContent());
1522 /*else if (subchild->GetName() == "includes")
1524 // NOTE: we'll get the header from the <location> tags
1525 // scattered inside <memberdef> tags instead of
1526 // this <includes> tag since it does not contain
1527 // the absolute path of the header
1529 klass.SetHeader(subchild->GetNodeContent());
1531 else if (subchild
->GetName() == "detaileddescription")
1533 // identify <onlyfor> custom XML tags
1534 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1536 else if (subchild
->GetName() == "basecompoundref")
1538 // add the name of this parent to the list of klass' parents
1539 klass
.AddParent(subchild
->GetNodeContent());
1542 subchild
= subchild
->GetNext();
1547 m_classes
.Add(klass
);
1549 LogWarning("discarding class '%s' with %d methods...",
1550 klass
.GetName(), klass
.GetMethodCount());
1553 child
= child
->GetNext();
1555 // give feedback to the user about the progress...
1556 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1562 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1564 wxArgumentTypeArray args
;
1567 wxXmlNode
*child
= p
->GetChildren();
1570 if (child
->GetName() == "name")
1571 m
.SetName(child
->GetNodeContent());
1572 else if (child
->GetName() == "type")
1573 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1574 else if (child
->GetName() == "param")
1576 wxString typestr
, namestr
, defstr
, arrstr
;
1577 wxXmlNode
*n
= child
->GetChildren();
1580 if (n
->GetName() == "type")
1581 // if the <type> node has children, they should be all TEXT and <ref> nodes
1582 // and we need to take the text they contain, in the order they appear
1583 typestr
= GetTextFromChildren(n
);
1584 else if (n
->GetName() == "declname")
1585 namestr
= GetTextFromChildren(n
);
1586 else if (n
->GetName() == "defval")
1587 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1588 else if (n
->GetName() == "array")
1589 arrstr
= GetTextFromChildren(n
);
1594 if (typestr
.IsEmpty()) {
1595 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1599 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1601 // can we use preprocessor output to transform the default value
1602 // into the same form which gets processed by wxXmlGccInterface?
1603 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1604 if (it
!= m_preproc
.end())
1605 newarg
.SetDefaultValue(defstr
, it
->second
);
1609 else if (child
->GetName() == "location")
1612 if (child
->GetAttribute("line").ToLong(&line
))
1613 m
.SetLocation((int)line
);
1614 header
= child
->GetAttribute("file");
1616 else if (child
->GetName() == "detaileddescription")
1618 // when a method has a @deprecated tag inside its description,
1619 // Doxygen outputs somewhere nested inside <detaileddescription>
1620 // a <xreftitle>Deprecated</xreftitle> tag.
1621 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1623 // identify <onlyfor> custom XML tags
1624 m
.SetAvailability(GetAvailabilityFor(child
));
1627 child
= child
->GetNext();
1630 m
.SetArgumentTypes(args
);
1631 m
.SetConst(p
->GetAttribute("const")=="yes");
1632 m
.SetStatic(p
->GetAttribute("static")=="yes");
1634 // NOTE: Doxygen is smart enough to mark as virtual those functions
1635 // which are declared virtual in base classes but don't have
1636 // the "virtual" keyword explicitely indicated in the derived
1637 // classes... so we don't need any further logic for virtuals
1639 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1640 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1643 LogError("The prototype '%s' is not valid!", m
.GetAsString());