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
22 #include "wx/xml/xml.h"
23 #include "wx/wfstream.h"
24 #include "wx/hashmap.h"
25 #include "wx/filename.h"
26 #include "xmlparser.h"
29 #include <wx/arrimpl.cpp>
30 WX_DEFINE_OBJARRAY(wxTypeArray
)
31 WX_DEFINE_OBJARRAY(wxArgumentTypeArray
)
32 WX_DEFINE_OBJARRAY(wxMethodArray
)
33 WX_DEFINE_OBJARRAY(wxClassArray
)
36 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
37 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
40 // defined in ifacecheck.cpp
41 extern bool g_verbose
;
44 bool g_bLogEnabled
= true;
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
54 void wxType::SetTypeFromString(const wxString
& t
)
57 TODO: optimize the following code writing a single function
58 which works at char-level and does everything in a single pass
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"
67 // make sure the * and & operator always use the same spacing rules
68 // (to make sure GetAsString() output is always consistent)
69 m_strType
.Replace("*", "* ");
70 m_strType
.Replace("&", "& ");
71 m_strType
.Replace(" *", "*");
72 m_strType
.Replace(" &", "&");
74 while (m_strType
.Contains(" "))
75 m_strType
.Replace(" ", " "); // do it once again
77 m_strType
.Replace(" ,", ",");
79 m_strType
= m_strType
.Strip(wxString::both
);
81 // now set the clean version
82 m_strTypeClean
= m_strType
;
83 m_strTypeClean
.Replace("const", "");
84 m_strTypeClean
.Replace("static", "");
85 m_strTypeClean
.Replace("*", "");
86 m_strTypeClean
.Replace("&", "");
87 m_strTypeClean
.Replace("[]", "");
88 m_strTypeClean
= m_strTypeClean
.Strip(wxString::both
);
90 // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
91 // need to be considered as the same type
92 if (m_strTypeClean
.EndsWith("Base"))
93 m_strTypeClean
= m_strTypeClean
.Left(m_strTypeClean
.Len()-4);
96 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
97 // fix this to avoid false positives
98 m_strTypeClean
.Replace("wxDateTime::", "");
99 m_strTypeClean
.Replace("wxStockGDI::", ""); // same story for some other classes
100 m_strTypeClean
.Replace("wxHelpEvent::", "");
103 bool wxType::IsOk() const
105 // NB: m_strType can contain the :: operator; think to e.g. the
106 // "reverse_iterator_impl<wxString::const_iterator>" type
107 // It can also contain commas, * and & operators etc
109 return !m_strTypeClean
.IsEmpty();
112 bool wxType::operator==(const wxType
& m
) const
114 // brain-dead comparison:
116 if (m_strTypeClean
== m
.m_strTypeClean
&&
117 IsConst() == m
.IsConst() &&
118 IsStatic() == m
.IsStatic() &&
119 IsPointer() == m
.IsPointer() &&
120 IsReference() == m
.IsReference())
125 LogMessage("Type '%s' does not match type '%s'", m_strType
, m
.m_strType
);
126 LogMessage(" => TypeClean %s / %s; IsConst %d / %d; IsStatic %d / %d; IsPointer %d / %d; IsReference %d / %d",
127 m_strTypeClean
, m
.m_strTypeClean
, IsConst(), m
.IsConst(),
128 IsStatic(), m
.IsStatic(), IsPointer(), m
.IsPointer(),
129 IsReference(), m
.IsReference());
136 // ----------------------------------------------------------------------------
138 // ----------------------------------------------------------------------------
140 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
142 m_strDefaultValue
= defval
.Strip(wxString::both
);
143 m_strDefaultValueForCmp
= defvalForCmp
.IsEmpty() ? m_strDefaultValue
: defvalForCmp
.Strip(wxString::both
);
145 // adjust aesthetic form of DefaultValue for the modify mode of ifacecheck:
146 // we may need to write it out in an interface header
147 if (m_strDefaultValue
== "0u")
148 m_strDefaultValue
= "0";
150 // in order to make valid&simple comparison on argument defaults,
151 // we reduce some of the multiple forms in which the same things may appear
153 if (m_strDefaultValueForCmp
== "0u")
154 m_strDefaultValueForCmp
= "0";
156 m_strDefaultValue
.Replace("0x000000001", "1");
157 m_strDefaultValueForCmp
.Replace("0x000000001", "1");
159 // fix for unicode strings:
160 m_strDefaultValue
.Replace("\\000\\000\\000", "");
161 m_strDefaultValueForCmp
.Replace("\\000\\000\\000", "");
163 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
164 m_strDefaultValueForCmp
.EndsWith(")"))
166 // get rid of the wxT() part
167 unsigned int len
= m_strDefaultValueForCmp
.Len();
168 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
173 m_strDefaultValueForCmp.Replace("0", "NULL");
175 m_strDefaultValueForCmp.Replace("NULL", "0");
178 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
179 // fix this to avoid false positives
180 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
181 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
182 m_strDefaultValueForCmp
.Replace("wxHelpEvent::", ""); // same story for some other classes
184 m_strDefaultValueForCmp
.Replace("wxGet_wxConvLocal()", "wxConvLocal");
186 m_strDefaultValueForCmp
.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
189 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
190 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
193 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
195 if ((const wxType
&)(*this) != (const wxType
&)m
)
199 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
200 // numbers; avoid false positives in this case!
201 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
202 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
205 // fix for default values which were replaced by gcc-xml with their numeric values
206 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
207 if (m_strTypeClean
== "long" || m_strTypeClean
== "int")
209 if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) ||
210 (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx")))
213 LogMessage("Supposing '%s' default value to be the same of '%s'...",
214 m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
);
220 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
222 // maybe the default values are numbers.
223 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
224 // To handle these cases, we try to convert the default value strings to numbers:
225 long def1val
, def2val
;
226 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
227 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
229 if (def1val
== def2val
)
230 return true; // the default values match
234 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
235 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
239 // we deliberately avoid checks on the argument name
245 // ----------------------------------------------------------------------------
247 // ----------------------------------------------------------------------------
249 bool wxMethod::IsOk() const
251 // NOTE: m_retType can be a wxEmptyType, and means that this method
252 // is a ctor or a dtor.
253 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
254 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
258 if (m_strName
.IsEmpty())
261 // a function can't be both const and static or virtual and static!
262 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
263 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
267 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
269 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
270 if (!m_args
[i
].IsOk()) {
271 LogError("'%s' method has invalid %d-th argument type: %s",
272 m_strName
, i
+1, m_args
[i
].GetAsString());
276 // NB: the default value of the arguments can contain pretty much everything
277 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
278 // so we don't do any test on their contents
279 if (m_args
.GetCount()>0)
281 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
282 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
284 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
285 LogError("'%s' method has %d-th argument which has no default value "
286 "(while the previous one had one!)",
291 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
298 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
300 if (GetReturnType() != m
.GetReturnType() ||
301 GetName() != m
.GetName())
304 LogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m
.GetName());
308 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
310 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
311 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
315 // compare argument types
316 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
317 if (m_args
[i
] != m
.m_args
[i
])
323 bool wxMethod::operator==(const wxMethod
& m
) const
326 if (IsConst() != m
.IsConst() ||
327 IsStatic() != m
.IsStatic() ||
328 IsVirtual() != m
.IsVirtual() ||
329 IsPureVirtual() != m
.IsPureVirtual() ||
330 IsDeprecated() != m
.IsDeprecated() ||
331 GetAccessSpecifier() != m
.GetAccessSpecifier())
334 LogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m
.GetName());
339 // check everything else
340 return MatchesExceptForAttributes(m
);
343 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
344 bool bDeprecated
, bool bAccessSpec
) const
348 // NOTE: for return and argument types, never use wxType::GetAsCleanString
349 // since in that way we'd miss important decorators like &,*,const etc
351 if (m_retType
!=wxEmptyType
)
352 ret
+= m_retType
.GetAsString() + " ";
353 //else; this is a ctor or dtor
355 ret
+= m_strName
+ "(";
357 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
359 ret
+= m_args
[i
].GetAsString();
361 const wxString
& name
= m_args
[i
].GetArgumentName();
362 if (bWithArgumentNames
&& !name
.IsEmpty())
365 const wxString
& def
= bCleanDefaultValues
?
366 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
373 if (m_args
.GetCount()>0)
374 ret
= ret
.Left(ret
.Len()-2);
381 ret
= "static " + ret
;
382 if (m_bVirtual
|| m_bPureVirtual
)
383 ret
= "virtual " + ret
;
386 if (m_bDeprecated
&& bDeprecated
)
387 ret
+= " [deprecated]";
396 case wxMAS_PROTECTED
:
397 ret
+= " [protected]";
408 void wxMethod::Dump(wxTextOutputStream
& stream
) const
410 stream
<< "[" + m_retType
.GetAsString() + "]";
411 stream
<< "[" + m_strName
+ "]";
413 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
414 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
415 "=" + m_args
[i
].GetDefaultValue() + "]";
422 stream
<< " VIRTUAL";
424 stream
<< " PURE-VIRTUAL";
426 stream
<< " DEPRECATED";
431 // ----------------------------------------------------------------------------
433 // ----------------------------------------------------------------------------
435 wxString
wxClass::GetNameWithoutTemplate() const
437 // NB: I'm not sure this is the right terminology for this function!
439 if (m_strName
.Contains("<"))
440 return m_strName
.Left(m_strName
.Find("<"));
444 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
446 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
447 // named wxWritableCharTypeBuffer, without the <...> part!
449 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
455 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
457 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
463 void wxClass::Dump(wxTextOutputStream
& out
) const
465 out
<< m_strName
+ "\n";
467 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
469 // dump all our methods
471 m_methods
[i
].Dump(out
);
478 bool wxClass::CheckConsistency() const
480 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
481 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
482 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
484 LogError("class %s has two methods with the same prototype: '%s'",
485 m_strName
, m_methods
[i
].GetAsString());
489 //((wxClass*)this)->m_methods.RemoveAt(j);
496 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
498 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
499 if (m_methods
[i
] == m
)
500 return &m_methods
[i
];
504 const wxMethod
* wxClass::RecursiveUpwardFindMethod(const wxMethod
& m
,
505 const wxXmlInterface
* allclasses
) const
507 // first, search into *this
508 const wxMethod
* ret
= FindMethod(m
);
512 // then, search into its parents
513 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
515 // ignore non-wx-classes parents
516 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
517 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
519 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
521 LogError("Could not find parent '%s' of class '%s'...",
522 m_parents
[i
], GetName());
526 const wxMethod
*parentMethod
= parent
->RecursiveUpwardFindMethod(m
, allclasses
);
532 // could not find anything even in parent classes...
536 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
538 wxMethodPtrArray ret
;
540 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
541 if (m_methods
[i
].GetName() == name
)
542 ret
.Add(&m_methods
[i
]);
548 wxMethodPtrArray
wxClass::RecursiveUpwardFindMethodsNamed(const wxString
& name
,
549 const wxXmlInterface
* allclasses
) const
551 // first, search into *this
552 wxMethodPtrArray ret
= FindMethodsNamed(name
);
553 if (ret
.GetCount()>0)
554 return ret
; // stop here, don't look upward in the parents
556 // then, search into parents of this class
557 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
559 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
560 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
562 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
564 LogError("Could not find parent '%s' of class '%s'...",
565 m_parents
[i
], GetName());
569 wxMethodPtrArray temp
= parent
->RecursiveUpwardFindMethodsNamed(name
, allclasses
);
570 WX_APPEND_ARRAY(ret
, temp
);
579 // ----------------------------------------------------------------------------
581 // ----------------------------------------------------------------------------
583 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
585 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
587 // sort alphabetically
588 return item1
->GetName().Cmp(item2
->GetName());
591 void wxXmlInterface::Dump(const wxString
& filename
)
593 wxFFileOutputStream
apioutput( filename
);
594 wxTextOutputStream
apiout( apioutput
);
596 // dump the classes in alphabetical order
597 wxSortedClassArray
sorted(CompareWxClassObjects
);
598 sorted
.Alloc(m_classes
.GetCount());
599 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
600 sorted
.Add(&m_classes
[i
]);
602 // now they have been sorted
603 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
604 sorted
[i
]->Dump(apiout
);
607 bool wxXmlInterface::CheckParseResults() const
609 // this check can be quite slow, so do it only for debug releases:
611 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
612 if (!m_classes
[i
].CheckConsistency())
619 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
623 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
624 if (m_classes
[i
].GetHeader() == headerfile
)
625 ret
.Add(&m_classes
[i
]);
631 // ----------------------------------------------------------------------------
632 // wxXmlGccInterface helper declarations
633 // ----------------------------------------------------------------------------
635 // or-able flags for a toResolveTypeItem->attrib:
636 #define ATTRIB_CONST 1
637 #define ATTRIB_REFERENCE 2
638 #define ATTRIB_POINTER 4
639 #define ATTRIB_ARRAY 8
641 // it may sound strange but gccxml, in order to produce shorter ID names
642 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
643 // in order to be able to translate such strings into numbers using strtoul()
644 // we use as base 10 (possible digits) + 25 (possible characters) = 35
645 #define GCCXML_BASE 35
647 class toResolveTypeItem
650 toResolveTypeItem() { attribs
=0; }
651 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
652 : ref(refID
), attribs(attribint
) {}
654 unsigned long ref
, // the referenced type's ID
655 attribs
; // the attributes of this reference
660 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
661 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
662 wxIntegerHash
, wxIntegerEqual
,
663 wxToResolveTypeHashMap
);
665 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
666 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
667 wxIntegerHash
, wxIntegerEqual
,
668 wxClassMemberIdHashMap
);
672 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
676 // utility to parse gccXML ID values;
677 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
678 // but is a little bit faster
679 bool getID(unsigned long *id
, const wxString
& str
)
681 const wxStringCharType
* const start
= str
.wx_str()+1;
682 wxStringCharType
*end
;
683 #if wxUSE_UNICODE_WCHAR
684 unsigned long val
= wcstoul(start
, &end
, GCCXML_BASE
);
686 unsigned long val
= strtoul(start
, &end
, GCCXML_BASE
);
689 // return true only if scan was stopped by the terminating NUL and
690 // if the string was not empty to start with and no under/overflow
692 if ( *end
!= '\0' || end
== start
|| errno
== ERANGE
|| errno
== EINVAL
)
699 // utility specialized to parse efficiently the gccXML list of IDs which occur
700 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
701 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxString
& str
)
703 const wxStringCharType
* const start
= str
.wx_str();
704 #if wxUSE_UNICODE_WCHAR
705 size_t len
= wcslen(start
);
707 size_t len
= strlen(start
);
710 if (len
== 0 || start
[0] != '_')
713 const wxStringCharType
*curpos
= start
,
715 wxStringCharType
*nexttoken
;
719 // curpos always points to the underscore of the next token to parse:
720 #if wxUSE_UNICODE_WCHAR
721 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
723 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
725 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
728 // advance current position
729 curpos
= nexttoken
+ 1;
731 // add this ID to the hashmap
732 wxClassMemberIdHashMap::value_type
v(id
, p
);
740 // ----------------------------------------------------------------------------
742 // ----------------------------------------------------------------------------
744 bool wxXmlGccInterface::Parse(const wxString
& filename
)
750 LogMessage("Parsing %s...", filename
);
752 if (!doc
.Load(filename
)) {
753 LogError("can't load %s", filename
);
757 // start processing the XML file
758 if (doc
.GetRoot()->GetName() != "GCC_XML") {
759 LogError("invalid root node for %s", filename
);
763 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
766 #define MIN_REVISION 120
768 if (!version
.StartsWith("1."))
772 unsigned long rev
= 0;
773 if (!version
.Mid(2).ToULong(&rev
))
776 if (rev
< MIN_REVISION
)
782 LogError("The version of GCC-XML used for the creation of %s is too old; "
783 "the cvs_revision attribute of the root node reports '%s', "
784 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
788 wxToResolveTypeHashMap toResolveTypes
;
789 wxClassMemberIdHashMap members
;
790 wxTypeIdHashMap types
;
791 wxTypeIdHashMap files
;
792 wxTypeIdHashMap typedefs
;
794 // prealloc quite a lot of memory!
795 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
797 // build a list of wx classes and in general of all existent types
798 child
= doc
.GetRoot()->GetChildren();
801 const wxString
& n
= child
->GetName();
803 unsigned long id
= 0;
804 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
806 // NOTE: <File> nodes can have an id == "f0"...
808 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
814 wxString cname
= child
->GetAttribute("name");
815 if (cname
.IsEmpty()) {
816 LogError("Invalid empty name for '%s' node", n
);
820 // only register wx classes (do remember also the IDs of their members)
821 if (cname
.StartsWith("wx"))
823 // NB: "file" attribute contains an ID value that we'll resolve later
824 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
826 // the just-inserted class:
827 wxClass
*newClass
= &m_classes
.Last();
829 // now get a list of the base classes:
830 wxXmlNode
*baseNode
= child
->GetChildren();
833 // for now we store as "parents" only the parent IDs...
834 // later we will resolve them into full class names
835 if (baseNode
->GetName() == "Base")
836 newClass
->AddParent(baseNode
->GetAttribute("type"));
838 baseNode
= baseNode
->GetNext();
841 const wxString
& ids
= child
->GetAttribute("members");
844 if (child
->GetAttribute("incomplete") != "1") {
845 LogError("Invalid member IDs for '%s' class node: %s",
846 cname
, child
->GetAttribute("id"));
849 //else: don't warn the user; it looks like "incomplete" classes
850 // never have any member...
854 // decode the non-empty list of IDs:
855 if (!getMemberIDs(&members
, newClass
, ids
)) {
856 LogError("Invalid member IDs for '%s' class node: %s",
857 cname
, child
->GetAttribute("id"));
863 // register this class also as possible return/argument type:
866 else if (n
== "Typedef")
868 unsigned long typeId
= 0;
869 if (!getID(&typeId
, child
->GetAttribute("type"))) {
870 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
874 // this typedef node tell us that every type referenced with the
875 // "typeId" ID should be called with another name:
876 wxString name
= child
->GetAttribute("name");
878 // save this typedef in a separate hashmap...
879 typedefs
[typeId
] = name
;
883 else if (n
== "PointerType" || n
== "ReferenceType" ||
884 n
== "CvQualifiedType" || n
== "ArrayType")
886 unsigned long type
= 0;
887 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
888 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
892 unsigned long attr
= 0;
893 if (n
== "PointerType")
894 attr
= ATTRIB_POINTER
;
895 else if (n
== "ReferenceType")
896 attr
= ATTRIB_REFERENCE
;
897 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
899 else if (n
== "ArrayType")
902 // these nodes make reference to other types... we'll resolve them later
903 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
905 else if (n
== "FunctionType" || n
== "MethodType")
908 TODO: parsing FunctionType and MethodType nodes is not as easy
909 as for other "simple" types.
913 wxXmlNode
*arg
= child
->GetChildren();
916 if (arg
->GetName() == "Argument")
917 argstr
+= arg
->GetAttribute("type") + ", ";
918 arg
= arg
->GetNext();
921 if (argstr
.Len() > 0)
922 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
924 // these nodes make reference to other types... we'll resolve them later
925 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
926 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
928 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
929 // to a typedef later...
931 else if (n
== "File")
933 if (!child
->GetAttribute("id").StartsWith("f")) {
934 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
938 // just ignore this node... all file IDs/names were already parsed
939 files
[id
] = child
->GetAttribute("name");
943 // we register everything else as a possible return/argument type:
944 const wxString
& name
= child
->GetAttribute("name");
949 //typeNames.Add(name);
954 // this may happen with unnamed structs/union, special ctors,
955 // or other exotic things which we are not interested to, since
956 // they're never used as return/argument types by wxWidgets methods
959 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
960 n
, child
->GetAttribute("id"));
966 child
= child
->GetNext();
968 // give feedback to the user about the progress...
969 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
972 // some nodes with IDs referenced by methods as return/argument types, do reference
973 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
974 // thus we need to resolve their name iteratively:
975 while (toResolveTypes
.size()>0)
978 LogMessage("%d types were collected; %d types need yet to be resolved...",
979 types
.size(), toResolveTypes
.size());
981 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
982 i
!= toResolveTypes
.end();)
984 unsigned long id
= i
->first
;
985 unsigned long referenced
= i
->second
.ref
;
987 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
988 if (primary
!= types
.end())
990 // this to-resolve-type references a "primary" type
992 wxString newtype
= primary
->second
;
993 int attribs
= i
->second
.attribs
;
995 // attribs may contain a combination of ATTRIB_* flags:
996 if (attribs
& ATTRIB_CONST
)
997 newtype
= "const " + newtype
;
998 if (attribs
& ATTRIB_REFERENCE
)
999 newtype
= newtype
+ "&";
1000 if (attribs
& ATTRIB_POINTER
)
1001 newtype
= newtype
+ "*";
1002 if (attribs
& ATTRIB_ARRAY
)
1003 newtype
= newtype
+ "[]";
1005 // add the resolved type to the list of "primary" types
1006 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
1007 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
1009 types
[id
] = newtype
;
1011 // this one has been resolved; erase it through its iterator!
1012 toResolveTypes
.erase(i
);
1014 // now iterator i is invalid; assign it again to the beginning
1015 i
= toResolveTypes
.begin();
1019 // then search in the referenced types themselves:
1020 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
1021 if (idx2
!= toResolveTypes
.end())
1023 // merge this to-resolve-type with the idx2->second type
1024 i
->second
.ref
= idx2
->second
.ref
;
1025 i
->second
.attribs
|= idx2
->second
.attribs
;
1027 // this type will eventually be solved in the next while() iteration
1032 LogError("Cannot solve '%d' reference type!", referenced
);
1039 // resolve header names
1040 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1042 unsigned long fileID
= 0;
1043 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
1044 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
1049 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
1050 if (idx
== files
.end())
1052 // this is an error!
1053 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
1056 m_classes
[i
].SetHeader(idx
->second
);
1059 // resolve parent names
1060 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1062 for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++)
1066 if (!getID(&id
, m_classes
[i
].GetParent(k
))) {
1067 LogError("invalid parent class ID for '%s'", m_classes
[i
].GetName());
1071 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1072 if (idx
== types
.end())
1074 // this is an error!
1075 LogError("couldn't find parent class ID '%d'", id
);
1078 // replace k-th parent with its true name:
1079 m_classes
[i
].SetParent(k
, idx
->second
);
1083 // build the list of the wx methods
1084 child
= doc
.GetRoot()->GetChildren();
1087 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
1089 // only register public&protected methods
1090 if ((acc
== "public" || acc
== "protected") &&
1091 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
1093 unsigned long id
= 0;
1094 if (!getID(&id
, child
->GetAttribute("id"))) {
1095 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
1099 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
1100 if (it
!= members
.end())
1102 wxClass
*p
= it
->second
;
1104 // this <Method> node is a method of the i-th class!
1106 if (!ParseMethod(child
, types
, newfunc
)) {
1107 LogError("The method '%s' could not be added to class '%s'",
1108 child
->GetAttribute("demangled"), p
->GetName());
1112 // do some additional check that we can do only here:
1114 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
1115 LogError("The method '%s' does not seem to be a ctor for '%s'",
1116 newfunc
.GetName(), p
->GetName());
1119 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
1120 LogError("The method '%s' does not seem to be a dtor for '%s'",
1121 newfunc
.GetName(), p
->GetName());
1125 p
->AddMethod(newfunc
);
1129 child
= child
->GetNext();
1131 // give feedback to the user about the progress...
1132 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1135 if (!CheckParseResults())
1141 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1142 const wxTypeIdHashMap
& types
,
1145 // get the real name
1146 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1147 if (p
->GetName() == "Destructor")
1149 else if (p
->GetName() == "OperatorMethod")
1150 name
= "operator" + name
;
1152 // resolve return type
1154 unsigned long retid
= 0;
1155 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1157 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1158 LogError("Empty return ID for method '%s', with ID '%s'",
1159 name
, p
->GetAttribute("id"));
1165 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1166 if (retidx
== types
.end()) {
1167 LogError("Could not find return type ID '%s'", retid
);
1171 ret
= wxType(retidx
->second
);
1173 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1174 retidx
->second
, name
, p
->GetAttribute("id"));
1179 // resolve argument types
1180 wxArgumentTypeArray argtypes
;
1181 wxXmlNode
*arg
= p
->GetChildren();
1184 if (arg
->GetName() == "Argument")
1186 unsigned long id
= 0;
1187 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1188 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1189 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1193 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1194 if (idx
== types
.end()) {
1195 LogError("Could not find argument type ID '%s'", id
);
1199 argtypes
.Add(wxArgumentType(idx
->second
,
1200 arg
->GetAttribute("default"),
1201 arg
->GetAttribute("name")));
1204 arg
= arg
->GetNext();
1207 m
.SetReturnType(ret
);
1209 m
.SetArgumentTypes(argtypes
);
1210 m
.SetConst(p
->GetAttribute("const") == "1");
1211 m
.SetStatic(p
->GetAttribute("static") == "1");
1213 // NOTE: gccxml is smart enough to mark as virtual those functions
1214 // which are declared virtual in base classes but don't have
1215 // the "virtual" keyword explicitely indicated in the derived
1216 // classes... so we don't need any further logic for virtuals
1218 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1219 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1220 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1222 // decode access specifier
1223 if (p
->GetAttribute("access") == "public")
1224 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1225 else if (p
->GetAttribute("access") == "protected")
1226 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1227 else if (p
->GetAttribute("access") == "private")
1228 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1231 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1240 // ----------------------------------------------------------------------------
1241 // wxXmlDoxygenInterface global helpers
1242 // ----------------------------------------------------------------------------
1244 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1248 // consider the tree
1250 // <a><b>this</b> is a <b>string</b></a>
1259 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1260 // this function returns "this is a string"
1262 wxXmlNode
*ref
= n
->GetChildren();
1264 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1265 text
+= ref
->GetNodeContent();
1266 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1267 text
+= ref
->GetContent();
1269 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1271 ref
= ref
->GetNext();
1277 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1282 wxXmlNode
*p
= parent
->GetChildren();
1285 switch (p
->GetType())
1287 case wxXML_TEXT_NODE
:
1288 if (p
->GetContent() == name
)
1292 case wxXML_ELEMENT_NODE
:
1293 // recurse into this node...
1294 if (HasTextNodeContaining(p
, name
))
1309 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1314 const wxXmlNode
*p
= parent
->GetChildren();
1317 if (p
->GetName() == name
)
1320 // search recursively in the children of this node
1321 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1331 int GetAvailabilityFor(const wxXmlNode
*node
)
1333 // identify <onlyfor> custom XML tags
1334 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1336 return wxPORT_UNKNOWN
;
1338 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1339 int nAvail
= wxPORT_UNKNOWN
;
1340 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1342 if (!ports
[i
].StartsWith("wx")) {
1343 LogError("unexpected port ID '%s'", ports
[i
]);
1347 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1354 // ----------------------------------------------------------------------------
1355 // wxXmlDoxygenInterface
1356 // ----------------------------------------------------------------------------
1358 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1360 wxXmlDocument index
;
1361 wxXmlNode
*compound
;
1363 LogMessage("Parsing %s...", filename
);
1365 if (!index
.Load(filename
)) {
1366 LogError("can't load %s", filename
);
1370 // start processing the index:
1371 if (index
.GetRoot()->GetName() != "doxygenindex") {
1372 LogError("invalid root node for %s", filename
);
1377 NB: we may need in future to do a version-check here if the
1378 format of the XML generated by doxygen changes.
1379 For now (doxygen version 1.5.5), this check is not required
1380 since AFAIK the XML format never changed since it was introduced.
1383 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1385 // process files referenced by this index file
1386 compound
= index
.GetRoot()->GetChildren();
1389 if (compound
->GetName() == "compound" &&
1390 compound
->GetAttribute("kind") == "class")
1392 wxString refid
= compound
->GetAttribute("refid");
1394 wxFileName
fn(filename
);
1395 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1399 compound
= compound
->GetNext();
1403 if (!CheckParseResults())
1409 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1411 wxClassMemberIdHashMap parents
;
1417 LogMessage("Parsing %s...", filename
);
1419 if (!doc
.Load(filename
)) {
1420 LogError("can't load %s", filename
);
1424 // start processing this compound definition XML
1425 if (doc
.GetRoot()->GetName() != "doxygen") {
1426 LogError("invalid root node for %s", filename
);
1430 // build a list of wx classes
1431 child
= doc
.GetRoot()->GetChildren();
1434 if (child
->GetName() == "compounddef" &&
1435 child
->GetAttribute("kind") == "class")
1439 wxString absoluteFile
, header
;
1441 wxXmlNode
*subchild
= child
->GetChildren();
1444 wxString kind
= subchild
->GetAttribute("kind");
1446 // parse only public&protected functions:
1447 if (subchild
->GetName() == "sectiondef" &&
1448 (kind
== "public-func" || kind
== "protected-func"))
1451 wxXmlNode
*membernode
= subchild
->GetChildren();
1454 if (membernode
->GetName() == "memberdef" &&
1455 membernode
->GetAttribute("kind") == "function")
1459 if (!ParseMethod(membernode
, m
, header
)) {
1460 LogError("The method '%s' could not be added to class '%s'",
1461 m
.GetName(), klass
.GetName());
1465 if (kind
== "public-func")
1466 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1467 else if (kind
== "protected-func")
1468 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1469 else if (kind
== "private-func")
1470 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1472 if (absoluteFile
.IsEmpty())
1473 absoluteFile
= header
;
1474 else if (header
!= absoluteFile
)
1476 LogError("The method '%s' is documented in a different "
1477 "file from others (which belong to '%s') ?",
1478 header
, absoluteFile
);
1485 membernode
= membernode
->GetNext();
1488 // all methods of this class were taken from the header "absoluteFile":
1489 klass
.SetHeader(absoluteFile
);
1491 else if (subchild
->GetName() == "compoundname")
1493 klass
.SetName(subchild
->GetNodeContent());
1495 /*else if (subchild->GetName() == "includes")
1497 // NOTE: we'll get the header from the <location> tags
1498 // scattered inside <memberdef> tags instead of
1499 // this <includes> tag since it does not contain
1500 // the absolute path of the header
1502 klass.SetHeader(subchild->GetNodeContent());
1504 else if (subchild
->GetName() == "detaileddescription")
1506 // identify <onlyfor> custom XML tags
1507 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1509 else if (subchild
->GetName() == "basecompoundref")
1511 // add the name of this parent to the list of klass' parents
1512 klass
.AddParent(subchild
->GetNodeContent());
1515 subchild
= subchild
->GetNext();
1520 m_classes
.Add(klass
);
1522 LogWarning("discarding class '%s' with %d methods...",
1523 klass
.GetName(), klass
.GetMethodCount());
1526 child
= child
->GetNext();
1528 // give feedback to the user about the progress...
1529 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1535 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1537 wxArgumentTypeArray args
;
1540 wxXmlNode
*child
= p
->GetChildren();
1543 if (child
->GetName() == "name")
1544 m
.SetName(child
->GetNodeContent());
1545 else if (child
->GetName() == "type")
1546 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1547 else if (child
->GetName() == "param")
1549 wxString typestr
, namestr
, defstr
, arrstr
;
1550 wxXmlNode
*n
= child
->GetChildren();
1553 if (n
->GetName() == "type")
1554 // if the <type> node has children, they should be all TEXT and <ref> nodes
1555 // and we need to take the text they contain, in the order they appear
1556 typestr
= GetTextFromChildren(n
);
1557 else if (n
->GetName() == "declname")
1558 namestr
= GetTextFromChildren(n
);
1559 else if (n
->GetName() == "defval")
1560 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1561 else if (n
->GetName() == "array")
1562 arrstr
= GetTextFromChildren(n
);
1567 if (typestr
.IsEmpty()) {
1568 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1572 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1574 // can we use preprocessor output to transform the default value
1575 // into the same form which gets processed by wxXmlGccInterface?
1576 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1577 if (it
!= m_preproc
.end())
1578 newarg
.SetDefaultValue(defstr
, it
->second
);
1582 else if (child
->GetName() == "location")
1585 if (child
->GetAttribute("line").ToLong(&line
))
1586 m
.SetLocation((int)line
);
1587 header
= child
->GetAttribute("file");
1589 else if (child
->GetName() == "detaileddescription")
1591 // when a method has a @deprecated tag inside its description,
1592 // Doxygen outputs somewhere nested inside <detaileddescription>
1593 // a <xreftitle>Deprecated</xreftitle> tag.
1594 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1596 // identify <onlyfor> custom XML tags
1597 m
.SetAvailability(GetAvailabilityFor(child
));
1600 child
= child
->GetNext();
1603 m
.SetArgumentTypes(args
);
1604 m
.SetConst(p
->GetAttribute("const")=="yes");
1605 m
.SetStatic(p
->GetAttribute("static")=="yes");
1607 // NOTE: Doxygen is smart enough to mark as virtual those functions
1608 // which are declared virtual in base classes but don't have
1609 // the "virtual" keyword explicitely indicated in the derived
1610 // classes... so we don't need any further logic for virtuals
1612 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1613 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1616 LogError("The prototype '%s' is not valid!", m
.GetAsString());