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())
124 LogMessage("Type '%s' does not match type '%s'", m_strType
, m
.m_strType
);
130 // ----------------------------------------------------------------------------
132 // ----------------------------------------------------------------------------
134 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
136 m_strDefaultValue
= defval
.Strip(wxString::both
);
137 m_strDefaultValueForCmp
= defvalForCmp
.IsEmpty() ? m_strDefaultValue
: defvalForCmp
.Strip(wxString::both
);
139 // adjust aesthetic form of DefaultValue for the modify mode of ifacecheck:
140 // we may need to write it out in an interface header
141 if (m_strDefaultValue
== "0u")
142 m_strDefaultValue
= "0";
144 // in order to make valid&simple comparison on argument defaults,
145 // we reduce some of the multiple forms in which the same things may appear
147 if (m_strDefaultValueForCmp
== "0u")
148 m_strDefaultValueForCmp
= "0";
150 m_strDefaultValue
.Replace("0x000000001", "1");
151 m_strDefaultValueForCmp
.Replace("0x000000001", "1");
153 // fix for unicode strings:
154 m_strDefaultValue
.Replace("\\000\\000\\000", "");
155 m_strDefaultValueForCmp
.Replace("\\000\\000\\000", "");
157 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
158 m_strDefaultValueForCmp
.EndsWith(")"))
160 // get rid of the wxT() part
161 unsigned int len
= m_strDefaultValueForCmp
.Len();
162 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
167 m_strDefaultValueForCmp.Replace("0", "NULL");
169 m_strDefaultValueForCmp.Replace("NULL", "0");
172 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
173 // fix this to avoid false positives
174 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
175 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
176 m_strDefaultValueForCmp
.Replace("wxHelpEvent::", ""); // same story for some other classes
178 m_strDefaultValueForCmp
.Replace("wxGet_wxConvLocal()", "wxConvLocal");
180 m_strDefaultValueForCmp
.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
183 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
184 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
187 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
189 if ((const wxType
&)(*this) != (const wxType
&)m
)
193 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
194 // numbers; avoid false positives in this case!
195 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
196 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
199 // fix for default values which were replaced by gcc-xml with their numeric values
200 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
201 if (m_strTypeClean
== "long" || m_strTypeClean
== "int")
203 if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) ||
204 (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx")))
207 LogMessage("Supposing '%s' default value to be the same of '%s'...",
208 m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
);
214 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
216 // maybe the default values are numbers.
217 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
218 // To handle these cases, we try to convert the default value strings to numbers:
219 long def1val
, def2val
;
220 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
221 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
223 if (def1val
== def2val
)
224 return true; // the default values match
228 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
229 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
233 // we deliberately avoid checks on the argument name
239 // ----------------------------------------------------------------------------
241 // ----------------------------------------------------------------------------
243 bool wxMethod::IsOk() const
245 // NOTE: m_retType can be a wxEmptyType, and means that this method
246 // is a ctor or a dtor.
247 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
248 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
252 if (m_strName
.IsEmpty())
255 // a function can't be both const and static or virtual and static!
256 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
257 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
261 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
263 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
264 if (!m_args
[i
].IsOk()) {
265 LogError("'%s' method has invalid %d-th argument type: %s",
266 m_strName
, i
+1, m_args
[i
].GetAsString());
270 // NB: the default value of the arguments can contain pretty much everything
271 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
272 // so we don't do any test on their contents
273 if (m_args
.GetCount()>0)
275 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
276 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
278 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
279 LogError("'%s' method has %d-th argument which has no default value "
280 "(while the previous one had one!)",
285 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
292 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
294 if (GetReturnType() != m
.GetReturnType() ||
295 GetName() != m
.GetName())
298 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
300 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
301 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
305 // compare argument types
306 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
307 if (m_args
[i
] != m
.m_args
[i
])
313 bool wxMethod::operator==(const wxMethod
& m
) const
316 if (IsConst() != m
.IsConst() ||
317 IsStatic() != m
.IsStatic() ||
318 IsVirtual() != m
.IsVirtual() ||
319 IsPureVirtual() != m
.IsPureVirtual() ||
320 IsDeprecated() != m
.IsDeprecated() ||
321 GetAccessSpecifier() != m
.GetAccessSpecifier())
324 // check everything else
325 return MatchesExceptForAttributes(m
);
328 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
329 bool bDeprecated
, bool bAccessSpec
) const
333 // NOTE: for return and argument types, never use wxType::GetAsCleanString
334 // since in that way we'd miss important decorators like &,*,const etc
336 if (m_retType
!=wxEmptyType
)
337 ret
+= m_retType
.GetAsString() + " ";
338 //else; this is a ctor or dtor
340 ret
+= m_strName
+ "(";
342 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
344 ret
+= m_args
[i
].GetAsString();
346 const wxString
& name
= m_args
[i
].GetArgumentName();
347 if (bWithArgumentNames
&& !name
.IsEmpty())
350 const wxString
& def
= bCleanDefaultValues
?
351 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
358 if (m_args
.GetCount()>0)
359 ret
= ret
.Left(ret
.Len()-2);
366 ret
= "static " + ret
;
367 if (m_bVirtual
|| m_bPureVirtual
)
368 ret
= "virtual " + ret
;
371 if (m_bDeprecated
&& bDeprecated
)
372 ret
+= " [deprecated]";
381 case wxMAS_PROTECTED
:
382 ret
+= " [protected]";
393 void wxMethod::Dump(wxTextOutputStream
& stream
) const
395 stream
<< "[" + m_retType
.GetAsString() + "]";
396 stream
<< "[" + m_strName
+ "]";
398 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
399 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
400 "=" + m_args
[i
].GetDefaultValue() + "]";
407 stream
<< " VIRTUAL";
409 stream
<< " PURE-VIRTUAL";
411 stream
<< " DEPRECATED";
416 // ----------------------------------------------------------------------------
418 // ----------------------------------------------------------------------------
420 wxString
wxClass::GetNameWithoutTemplate() const
422 // NB: I'm not sure this is the right terminology for this function!
424 if (m_strName
.Contains("<"))
425 return m_strName
.Left(m_strName
.Find("<"));
429 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
431 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
432 // named wxWritableCharTypeBuffer, without the <...> part!
434 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
440 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
442 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
448 void wxClass::Dump(wxTextOutputStream
& out
) const
450 out
<< m_strName
+ "\n";
452 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
454 // dump all our methods
456 m_methods
[i
].Dump(out
);
463 bool wxClass::CheckConsistency() const
465 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
466 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
467 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
469 LogError("class %s has two methods with the same prototype: '%s'",
470 m_strName
, m_methods
[i
].GetAsString());
472 ((wxClass
*)this)->m_methods
.RemoveAt(j
);
479 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
481 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
482 if (m_methods
[i
] == m
)
483 return &m_methods
[i
];
487 const wxMethod
* wxClass::RecursiveUpwardFindMethod(const wxMethod
& m
,
488 const wxXmlInterface
* allclasses
) const
490 // first, search into *this
491 const wxMethod
* ret
= FindMethod(m
);
495 // then, search into its parents
496 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
498 // ignore non-wx-classes parents
499 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
500 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
502 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
504 LogError("Could not find parent '%s' of class '%s'...",
505 m_parents
[i
], GetName());
509 const wxMethod
*parentMethod
= parent
->RecursiveUpwardFindMethod(m
, allclasses
);
515 // could not find anything even in parent classes...
519 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
521 wxMethodPtrArray ret
;
523 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
524 if (m_methods
[i
].GetName() == name
)
525 ret
.Add(&m_methods
[i
]);
531 wxMethodPtrArray
wxClass::RecursiveUpwardFindMethodsNamed(const wxString
& name
,
532 const wxXmlInterface
* allclasses
) const
534 // first, search into *this
535 wxMethodPtrArray ret
= FindMethodsNamed(name
);
536 if (ret
.GetCount()>0)
537 return ret
; // stop here, don't look upward in the parents
539 // then, search into parents of this class
540 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
542 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
543 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
545 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
547 LogError("Could not find parent '%s' of class '%s'...",
548 m_parents
[i
], GetName());
552 wxMethodPtrArray temp
= parent
->RecursiveUpwardFindMethodsNamed(name
, allclasses
);
553 WX_APPEND_ARRAY(ret
, temp
);
562 // ----------------------------------------------------------------------------
564 // ----------------------------------------------------------------------------
566 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
568 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
570 // sort alphabetically
571 return item1
->GetName().Cmp(item2
->GetName());
574 void wxXmlInterface::Dump(const wxString
& filename
)
576 wxFFileOutputStream
apioutput( filename
);
577 wxTextOutputStream
apiout( apioutput
);
579 // dump the classes in alphabetical order
580 wxSortedClassArray
sorted(CompareWxClassObjects
);
581 sorted
.Alloc(m_classes
.GetCount());
582 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
583 sorted
.Add(&m_classes
[i
]);
585 // now they have been sorted
586 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
587 sorted
[i
]->Dump(apiout
);
590 bool wxXmlInterface::CheckParseResults() const
592 // this check can be quite slow, so do it only for debug releases:
594 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
595 if (!m_classes
[i
].CheckConsistency())
602 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
606 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
607 if (m_classes
[i
].GetHeader() == headerfile
)
608 ret
.Add(&m_classes
[i
]);
614 // ----------------------------------------------------------------------------
615 // wxXmlGccInterface helper declarations
616 // ----------------------------------------------------------------------------
618 // or-able flags for a toResolveTypeItem->attrib:
619 #define ATTRIB_CONST 1
620 #define ATTRIB_REFERENCE 2
621 #define ATTRIB_POINTER 4
622 #define ATTRIB_ARRAY 8
624 // it may sound strange but gccxml, in order to produce shorter ID names
625 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
626 // in order to be able to translate such strings into numbers using strtoul()
627 // we use as base 10 (possible digits) + 25 (possible characters) = 35
628 #define GCCXML_BASE 35
630 class toResolveTypeItem
633 toResolveTypeItem() { attribs
=0; }
634 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
635 : ref(refID
), attribs(attribint
) {}
637 unsigned long ref
, // the referenced type's ID
638 attribs
; // the attributes of this reference
643 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
644 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
645 wxIntegerHash
, wxIntegerEqual
,
646 wxToResolveTypeHashMap
);
648 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
649 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
650 wxIntegerHash
, wxIntegerEqual
,
651 wxClassMemberIdHashMap
);
655 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
659 // utility to parse gccXML ID values;
660 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
661 // but is a little bit faster
662 bool getID(unsigned long *id
, const wxStringCharType
* str
)
664 wxStringCharType
*end
;
665 #if wxUSE_UNICODE_WCHAR
666 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
668 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
671 // return true only if scan was stopped by the terminating NUL and
672 // if the string was not empty to start with and no under/overflow
674 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
681 // utility specialized to parse efficiently the gccXML list of IDs which occur
682 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
683 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
685 #if wxUSE_UNICODE_WCHAR
686 size_t len
= wcslen(str
);
688 size_t len
= strlen(str
);
691 if (len
== 0 || str
[0] != '_')
694 const wxStringCharType
*curpos
= str
,
696 wxStringCharType
*nexttoken
;
700 // curpos always points to the underscore of the next token to parse:
701 #if wxUSE_UNICODE_WCHAR
702 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
704 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
706 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
709 // advance current position
710 curpos
= nexttoken
+ 1;
712 // add this ID to the hashmap
713 wxClassMemberIdHashMap::value_type
v(id
, p
);
721 // ----------------------------------------------------------------------------
723 // ----------------------------------------------------------------------------
725 bool wxXmlGccInterface::Parse(const wxString
& filename
)
731 LogMessage("Parsing %s...", filename
);
733 if (!doc
.Load(filename
)) {
734 LogError("can't load %s", filename
);
738 // start processing the XML file
739 if (doc
.GetRoot()->GetName() != "GCC_XML") {
740 LogError("invalid root node for %s", filename
);
744 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
747 #define MIN_REVISION 120
749 if (!version
.StartsWith("1."))
753 unsigned long rev
= 0;
754 if (!version
.Mid(2).ToULong(&rev
))
757 if (rev
< MIN_REVISION
)
763 LogError("The version of GCC-XML used for the creation of %s is too old; "
764 "the cvs_revision attribute of the root node reports '%s', "
765 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
769 wxToResolveTypeHashMap toResolveTypes
;
770 wxClassMemberIdHashMap members
;
771 wxTypeIdHashMap types
;
772 wxTypeIdHashMap files
;
773 wxTypeIdHashMap typedefs
;
775 // prealloc quite a lot of memory!
776 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
778 // build a list of wx classes and in general of all existent types
779 child
= doc
.GetRoot()->GetChildren();
782 const wxString
& n
= child
->GetName();
784 unsigned long id
= 0;
785 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
787 // NOTE: <File> nodes can have an id == "f0"...
789 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
795 wxString cname
= child
->GetAttribute("name");
796 if (cname
.IsEmpty()) {
797 LogError("Invalid empty name for '%s' node", n
);
801 // only register wx classes (do remember also the IDs of their members)
802 if (cname
.StartsWith("wx"))
804 // NB: "file" attribute contains an ID value that we'll resolve later
805 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
807 // the just-inserted class:
808 wxClass
*newClass
= &m_classes
.Last();
810 // now get a list of the base classes:
811 wxXmlNode
*baseNode
= child
->GetChildren();
814 // for now we store as "parents" only the parent IDs...
815 // later we will resolve them into full class names
816 if (baseNode
->GetName() == "Base")
817 newClass
->AddParent(baseNode
->GetAttribute("type"));
819 baseNode
= baseNode
->GetNext();
822 const wxString
& ids
= child
->GetAttribute("members");
825 if (child
->GetAttribute("incomplete") != "1") {
826 LogError("Invalid member IDs for '%s' class node: %s",
827 cname
, child
->GetAttribute("id"));
830 //else: don't warn the user; it looks like "incomplete" classes
831 // never have any member...
835 // decode the non-empty list of IDs:
836 if (!getMemberIDs(&members
, newClass
, ids
)) {
837 LogError("Invalid member IDs for '%s' class node: %s",
838 cname
, child
->GetAttribute("id"));
844 // register this class also as possible return/argument type:
847 else if (n
== "Typedef")
849 unsigned long typeId
= 0;
850 if (!getID(&typeId
, child
->GetAttribute("type"))) {
851 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
855 // this typedef node tell us that every type referenced with the
856 // "typeId" ID should be called with another name:
857 wxString name
= child
->GetAttribute("name");
859 // save this typedef in a separate hashmap...
860 typedefs
[typeId
] = name
;
864 else if (n
== "PointerType" || n
== "ReferenceType" ||
865 n
== "CvQualifiedType" || n
== "ArrayType")
867 unsigned long type
= 0;
868 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
869 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
873 unsigned long attr
= 0;
874 if (n
== "PointerType")
875 attr
= ATTRIB_POINTER
;
876 else if (n
== "ReferenceType")
877 attr
= ATTRIB_REFERENCE
;
878 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
880 else if (n
== "ArrayType")
883 // these nodes make reference to other types... we'll resolve them later
884 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
886 else if (n
== "FunctionType" || n
== "MethodType")
889 TODO: parsing FunctionType and MethodType nodes is not as easy
890 as for other "simple" types.
894 wxXmlNode
*arg
= child
->GetChildren();
897 if (arg
->GetName() == "Argument")
898 argstr
+= arg
->GetAttribute("type") + ", ";
899 arg
= arg
->GetNext();
902 if (argstr
.Len() > 0)
903 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
905 // these nodes make reference to other types... we'll resolve them later
906 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
907 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
909 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
910 // to a typedef later...
912 else if (n
== "File")
914 if (!child
->GetAttribute("id").StartsWith("f")) {
915 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
919 // just ignore this node... all file IDs/names were already parsed
920 files
[id
] = child
->GetAttribute("name");
924 // we register everything else as a possible return/argument type:
925 const wxString
& name
= child
->GetAttribute("name");
930 //typeNames.Add(name);
935 // this may happen with unnamed structs/union, special ctors,
936 // or other exotic things which we are not interested to, since
937 // they're never used as return/argument types by wxWidgets methods
940 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
941 n
, child
->GetAttribute("id"));
947 child
= child
->GetNext();
949 // give feedback to the user about the progress...
950 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
953 // some nodes with IDs referenced by methods as return/argument types, do reference
954 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
955 // thus we need to resolve their name iteratively:
956 while (toResolveTypes
.size()>0)
959 LogMessage("%d types were collected; %d types need yet to be resolved...",
960 types
.size(), toResolveTypes
.size());
962 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
963 i
!= toResolveTypes
.end();)
965 unsigned long id
= i
->first
;
966 unsigned long referenced
= i
->second
.ref
;
968 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
969 if (primary
!= types
.end())
971 // this to-resolve-type references a "primary" type
973 wxString newtype
= primary
->second
;
974 int attribs
= i
->second
.attribs
;
976 // attribs may contain a combination of ATTRIB_* flags:
977 if (attribs
& ATTRIB_CONST
)
978 newtype
= "const " + newtype
;
979 if (attribs
& ATTRIB_REFERENCE
)
980 newtype
= newtype
+ "&";
981 if (attribs
& ATTRIB_POINTER
)
982 newtype
= newtype
+ "*";
983 if (attribs
& ATTRIB_ARRAY
)
984 newtype
= newtype
+ "[]";
986 // add the resolved type to the list of "primary" types
987 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
988 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
992 // this one has been resolved; erase it through its iterator!
993 toResolveTypes
.erase(i
);
995 // now iterator i is invalid; assign it again to the beginning
996 i
= toResolveTypes
.begin();
1000 // then search in the referenced types themselves:
1001 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
1002 if (idx2
!= toResolveTypes
.end())
1004 // merge this to-resolve-type with the idx2->second type
1005 i
->second
.ref
= idx2
->second
.ref
;
1006 i
->second
.attribs
|= idx2
->second
.attribs
;
1008 // this type will eventually be solved in the next while() iteration
1013 LogError("Cannot solve '%d' reference type!", referenced
);
1020 // resolve header names
1021 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1023 unsigned long fileID
= 0;
1024 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
1025 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
1030 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
1031 if (idx
== files
.end())
1033 // this is an error!
1034 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
1037 m_classes
[i
].SetHeader(idx
->second
);
1040 // resolve parent names
1041 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1043 for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++)
1047 if (!getID(&id
, m_classes
[i
].GetParent(k
))) {
1048 LogError("invalid parent class ID for '%s'", m_classes
[i
].GetName());
1052 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1053 if (idx
== types
.end())
1055 // this is an error!
1056 LogError("couldn't find parent class ID '%d'", id
);
1059 // replace k-th parent with its true name:
1060 m_classes
[i
].SetParent(k
, idx
->second
);
1064 // build the list of the wx methods
1065 child
= doc
.GetRoot()->GetChildren();
1068 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
1070 // only register public&protected methods
1071 if ((acc
== "public" || acc
== "protected") &&
1072 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
1074 unsigned long id
= 0;
1075 if (!getID(&id
, child
->GetAttribute("id"))) {
1076 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
1080 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
1081 if (it
!= members
.end())
1083 wxClass
*p
= it
->second
;
1085 // this <Method> node is a method of the i-th class!
1087 if (!ParseMethod(child
, types
, newfunc
)) {
1088 LogError("The method '%s' could not be added to class '%s'",
1089 child
->GetAttribute("demangled"), p
->GetName());
1093 // do some additional check that we can do only here:
1095 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
1096 LogError("The method '%s' does not seem to be a ctor for '%s'",
1097 newfunc
.GetName(), p
->GetName());
1100 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
1101 LogError("The method '%s' does not seem to be a dtor for '%s'",
1102 newfunc
.GetName(), p
->GetName());
1106 p
->AddMethod(newfunc
);
1110 child
= child
->GetNext();
1112 // give feedback to the user about the progress...
1113 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1116 if (!CheckParseResults())
1122 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1123 const wxTypeIdHashMap
& types
,
1126 // get the real name
1127 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1128 if (p
->GetName() == "Destructor")
1130 else if (p
->GetName() == "OperatorMethod")
1131 name
= "operator" + name
;
1133 // resolve return type
1135 unsigned long retid
= 0;
1136 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1138 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1139 LogError("Empty return ID for method '%s', with ID '%s'",
1140 name
, p
->GetAttribute("id"));
1146 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1147 if (retidx
== types
.end()) {
1148 LogError("Could not find return type ID '%s'", retid
);
1152 ret
= wxType(retidx
->second
);
1154 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1155 retidx
->second
, name
, p
->GetAttribute("id"));
1160 // resolve argument types
1161 wxArgumentTypeArray argtypes
;
1162 wxXmlNode
*arg
= p
->GetChildren();
1165 if (arg
->GetName() == "Argument")
1167 unsigned long id
= 0;
1168 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1169 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1170 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1174 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1175 if (idx
== types
.end()) {
1176 LogError("Could not find argument type ID '%s'", id
);
1180 argtypes
.Add(wxArgumentType(idx
->second
,
1181 arg
->GetAttribute("default"),
1182 arg
->GetAttribute("name")));
1185 arg
= arg
->GetNext();
1188 m
.SetReturnType(ret
);
1190 m
.SetArgumentTypes(argtypes
);
1191 m
.SetConst(p
->GetAttribute("const") == "1");
1192 m
.SetStatic(p
->GetAttribute("static") == "1");
1194 // NOTE: gccxml is smart enough to mark as virtual those functions
1195 // which are declared virtual in base classes but don't have
1196 // the "virtual" keyword explicitely indicated in the derived
1197 // classes... so we don't need any further logic for virtuals
1199 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1200 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1201 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1203 // decode access specifier
1204 if (p
->GetAttribute("access") == "public")
1205 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1206 else if (p
->GetAttribute("access") == "protected")
1207 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1208 else if (p
->GetAttribute("access") == "private")
1209 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1212 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1221 // ----------------------------------------------------------------------------
1222 // wxXmlDoxygenInterface global helpers
1223 // ----------------------------------------------------------------------------
1225 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1229 // consider the tree
1231 // <a><b>this</b> is a <b>string</b></a>
1240 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1241 // this function returns "this is a string"
1243 wxXmlNode
*ref
= n
->GetChildren();
1245 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1246 text
+= ref
->GetNodeContent();
1247 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1248 text
+= ref
->GetContent();
1250 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1252 ref
= ref
->GetNext();
1258 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1263 wxXmlNode
*p
= parent
->GetChildren();
1266 switch (p
->GetType())
1268 case wxXML_TEXT_NODE
:
1269 if (p
->GetContent() == name
)
1273 case wxXML_ELEMENT_NODE
:
1274 // recurse into this node...
1275 if (HasTextNodeContaining(p
, name
))
1290 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1295 const wxXmlNode
*p
= parent
->GetChildren();
1298 if (p
->GetName() == name
)
1301 // search recursively in the children of this node
1302 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1312 int GetAvailabilityFor(const wxXmlNode
*node
)
1314 // identify <onlyfor> custom XML tags
1315 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1317 return wxPORT_UNKNOWN
;
1319 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1320 int nAvail
= wxPORT_UNKNOWN
;
1321 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1323 if (!ports
[i
].StartsWith("wx")) {
1324 LogError("unexpected port ID '%s'", ports
[i
]);
1328 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1335 // ----------------------------------------------------------------------------
1336 // wxXmlDoxygenInterface
1337 // ----------------------------------------------------------------------------
1339 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1341 wxXmlDocument index
;
1342 wxXmlNode
*compound
;
1344 LogMessage("Parsing %s...", filename
);
1346 if (!index
.Load(filename
)) {
1347 LogError("can't load %s", filename
);
1351 // start processing the index:
1352 if (index
.GetRoot()->GetName() != "doxygenindex") {
1353 LogError("invalid root node for %s", filename
);
1358 NB: we may need in future to do a version-check here if the
1359 format of the XML generated by doxygen changes.
1360 For now (doxygen version 1.5.5), this check is not required
1361 since AFAIK the XML format never changed since it was introduced.
1364 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1366 // process files referenced by this index file
1367 compound
= index
.GetRoot()->GetChildren();
1370 if (compound
->GetName() == "compound" &&
1371 compound
->GetAttribute("kind") == "class")
1373 wxString refid
= compound
->GetAttribute("refid");
1375 wxFileName
fn(filename
);
1376 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1380 compound
= compound
->GetNext();
1384 if (!CheckParseResults())
1390 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1392 wxClassMemberIdHashMap parents
;
1398 LogMessage("Parsing %s...", filename
);
1400 if (!doc
.Load(filename
)) {
1401 LogError("can't load %s", filename
);
1405 // start processing this compound definition XML
1406 if (doc
.GetRoot()->GetName() != "doxygen") {
1407 LogError("invalid root node for %s", filename
);
1411 // build a list of wx classes
1412 child
= doc
.GetRoot()->GetChildren();
1415 if (child
->GetName() == "compounddef" &&
1416 child
->GetAttribute("kind") == "class")
1420 wxString absoluteFile
, header
;
1422 wxXmlNode
*subchild
= child
->GetChildren();
1425 wxString kind
= subchild
->GetAttribute("kind");
1427 // parse only public&protected functions:
1428 if (subchild
->GetName() == "sectiondef" &&
1429 (kind
== "public-func" || kind
== "protected-func"))
1432 wxXmlNode
*membernode
= subchild
->GetChildren();
1435 if (membernode
->GetName() == "memberdef" &&
1436 membernode
->GetAttribute("kind") == "function")
1440 if (!ParseMethod(membernode
, m
, header
)) {
1441 LogError("The method '%s' could not be added to class '%s'",
1442 m
.GetName(), klass
.GetName());
1446 if (kind
== "public-func")
1447 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1448 else if (kind
== "protected-func")
1449 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1450 else if (kind
== "private-func")
1451 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1453 if (absoluteFile
.IsEmpty())
1454 absoluteFile
= header
;
1455 else if (header
!= absoluteFile
)
1457 LogError("The method '%s' is documented in a different "
1458 "file from others (which belong to '%s') ?",
1459 header
, absoluteFile
);
1466 membernode
= membernode
->GetNext();
1469 // all methods of this class were taken from the header "absoluteFile":
1470 klass
.SetHeader(absoluteFile
);
1472 else if (subchild
->GetName() == "compoundname")
1474 klass
.SetName(subchild
->GetNodeContent());
1476 /*else if (subchild->GetName() == "includes")
1478 // NOTE: we'll get the header from the <location> tags
1479 // scattered inside <memberdef> tags instead of
1480 // this <includes> tag since it does not contain
1481 // the absolute path of the header
1483 klass.SetHeader(subchild->GetNodeContent());
1485 else if (subchild
->GetName() == "detaileddescription")
1487 // identify <onlyfor> custom XML tags
1488 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1490 else if (subchild
->GetName() == "basecompoundref")
1492 // add the name of this parent to the list of klass' parents
1493 klass
.AddParent(subchild
->GetNodeContent());
1496 subchild
= subchild
->GetNext();
1501 m_classes
.Add(klass
);
1503 LogWarning("discarding class '%s' with %d methods...",
1504 klass
.GetName(), klass
.GetMethodCount());
1507 child
= child
->GetNext();
1509 // give feedback to the user about the progress...
1510 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1516 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1518 wxArgumentTypeArray args
;
1521 wxXmlNode
*child
= p
->GetChildren();
1524 if (child
->GetName() == "name")
1525 m
.SetName(child
->GetNodeContent());
1526 else if (child
->GetName() == "type")
1527 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1528 else if (child
->GetName() == "param")
1530 wxString typestr
, namestr
, defstr
, arrstr
;
1531 wxXmlNode
*n
= child
->GetChildren();
1534 if (n
->GetName() == "type")
1535 // if the <type> node has children, they should be all TEXT and <ref> nodes
1536 // and we need to take the text they contain, in the order they appear
1537 typestr
= GetTextFromChildren(n
);
1538 else if (n
->GetName() == "declname")
1539 namestr
= GetTextFromChildren(n
);
1540 else if (n
->GetName() == "defval")
1541 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1542 else if (n
->GetName() == "array")
1543 arrstr
= GetTextFromChildren(n
);
1548 if (typestr
.IsEmpty()) {
1549 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1553 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1555 // can we use preprocessor output to transform the default value
1556 // into the same form which gets processed by wxXmlGccInterface?
1557 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1558 if (it
!= m_preproc
.end())
1559 newarg
.SetDefaultValue(defstr
, it
->second
);
1563 else if (child
->GetName() == "location")
1566 if (child
->GetAttribute("line").ToLong(&line
))
1567 m
.SetLocation((int)line
);
1568 header
= child
->GetAttribute("file");
1570 else if (child
->GetName() == "detaileddescription")
1572 // when a method has a @deprecated tag inside its description,
1573 // Doxygen outputs somewhere nested inside <detaileddescription>
1574 // a <xreftitle>Deprecated</xreftitle> tag.
1575 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1577 // identify <onlyfor> custom XML tags
1578 m
.SetAvailability(GetAvailabilityFor(child
));
1581 child
= child
->GetNext();
1584 m
.SetArgumentTypes(args
);
1585 m
.SetConst(p
->GetAttribute("const")=="yes");
1586 m
.SetStatic(p
->GetAttribute("static")=="yes");
1588 // NOTE: Doxygen is smart enough to mark as virtual those functions
1589 // which are declared virtual in base classes but don't have
1590 // the "virtual" keyword explicitely indicated in the derived
1591 // classes... so we don't need any further logic for virtuals
1593 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1594 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1597 LogError("The prototype '%s' is not valid!", m
.GetAsString());