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", "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);
107 // remove the namespace from the types; there's no problem of conflicts
108 // (except for templates) and this avoids tons of false warnings
109 if (m_strTypeClean
.Contains("::") && !m_strTypeClean
.Contains("<"))
110 m_strTypeClean
= m_strTypeClean
.Mid(m_strTypeClean
.Find("::")+2);
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 wxLogMessage("Type '%s' does not match type '%s'", m_strType
, m
.m_strType
);
139 wxLogMessage(" => TypeClean %s / %s; IsConst %d / %d; IsStatic %d / %d; IsPointer %d / %d; IsReference %d / %d",
140 m_strTypeClean
, m
.m_strTypeClean
, IsConst(), m
.IsConst(),
141 IsStatic(), m
.IsStatic(), IsPointer(), m
.IsPointer(),
142 IsReference(), m
.IsReference());
149 // ----------------------------------------------------------------------------
151 // ----------------------------------------------------------------------------
153 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
155 m_strDefaultValue
= defval
.Strip(wxString::both
);
156 m_strDefaultValueForCmp
= defvalForCmp
.IsEmpty() ?
157 m_strDefaultValue
: defvalForCmp
.Strip(wxString::both
);
160 // clean the default argument strings
161 // ----------------------------------
163 // Note: we adjust the aesthetic form of the m_strDefaultValue string for the "modify mode"
164 // of ifacecheck: we may need to write it out in an interface header
167 for (int i
=0; i
<2; i
++) // to avoid copying&pasting the code!
169 if (i
== 0) p
= &m_strDefaultValue
;
170 if (i
== 1) p
= &m_strDefaultValueForCmp
;
172 if (*p
== "0u") *p
= "0";
174 p
->Replace("0x000000001", "1");
175 p
->Replace("\\000\\000\\000", ""); // fix for unicode strings:
176 p
->Replace("\\011", "\\t");
177 p
->Replace("e+0", "");
178 p
->Replace("2147483647", "__INT_MAX__");
180 // ADHOC-FIX: for wxConv* default values
181 p
->Replace("wxConvAuto(wxFONTENCODING_DEFAULT)", "wxConvAuto()");
182 p
->Replace("wxGet_wxConvUTF8()", "wxConvUTF8");
183 p
->Replace("wxGet_wxConvLocal()", "wxConvLocal");
187 // clean ONLY the default argument string specific for comparison
188 // --------------------------------------------------------------
190 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
191 m_strDefaultValueForCmp
.EndsWith(")"))
193 // get rid of the wxT() part
194 unsigned int len
= m_strDefaultValueForCmp
.Len();
195 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
199 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
200 // fix this to avoid false positives
201 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
202 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
203 m_strDefaultValueForCmp
.Replace("wxHelpEvent::", ""); // same story for some other classes
204 m_strDefaultValueForCmp
.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
207 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
208 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
211 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
213 if ((const wxType
&)(*this) != (const wxType
&)m
)
216 // check if the default values match
217 // ---------------------------------
221 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
222 // numbers; avoid false positives in this case!
223 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
224 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
227 // fix for default values which were replaced by gcc-xml with their numeric values
228 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
229 if (m_strTypeClean
== "long" || m_strTypeClean
== "int")
231 if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) ||
232 (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx")))
235 wxLogMessage("Supposing '%s' default value to be the same of '%s'...",
236 m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
);
241 else if (m_strTypeClean
== "float" || m_strTypeClean
== "double")
242 // gccXML translates the default floating values in a hardly usable
243 // format; e.g. 25.2 => 2.51999999999999992894572642398998141288757324219e+1
244 // we avoid check on these...
247 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
249 // maybe the default values are numbers.
250 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
251 // To handle these cases, we try to convert the default value strings to numbers:
252 long def1val
, def2val
;
253 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
254 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
256 if (def1val
== def2val
)
257 return true; // the default values match
261 wxLogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
262 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
266 // we deliberately avoid checks on the argument name
272 // ----------------------------------------------------------------------------
274 // ----------------------------------------------------------------------------
276 bool wxMethod::IsOk() const
278 // NOTE: m_retType can be a wxEmptyType, and means that this method
279 // is a ctor or a dtor.
280 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
281 wxLogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
285 if (m_strName
.IsEmpty())
288 // a function can't be both const and static or virtual and static!
289 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
290 wxLogError("'%s' method can't be both const/static or virtual/static", m_strName
);
294 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
296 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
297 if (!m_args
[i
].IsOk()) {
298 wxLogError("'%s' method has invalid %d-th argument type: %s",
299 m_strName
, i
+1, m_args
[i
].GetAsString());
303 // NB: the default value of the arguments can contain pretty much everything
304 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
305 // so we don't do any test on their contents
306 if (m_args
.GetCount()>0)
308 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
309 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
311 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
312 wxLogError("'%s' method has %d-th argument which has no default value "
313 "(while the previous one had one!)",
318 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
325 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
327 if (GetReturnType() != m
.GetReturnType() ||
328 GetName() != m
.GetName())
331 wxLogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m
.GetName());
335 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
337 wxLogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
338 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
342 // compare argument types
343 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
344 if (m_args
[i
] != m
.m_args
[i
])
350 bool wxMethod::ActsAsDefaultCtor() const
355 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
356 if (!m_args
[i
].HasDefaultValue())
362 bool wxMethod::operator==(const wxMethod
& m
) const
365 if (IsConst() != m
.IsConst() ||
366 IsStatic() != m
.IsStatic() ||
367 IsVirtual() != m
.IsVirtual() ||
368 IsPureVirtual() != m
.IsPureVirtual() ||
369 IsDeprecated() != m
.IsDeprecated() ||
370 GetAccessSpecifier() != m
.GetAccessSpecifier())
373 wxLogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m
.GetName());
378 // check everything else
379 return MatchesExceptForAttributes(m
);
382 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
383 bool bDeprecated
, bool bAccessSpec
) const
387 // NOTE: for return and argument types, never use wxType::GetAsCleanString
388 // since in that way we'd miss important decorators like &,*,const etc
390 if (m_retType
!=wxEmptyType
)
391 ret
+= m_retType
.GetAsString() + " ";
392 //else; this is a ctor or dtor
394 ret
+= m_strName
+ "(";
396 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
398 ret
+= m_args
[i
].GetAsString();
400 const wxString
& name
= m_args
[i
].GetArgumentName();
401 if (bWithArgumentNames
&& !name
.IsEmpty())
404 const wxString
& def
= bCleanDefaultValues
?
405 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
412 if (m_args
.GetCount()>0)
413 ret
= ret
.Left(ret
.Len()-2);
420 ret
= "static " + ret
;
421 if (m_bVirtual
|| m_bPureVirtual
)
422 ret
= "virtual " + ret
;
425 if (m_bDeprecated
&& bDeprecated
)
426 ret
+= " [deprecated]";
435 case wxMAS_PROTECTED
:
436 ret
+= " [protected]";
447 void wxMethod::Dump(wxTextOutputStream
& stream
) const
449 stream
<< "[" + m_retType
.GetAsString() + "]";
450 stream
<< "[" + m_strName
+ "]";
452 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
453 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
454 "=" + m_args
[i
].GetDefaultValue() + "]";
461 stream
<< " VIRTUAL";
463 stream
<< " PURE-VIRTUAL";
465 stream
<< " DEPRECATED";
470 // ----------------------------------------------------------------------------
472 // ----------------------------------------------------------------------------
474 wxString
wxClass::GetNameWithoutTemplate() const
476 // NB: I'm not sure this is the right terminology for this function!
478 if (m_strName
.Contains("<"))
479 return m_strName
.Left(m_strName
.Find("<"));
483 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
485 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
486 // named wxWritableCharTypeBuffer, without the <...> part!
488 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
494 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
496 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
502 void wxClass::Dump(wxTextOutputStream
& out
) const
504 out
<< m_strName
+ "\n";
506 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
508 // dump all our methods
510 m_methods
[i
].Dump(out
);
517 bool wxClass::CheckConsistency() const
519 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
520 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
521 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
523 wxLogError("class %s has two methods with the same prototype: '%s'",
524 m_strName
, m_methods
[i
].GetAsString());
528 //((wxClass*)this)->m_methods.RemoveAt(j);
535 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
537 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
538 if (m_methods
[i
] == m
)
539 return &m_methods
[i
];
543 const wxMethod
* wxClass::RecursiveUpwardFindMethod(const wxMethod
& m
,
544 const wxXmlInterface
* allclasses
) const
546 // first, search into *this
547 const wxMethod
* ret
= FindMethod(m
);
551 // then, search into its parents
552 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
554 // ignore non-wx-classes parents
555 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
556 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
558 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
560 wxLogError("Could not find parent '%s' of class '%s'...",
561 m_parents
[i
], GetName());
565 const wxMethod
*parentMethod
= parent
->RecursiveUpwardFindMethod(m
, allclasses
);
571 // could not find anything even in parent classes...
575 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
577 wxMethodPtrArray ret
;
579 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
580 if (m_methods
[i
].GetName() == name
)
581 ret
.Add(&m_methods
[i
]);
587 wxMethodPtrArray
wxClass::RecursiveUpwardFindMethodsNamed(const wxString
& name
,
588 const wxXmlInterface
* allclasses
) const
590 // first, search into *this
591 wxMethodPtrArray ret
= FindMethodsNamed(name
);
592 if (ret
.GetCount()>0)
593 return ret
; // stop here, don't look upward in the parents
595 // then, search into parents of this class
596 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
598 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
599 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
601 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
603 wxLogError("Could not find parent '%s' of class '%s'...",
604 m_parents
[i
], GetName());
608 wxMethodPtrArray temp
= parent
->RecursiveUpwardFindMethodsNamed(name
, allclasses
);
609 WX_APPEND_ARRAY(ret
, temp
);
618 // ----------------------------------------------------------------------------
620 // ----------------------------------------------------------------------------
622 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
624 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
626 // sort alphabetically
627 return item1
->GetName().Cmp(item2
->GetName());
630 void wxXmlInterface::Dump(const wxString
& filename
)
632 wxFFileOutputStream
apioutput( filename
);
633 wxTextOutputStream
apiout( apioutput
);
635 // dump the classes in alphabetical order
636 wxSortedClassArray
sorted(CompareWxClassObjects
);
637 sorted
.Alloc(m_classes
.GetCount());
640 for (i
=0; i
<m_classes
.GetCount(); i
++)
641 sorted
.Add(&m_classes
[i
]);
643 // now they have been sorted
644 for (i
=0; i
<sorted
.GetCount(); i
++)
645 sorted
[i
]->Dump(apiout
);
648 bool wxXmlInterface::CheckConsistency() const
650 // this check can be quite slow, so do it only for debug releases:
652 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
654 if (!m_classes
[i
].CheckConsistency())
657 for (unsigned int j
=0; j
<m_classes
.GetCount(); j
++)
658 if (i
!=j
&& m_classes
[i
].GetName() == m_classes
[j
].GetName())
660 wxLogError("two classes have the same name: %s",
661 m_classes
[i
].GetName());
670 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
674 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
675 if (m_classes
[i
].GetHeader() == headerfile
)
676 ret
.Add(&m_classes
[i
]);
682 // ----------------------------------------------------------------------------
683 // wxXmlGccInterface helper declarations
684 // ----------------------------------------------------------------------------
686 // or-able flags for a toResolveTypeItem->attrib:
687 #define ATTRIB_CONST 1
688 #define ATTRIB_REFERENCE 2
689 #define ATTRIB_POINTER 4
690 #define ATTRIB_ARRAY 8
692 // it may sound strange but gccxml, in order to produce shorter ID names
693 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
694 // in order to be able to translate such strings into numbers using strtoul()
695 // we use as base 10 (possible digits) + 25 (possible characters) = 35
696 #define GCCXML_BASE 35
698 class toResolveTypeItem
701 toResolveTypeItem() { attribs
=0; }
702 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
703 : ref(refID
), attribs(attribint
) {}
705 unsigned long ref
, // the referenced type's ID
706 attribs
; // the attributes of this reference
711 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
712 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
713 wxIntegerHash
, wxIntegerEqual
,
714 wxToResolveTypeHashMap
);
716 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
717 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
718 wxIntegerHash
, wxIntegerEqual
,
719 wxClassMemberIdHashMap
);
723 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
727 // utility to parse gccXML ID values;
728 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
729 // but is a little bit faster
730 bool getID(unsigned long *id
, const wxString
& str
)
732 const wxStringCharType
* const start
= str
.wx_str()+1;
733 wxStringCharType
*end
;
734 #if wxUSE_UNICODE_WCHAR
735 unsigned long val
= wcstoul(start
, &end
, GCCXML_BASE
);
737 unsigned long val
= strtoul(start
, &end
, GCCXML_BASE
);
740 // return true only if scan was stopped by the terminating NUL and
741 // if the string was not empty to start with and no under/overflow
743 if ( *end
!= '\0' || end
== start
|| errno
== ERANGE
|| errno
== EINVAL
)
750 // utility specialized to parse efficiently the gccXML list of IDs which occur
751 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
752 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxString
& str
)
754 const wxStringCharType
* const start
= str
.wx_str();
755 #if wxUSE_UNICODE_WCHAR
756 size_t len
= wcslen(start
);
758 size_t len
= strlen(start
);
761 if (len
== 0 || start
[0] != '_')
764 const wxStringCharType
*curpos
= start
,
766 wxStringCharType
*nexttoken
;
770 // curpos always points to the underscore of the next token to parse:
771 #if wxUSE_UNICODE_WCHAR
772 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
774 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
776 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
779 // advance current position
780 curpos
= nexttoken
+ 1;
782 // add this ID to the hashmap
783 wxClassMemberIdHashMap::value_type
v(id
, p
);
791 // ----------------------------------------------------------------------------
793 // ----------------------------------------------------------------------------
795 bool wxXmlGccInterface::Parse(const wxString
& filename
)
801 wxLogMessage("Parsing %s...", filename
);
803 if (!doc
.Load(filename
)) {
804 wxLogError("can't load %s", filename
);
808 // start processing the XML file
809 if (doc
.GetRoot()->GetName() != "GCC_XML") {
810 wxLogError("invalid root node for %s", filename
);
814 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
817 #define MIN_REVISION 120
819 if (!version
.StartsWith("1."))
823 unsigned long rev
= 0;
824 if (!version
.Mid(2).ToULong(&rev
))
827 if (rev
< MIN_REVISION
)
833 wxLogError("The version of GCC-XML used for the creation of %s is too old; "
834 "the cvs_revision attribute of the root node reports '%s', "
835 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
839 wxToResolveTypeHashMap toResolveTypes
;
840 wxClassMemberIdHashMap members
;
841 wxTypeIdHashMap types
;
842 wxTypeIdHashMap files
;
843 wxTypeIdHashMap typedefs
;
845 // prealloc quite a lot of memory!
846 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
848 // build a list of wx classes and in general of all existent types
849 child
= doc
.GetRoot()->GetChildren();
852 const wxString
& n
= child
->GetName();
854 unsigned long id
= 0;
855 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
857 // NOTE: <File> nodes can have an id == "f0"...
859 wxLogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
865 wxString cname
= child
->GetAttribute("name");
866 if (cname
.IsEmpty()) {
867 wxLogError("Invalid empty name for '%s' node", n
);
871 // only register wx classes (do remember also the IDs of their members)
872 if (cname
.StartsWith("wx"))
874 // NB: "file" attribute contains an ID value that we'll resolve later
875 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
877 // the just-inserted class:
878 wxClass
*newClass
= &m_classes
.Last();
880 // now get a list of the base classes:
881 wxXmlNode
*baseNode
= child
->GetChildren();
884 // for now we store as "parents" only the parent IDs...
885 // later we will resolve them into full class names
886 if (baseNode
->GetName() == "Base")
887 newClass
->AddParent(baseNode
->GetAttribute("type"));
889 baseNode
= baseNode
->GetNext();
892 const wxString
& ids
= child
->GetAttribute("members");
895 if (child
->GetAttribute("incomplete") != "1") {
896 wxLogError("Invalid member IDs for '%s' class node: %s",
897 cname
, child
->GetAttribute("id"));
900 //else: don't warn the user; it looks like "incomplete" classes
901 // never have any member...
905 // decode the non-empty list of IDs:
906 if (!getMemberIDs(&members
, newClass
, ids
)) {
907 wxLogError("Invalid member IDs for '%s' class node: %s",
908 cname
, child
->GetAttribute("id"));
914 // register this class also as possible return/argument type:
917 else if (n
== "Typedef")
919 unsigned long typeId
= 0;
920 if (!getID(&typeId
, child
->GetAttribute("type"))) {
921 wxLogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
925 // this typedef node tell us that every type referenced with the
926 // "typeId" ID should be called with another name:
927 wxString name
= child
->GetAttribute("name");
929 // save this typedef in a separate hashmap...
930 typedefs
[typeId
] = name
;
934 else if (n
== "PointerType" || n
== "ReferenceType" ||
935 n
== "CvQualifiedType" || n
== "ArrayType")
937 unsigned long type
= 0;
938 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
939 wxLogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
943 unsigned long attr
= 0;
944 if (n
== "PointerType")
945 attr
= ATTRIB_POINTER
;
946 else if (n
== "ReferenceType")
947 attr
= ATTRIB_REFERENCE
;
948 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
950 else if (n
== "ArrayType")
953 // these nodes make reference to other types... we'll resolve them later
954 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
956 else if (n
== "FunctionType" || n
== "MethodType")
959 TODO: parsing FunctionType and MethodType nodes is not as easy
960 as for other "simple" types.
964 wxXmlNode
*arg
= child
->GetChildren();
967 if (arg
->GetName() == "Argument")
968 argstr
+= arg
->GetAttribute("type") + ", ";
969 arg
= arg
->GetNext();
972 if (argstr
.Len() > 0)
973 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
975 // these nodes make reference to other types... we'll resolve them later
976 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
977 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
979 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
980 // to a typedef later...
982 else if (n
== "File")
984 if (!child
->GetAttribute("id").StartsWith("f")) {
985 wxLogError("Unexpected file ID: %s", child
->GetAttribute("id"));
989 // just ignore this node... all file IDs/names were already parsed
990 files
[id
] = child
->GetAttribute("name");
994 // we register everything else as a possible return/argument type:
995 const wxString
& name
= child
->GetAttribute("name");
1000 //typeNames.Add(name);
1005 // this may happen with unnamed structs/union, special ctors,
1006 // or other exotic things which we are not interested to, since
1007 // they're never used as return/argument types by wxWidgets methods
1010 wxLogWarning("Type node '%s' with ID '%s' does not have name attribute",
1011 n
, child
->GetAttribute("id"));
1013 types
[id
] = "TOFIX";
1017 child
= child
->GetNext();
1019 // give feedback to the user about the progress...
1020 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1023 // some nodes with IDs referenced by methods as return/argument types, do reference
1024 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
1025 // thus we need to resolve their name iteratively:
1026 while (toResolveTypes
.size()>0)
1029 wxLogMessage("%d types were collected; %d types need yet to be resolved...",
1030 types
.size(), toResolveTypes
.size());
1032 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
1033 i
!= toResolveTypes
.end();)
1035 unsigned long id
= i
->first
;
1036 unsigned long referenced
= i
->second
.ref
;
1038 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
1039 if (primary
!= types
.end())
1041 // this to-resolve-type references a "primary" type
1043 wxString newtype
= primary
->second
;
1044 int attribs
= i
->second
.attribs
;
1046 // attribs may contain a combination of ATTRIB_* flags:
1047 if (attribs
& ATTRIB_CONST
)
1048 newtype
= "const " + newtype
;
1049 if (attribs
& ATTRIB_REFERENCE
)
1050 newtype
= newtype
+ "&";
1051 if (attribs
& ATTRIB_POINTER
)
1052 newtype
= newtype
+ "*";
1053 if (attribs
& ATTRIB_ARRAY
)
1054 newtype
= newtype
+ "[]";
1056 // add the resolved type to the list of "primary" types
1057 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
1058 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
1060 types
[id
] = newtype
;
1062 // this one has been resolved; erase it through its iterator!
1063 toResolveTypes
.erase(i
);
1065 // now iterator i is invalid; assign it again to the beginning
1066 i
= toResolveTypes
.begin();
1070 // then search in the referenced types themselves:
1071 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
1072 if (idx2
!= toResolveTypes
.end())
1074 // merge this to-resolve-type with the idx2->second type
1075 i
->second
.ref
= idx2
->second
.ref
;
1076 i
->second
.attribs
|= idx2
->second
.attribs
;
1078 // this type will eventually be solved in the next while() iteration
1083 wxLogError("Cannot solve '%d' reference type!", referenced
);
1090 // resolve header names
1092 for (i
=0; i
<m_classes
.GetCount(); i
++)
1094 unsigned long fileID
= 0;
1095 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
1096 wxLogError("invalid header id: %s", m_classes
[i
].GetHeader());
1101 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
1102 if (idx
== files
.end())
1104 // this is an error!
1105 wxLogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
1108 m_classes
[i
].SetHeader(idx
->second
);
1111 // resolve parent names
1112 for (i
=0; i
<m_classes
.GetCount(); i
++)
1114 for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++)
1118 if (!getID(&id
, m_classes
[i
].GetParent(k
))) {
1119 wxLogError("invalid parent class ID for '%s'", m_classes
[i
].GetName());
1123 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1124 if (idx
== types
.end())
1126 // this is an error!
1127 wxLogError("couldn't find parent class ID '%d'", id
);
1130 // replace k-th parent with its true name:
1131 m_classes
[i
].SetParent(k
, idx
->second
);
1135 // build the list of the wx methods
1136 child
= doc
.GetRoot()->GetChildren();
1139 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
1141 // only register public&protected methods
1142 if ((acc
== "public" || acc
== "protected") &&
1143 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
1145 unsigned long id
= 0;
1146 if (!getID(&id
, child
->GetAttribute("id"))) {
1147 wxLogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
1151 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
1152 if (it
!= members
.end())
1154 wxClass
*p
= it
->second
;
1156 // this <Method> node is a method of the i-th class!
1158 if (!ParseMethod(child
, types
, newfunc
)) {
1159 wxLogError("The method '%s' could not be added to class '%s'",
1160 child
->GetAttribute("demangled"), p
->GetName());
1164 // do some additional check that we can do only here:
1166 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
1167 wxLogError("The method '%s' does not seem to be a ctor for '%s'",
1168 newfunc
.GetName(), p
->GetName());
1171 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
1172 wxLogError("The method '%s' does not seem to be a dtor for '%s'",
1173 newfunc
.GetName(), p
->GetName());
1177 p
->AddMethod(newfunc
);
1181 child
= child
->GetNext();
1183 // give feedback to the user about the progress...
1184 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1187 if (!CheckConsistency())
1188 return false; // the check failed
1193 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1194 const wxTypeIdHashMap
& types
,
1197 // get the real name
1198 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1199 if (p
->GetName() == "Destructor")
1201 else if (p
->GetName() == "OperatorMethod")
1202 name
= "operator" + name
;
1204 // resolve return type
1206 unsigned long retid
= 0;
1207 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1209 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1210 wxLogError("Empty return ID for method '%s', with ID '%s'",
1211 name
, p
->GetAttribute("id"));
1217 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1218 if (retidx
== types
.end()) {
1219 wxLogError("Could not find return type ID '%s'", retid
);
1223 ret
= wxType(retidx
->second
);
1225 wxLogError("Invalid return type '%s' for method '%s', with ID '%s'",
1226 retidx
->second
, name
, p
->GetAttribute("id"));
1231 // resolve argument types
1232 wxArgumentTypeArray argtypes
;
1233 wxXmlNode
*arg
= p
->GetChildren();
1236 if (arg
->GetName() == "Argument")
1238 unsigned long id
= 0;
1239 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1240 wxLogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1241 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1245 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1246 if (idx
== types
.end()) {
1247 wxLogError("Could not find argument type ID '%s'", id
);
1251 argtypes
.Add(wxArgumentType(idx
->second
,
1252 arg
->GetAttribute("default"),
1253 arg
->GetAttribute("name")));
1256 arg
= arg
->GetNext();
1259 m
.SetReturnType(ret
);
1261 m
.SetArgumentTypes(argtypes
);
1262 m
.SetConst(p
->GetAttribute("const") == "1");
1263 m
.SetStatic(p
->GetAttribute("static") == "1");
1265 // NOTE: gccxml is smart enough to mark as virtual those functions
1266 // which are declared virtual in base classes but don't have
1267 // the "virtual" keyword explicitely indicated in the derived
1268 // classes... so we don't need any further logic for virtuals
1270 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1271 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1272 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1274 // decode access specifier
1275 if (p
->GetAttribute("access") == "public")
1276 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1277 else if (p
->GetAttribute("access") == "protected")
1278 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1279 else if (p
->GetAttribute("access") == "private")
1280 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1283 wxLogError("The prototype '%s' is not valid!", m
.GetAsString());
1292 // ----------------------------------------------------------------------------
1293 // wxXmlDoxygenInterface global helpers
1294 // ----------------------------------------------------------------------------
1296 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1300 // consider the tree
1302 // <a><b>this</b> is a <b>string</b></a>
1311 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1312 // this function returns "this is a string"
1314 wxXmlNode
*ref
= n
->GetChildren();
1316 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1317 text
+= ref
->GetNodeContent();
1318 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1319 text
+= ref
->GetContent();
1321 wxLogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1323 ref
= ref
->GetNext();
1329 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1334 wxXmlNode
*p
= parent
->GetChildren();
1337 switch (p
->GetType())
1339 case wxXML_TEXT_NODE
:
1340 if (p
->GetContent() == name
)
1344 case wxXML_ELEMENT_NODE
:
1345 // recurse into this node...
1346 if (HasTextNodeContaining(p
, name
))
1361 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1366 const wxXmlNode
*p
= parent
->GetChildren();
1369 if (p
->GetName() == name
)
1372 // search recursively in the children of this node
1373 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1383 int GetAvailabilityFor(const wxXmlNode
*node
)
1385 // identify <onlyfor> custom XML tags
1386 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1388 return wxPORT_UNKNOWN
;
1390 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1391 int nAvail
= wxPORT_UNKNOWN
;
1392 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1394 if (!ports
[i
].StartsWith("wx")) {
1395 wxLogError("unexpected port ID '%s'", ports
[i
]);
1399 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1406 // ----------------------------------------------------------------------------
1407 // wxXmlDoxygenInterface
1408 // ----------------------------------------------------------------------------
1410 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1412 wxXmlDocument index
;
1413 wxXmlNode
*compound
;
1415 wxLogMessage("Parsing %s...", filename
);
1417 if (!index
.Load(filename
)) {
1418 wxLogError("can't load %s", filename
);
1422 // start processing the index:
1423 if (index
.GetRoot()->GetName() != "doxygenindex") {
1424 wxLogError("invalid root node for %s", filename
);
1429 NB: we may need in future to do a version-check here if the
1430 format of the XML generated by doxygen changes.
1431 For now (doxygen version 1.5.5), this check is not required
1432 since AFAIK the XML format never changed since it was introduced.
1435 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1437 // process files referenced by this index file
1438 compound
= index
.GetRoot()->GetChildren();
1441 if (compound
->GetName() == "compound" &&
1442 compound
->GetAttribute("kind") == "class")
1444 wxString refid
= compound
->GetAttribute("refid");
1446 wxFileName
fn(filename
);
1447 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1451 compound
= compound
->GetNext();
1455 if (!CheckConsistency())
1456 return false; // the check failed
1461 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1463 wxClassMemberIdHashMap parents
;
1469 wxLogMessage("Parsing %s...", filename
);
1471 if (!doc
.Load(filename
)) {
1472 wxLogError("can't load %s", filename
);
1476 // start processing this compound definition XML
1477 if (doc
.GetRoot()->GetName() != "doxygen") {
1478 wxLogError("invalid root node for %s", filename
);
1482 // build a list of wx classes
1483 child
= doc
.GetRoot()->GetChildren();
1486 if (child
->GetName() == "compounddef" &&
1487 child
->GetAttribute("kind") == "class")
1491 wxString absoluteFile
, header
;
1493 wxXmlNode
*subchild
= child
->GetChildren();
1496 wxString kind
= subchild
->GetAttribute("kind");
1498 // parse only public&protected functions:
1499 if (subchild
->GetName() == "sectiondef" &&
1500 (kind
== "public-func" || kind
== "protected-func"))
1503 wxXmlNode
*membernode
= subchild
->GetChildren();
1506 if (membernode
->GetName() == "memberdef" &&
1507 membernode
->GetAttribute("kind") == "function")
1511 if (!ParseMethod(membernode
, m
, header
)) {
1512 wxLogError("The method '%s' could not be added to class '%s'",
1513 m
.GetName(), klass
.GetName());
1517 if (kind
== "public-func")
1518 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1519 else if (kind
== "protected-func")
1520 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1521 else if (kind
== "private-func")
1522 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1524 if (absoluteFile
.IsEmpty())
1525 absoluteFile
= header
;
1526 else if (header
!= absoluteFile
)
1528 wxLogError("The method '%s' is documented in a different "
1529 "file from others (which belong to '%s') ?",
1530 header
, absoluteFile
);
1537 membernode
= membernode
->GetNext();
1540 // all methods of this class were taken from the header "absoluteFile":
1541 klass
.SetHeader(absoluteFile
);
1543 else if (subchild
->GetName() == "compoundname")
1545 klass
.SetName(subchild
->GetNodeContent());
1547 /*else if (subchild->GetName() == "includes")
1549 // NOTE: we'll get the header from the <location> tags
1550 // scattered inside <memberdef> tags instead of
1551 // this <includes> tag since it does not contain
1552 // the absolute path of the header
1554 klass.SetHeader(subchild->GetNodeContent());
1556 else if (subchild
->GetName() == "detaileddescription")
1558 // identify <onlyfor> custom XML tags
1559 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1561 else if (subchild
->GetName() == "basecompoundref")
1563 // add the name of this parent to the list of klass' parents
1564 klass
.AddParent(subchild
->GetNodeContent());
1567 subchild
= subchild
->GetNext();
1572 m_classes
.Add(klass
);
1574 wxLogWarning("discarding class '%s' with %d methods...",
1575 klass
.GetName(), klass
.GetMethodCount());
1578 child
= child
->GetNext();
1580 // give feedback to the user about the progress...
1581 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1587 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1589 wxArgumentTypeArray args
;
1592 wxXmlNode
*child
= p
->GetChildren();
1595 if (child
->GetName() == "name")
1596 m
.SetName(child
->GetNodeContent());
1597 else if (child
->GetName() == "type")
1598 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1599 else if (child
->GetName() == "param")
1601 wxString typestr
, namestr
, defstr
, arrstr
;
1602 wxXmlNode
*n
= child
->GetChildren();
1605 if (n
->GetName() == "type")
1606 // if the <type> node has children, they should be all TEXT and <ref> nodes
1607 // and we need to take the text they contain, in the order they appear
1608 typestr
= GetTextFromChildren(n
);
1609 else if (n
->GetName() == "declname")
1610 namestr
= GetTextFromChildren(n
);
1611 else if (n
->GetName() == "defval")
1612 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1613 else if (n
->GetName() == "array")
1614 arrstr
= GetTextFromChildren(n
);
1619 if (typestr
.IsEmpty()) {
1620 wxLogError("cannot find type node for a param in method '%s'", m
.GetName());
1624 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1626 // can we use preprocessor output to transform the default value
1627 // into the same form which gets processed by wxXmlGccInterface?
1628 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1629 if (it
!= m_preproc
.end())
1630 newarg
.SetDefaultValue(defstr
, it
->second
);
1634 else if (child
->GetName() == "location")
1637 if (child
->GetAttribute("line").ToLong(&line
))
1638 m
.SetLocation((int)line
);
1639 header
= child
->GetAttribute("file");
1641 else if (child
->GetName() == "detaileddescription")
1643 // when a method has a @deprecated tag inside its description,
1644 // Doxygen outputs somewhere nested inside <detaileddescription>
1645 // a <xreftitle>Deprecated</xreftitle> tag.
1646 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1648 // identify <onlyfor> custom XML tags
1649 m
.SetAvailability(GetAvailabilityFor(child
));
1652 child
= child
->GetNext();
1655 m
.SetArgumentTypes(args
);
1656 m
.SetConst(p
->GetAttribute("const")=="yes");
1657 m
.SetStatic(p
->GetAttribute("static")=="yes");
1659 // NOTE: Doxygen is smart enough to mark as virtual those functions
1660 // which are declared virtual in base classes but don't have
1661 // the "virtual" keyword explicitely indicated in the derived
1662 // classes... so we don't need any further logic for virtuals
1664 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1665 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1668 wxLogError("The prototype '%s' is not valid!", m
.GetAsString());