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());
638 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
639 sorted
.Add(&m_classes
[i
]);
641 // now they have been sorted
642 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
643 sorted
[i
]->Dump(apiout
);
646 bool wxXmlInterface::CheckConsistency() const
648 // this check can be quite slow, so do it only for debug releases:
650 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
652 if (!m_classes
[i
].CheckConsistency())
655 for (unsigned int j
=0; j
<m_classes
.GetCount(); j
++)
656 if (i
!=j
&& m_classes
[i
].GetName() == m_classes
[j
].GetName())
658 wxLogError("two classes have the same name: %s",
659 m_classes
[i
].GetName());
668 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
672 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
673 if (m_classes
[i
].GetHeader() == headerfile
)
674 ret
.Add(&m_classes
[i
]);
680 // ----------------------------------------------------------------------------
681 // wxXmlGccInterface helper declarations
682 // ----------------------------------------------------------------------------
684 // or-able flags for a toResolveTypeItem->attrib:
685 #define ATTRIB_CONST 1
686 #define ATTRIB_REFERENCE 2
687 #define ATTRIB_POINTER 4
688 #define ATTRIB_ARRAY 8
690 // it may sound strange but gccxml, in order to produce shorter ID names
691 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
692 // in order to be able to translate such strings into numbers using strtoul()
693 // we use as base 10 (possible digits) + 25 (possible characters) = 35
694 #define GCCXML_BASE 35
696 class toResolveTypeItem
699 toResolveTypeItem() { attribs
=0; }
700 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
701 : ref(refID
), attribs(attribint
) {}
703 unsigned long ref
, // the referenced type's ID
704 attribs
; // the attributes of this reference
709 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
710 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
711 wxIntegerHash
, wxIntegerEqual
,
712 wxToResolveTypeHashMap
);
714 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
715 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
716 wxIntegerHash
, wxIntegerEqual
,
717 wxClassMemberIdHashMap
);
721 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
725 // utility to parse gccXML ID values;
726 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
727 // but is a little bit faster
728 bool getID(unsigned long *id
, const wxString
& str
)
730 const wxStringCharType
* const start
= str
.wx_str()+1;
731 wxStringCharType
*end
;
732 #if wxUSE_UNICODE_WCHAR
733 unsigned long val
= wcstoul(start
, &end
, GCCXML_BASE
);
735 unsigned long val
= strtoul(start
, &end
, GCCXML_BASE
);
738 // return true only if scan was stopped by the terminating NUL and
739 // if the string was not empty to start with and no under/overflow
741 if ( *end
!= '\0' || end
== start
|| errno
== ERANGE
|| errno
== EINVAL
)
748 // utility specialized to parse efficiently the gccXML list of IDs which occur
749 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
750 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxString
& str
)
752 const wxStringCharType
* const start
= str
.wx_str();
753 #if wxUSE_UNICODE_WCHAR
754 size_t len
= wcslen(start
);
756 size_t len
= strlen(start
);
759 if (len
== 0 || start
[0] != '_')
762 const wxStringCharType
*curpos
= start
,
764 wxStringCharType
*nexttoken
;
768 // curpos always points to the underscore of the next token to parse:
769 #if wxUSE_UNICODE_WCHAR
770 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
772 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
774 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
777 // advance current position
778 curpos
= nexttoken
+ 1;
780 // add this ID to the hashmap
781 wxClassMemberIdHashMap::value_type
v(id
, p
);
789 // ----------------------------------------------------------------------------
791 // ----------------------------------------------------------------------------
793 bool wxXmlGccInterface::Parse(const wxString
& filename
)
799 wxLogMessage("Parsing %s...", filename
);
801 if (!doc
.Load(filename
)) {
802 wxLogError("can't load %s", filename
);
806 // start processing the XML file
807 if (doc
.GetRoot()->GetName() != "GCC_XML") {
808 wxLogError("invalid root node for %s", filename
);
812 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
815 #define MIN_REVISION 120
817 if (!version
.StartsWith("1."))
821 unsigned long rev
= 0;
822 if (!version
.Mid(2).ToULong(&rev
))
825 if (rev
< MIN_REVISION
)
831 wxLogError("The version of GCC-XML used for the creation of %s is too old; "
832 "the cvs_revision attribute of the root node reports '%s', "
833 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
837 wxToResolveTypeHashMap toResolveTypes
;
838 wxClassMemberIdHashMap members
;
839 wxTypeIdHashMap types
;
840 wxTypeIdHashMap files
;
841 wxTypeIdHashMap typedefs
;
843 // prealloc quite a lot of memory!
844 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
846 // build a list of wx classes and in general of all existent types
847 child
= doc
.GetRoot()->GetChildren();
850 const wxString
& n
= child
->GetName();
852 unsigned long id
= 0;
853 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
855 // NOTE: <File> nodes can have an id == "f0"...
857 wxLogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
863 wxString cname
= child
->GetAttribute("name");
864 if (cname
.IsEmpty()) {
865 wxLogError("Invalid empty name for '%s' node", n
);
869 // only register wx classes (do remember also the IDs of their members)
870 if (cname
.StartsWith("wx"))
872 // NB: "file" attribute contains an ID value that we'll resolve later
873 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
875 // the just-inserted class:
876 wxClass
*newClass
= &m_classes
.Last();
878 // now get a list of the base classes:
879 wxXmlNode
*baseNode
= child
->GetChildren();
882 // for now we store as "parents" only the parent IDs...
883 // later we will resolve them into full class names
884 if (baseNode
->GetName() == "Base")
885 newClass
->AddParent(baseNode
->GetAttribute("type"));
887 baseNode
= baseNode
->GetNext();
890 const wxString
& ids
= child
->GetAttribute("members");
893 if (child
->GetAttribute("incomplete") != "1") {
894 wxLogError("Invalid member IDs for '%s' class node: %s",
895 cname
, child
->GetAttribute("id"));
898 //else: don't warn the user; it looks like "incomplete" classes
899 // never have any member...
903 // decode the non-empty list of IDs:
904 if (!getMemberIDs(&members
, newClass
, ids
)) {
905 wxLogError("Invalid member IDs for '%s' class node: %s",
906 cname
, child
->GetAttribute("id"));
912 // register this class also as possible return/argument type:
915 else if (n
== "Typedef")
917 unsigned long typeId
= 0;
918 if (!getID(&typeId
, child
->GetAttribute("type"))) {
919 wxLogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
923 // this typedef node tell us that every type referenced with the
924 // "typeId" ID should be called with another name:
925 wxString name
= child
->GetAttribute("name");
927 // save this typedef in a separate hashmap...
928 typedefs
[typeId
] = name
;
932 else if (n
== "PointerType" || n
== "ReferenceType" ||
933 n
== "CvQualifiedType" || n
== "ArrayType")
935 unsigned long type
= 0;
936 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
937 wxLogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
941 unsigned long attr
= 0;
942 if (n
== "PointerType")
943 attr
= ATTRIB_POINTER
;
944 else if (n
== "ReferenceType")
945 attr
= ATTRIB_REFERENCE
;
946 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
948 else if (n
== "ArrayType")
951 // these nodes make reference to other types... we'll resolve them later
952 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
954 else if (n
== "FunctionType" || n
== "MethodType")
957 TODO: parsing FunctionType and MethodType nodes is not as easy
958 as for other "simple" types.
962 wxXmlNode
*arg
= child
->GetChildren();
965 if (arg
->GetName() == "Argument")
966 argstr
+= arg
->GetAttribute("type") + ", ";
967 arg
= arg
->GetNext();
970 if (argstr
.Len() > 0)
971 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
973 // these nodes make reference to other types... we'll resolve them later
974 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
975 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
977 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
978 // to a typedef later...
980 else if (n
== "File")
982 if (!child
->GetAttribute("id").StartsWith("f")) {
983 wxLogError("Unexpected file ID: %s", child
->GetAttribute("id"));
987 // just ignore this node... all file IDs/names were already parsed
988 files
[id
] = child
->GetAttribute("name");
992 // we register everything else as a possible return/argument type:
993 const wxString
& name
= child
->GetAttribute("name");
998 //typeNames.Add(name);
1003 // this may happen with unnamed structs/union, special ctors,
1004 // or other exotic things which we are not interested to, since
1005 // they're never used as return/argument types by wxWidgets methods
1008 wxLogWarning("Type node '%s' with ID '%s' does not have name attribute",
1009 n
, child
->GetAttribute("id"));
1011 types
[id
] = "TOFIX";
1015 child
= child
->GetNext();
1017 // give feedback to the user about the progress...
1018 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1021 // some nodes with IDs referenced by methods as return/argument types, do reference
1022 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
1023 // thus we need to resolve their name iteratively:
1024 while (toResolveTypes
.size()>0)
1027 wxLogMessage("%d types were collected; %d types need yet to be resolved...",
1028 types
.size(), toResolveTypes
.size());
1030 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
1031 i
!= toResolveTypes
.end();)
1033 unsigned long id
= i
->first
;
1034 unsigned long referenced
= i
->second
.ref
;
1036 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
1037 if (primary
!= types
.end())
1039 // this to-resolve-type references a "primary" type
1041 wxString newtype
= primary
->second
;
1042 int attribs
= i
->second
.attribs
;
1044 // attribs may contain a combination of ATTRIB_* flags:
1045 if (attribs
& ATTRIB_CONST
)
1046 newtype
= "const " + newtype
;
1047 if (attribs
& ATTRIB_REFERENCE
)
1048 newtype
= newtype
+ "&";
1049 if (attribs
& ATTRIB_POINTER
)
1050 newtype
= newtype
+ "*";
1051 if (attribs
& ATTRIB_ARRAY
)
1052 newtype
= newtype
+ "[]";
1054 // add the resolved type to the list of "primary" types
1055 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
1056 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
1058 types
[id
] = newtype
;
1060 // this one has been resolved; erase it through its iterator!
1061 toResolveTypes
.erase(i
);
1063 // now iterator i is invalid; assign it again to the beginning
1064 i
= toResolveTypes
.begin();
1068 // then search in the referenced types themselves:
1069 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
1070 if (idx2
!= toResolveTypes
.end())
1072 // merge this to-resolve-type with the idx2->second type
1073 i
->second
.ref
= idx2
->second
.ref
;
1074 i
->second
.attribs
|= idx2
->second
.attribs
;
1076 // this type will eventually be solved in the next while() iteration
1081 wxLogError("Cannot solve '%d' reference type!", referenced
);
1088 // resolve header names
1089 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1091 unsigned long fileID
= 0;
1092 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
1093 wxLogError("invalid header id: %s", m_classes
[i
].GetHeader());
1098 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
1099 if (idx
== files
.end())
1101 // this is an error!
1102 wxLogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
1105 m_classes
[i
].SetHeader(idx
->second
);
1108 // resolve parent names
1109 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1111 for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++)
1115 if (!getID(&id
, m_classes
[i
].GetParent(k
))) {
1116 wxLogError("invalid parent class ID for '%s'", m_classes
[i
].GetName());
1120 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1121 if (idx
== types
.end())
1123 // this is an error!
1124 wxLogError("couldn't find parent class ID '%d'", id
);
1127 // replace k-th parent with its true name:
1128 m_classes
[i
].SetParent(k
, idx
->second
);
1132 // build the list of the wx methods
1133 child
= doc
.GetRoot()->GetChildren();
1136 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
1138 // only register public&protected methods
1139 if ((acc
== "public" || acc
== "protected") &&
1140 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
1142 unsigned long id
= 0;
1143 if (!getID(&id
, child
->GetAttribute("id"))) {
1144 wxLogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
1148 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
1149 if (it
!= members
.end())
1151 wxClass
*p
= it
->second
;
1153 // this <Method> node is a method of the i-th class!
1155 if (!ParseMethod(child
, types
, newfunc
)) {
1156 wxLogError("The method '%s' could not be added to class '%s'",
1157 child
->GetAttribute("demangled"), p
->GetName());
1161 // do some additional check that we can do only here:
1163 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
1164 wxLogError("The method '%s' does not seem to be a ctor for '%s'",
1165 newfunc
.GetName(), p
->GetName());
1168 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
1169 wxLogError("The method '%s' does not seem to be a dtor for '%s'",
1170 newfunc
.GetName(), p
->GetName());
1174 p
->AddMethod(newfunc
);
1178 child
= child
->GetNext();
1180 // give feedback to the user about the progress...
1181 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1184 if (!CheckConsistency())
1185 return false; // the check failed
1190 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1191 const wxTypeIdHashMap
& types
,
1194 // get the real name
1195 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1196 if (p
->GetName() == "Destructor")
1198 else if (p
->GetName() == "OperatorMethod")
1199 name
= "operator" + name
;
1201 // resolve return type
1203 unsigned long retid
= 0;
1204 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1206 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1207 wxLogError("Empty return ID for method '%s', with ID '%s'",
1208 name
, p
->GetAttribute("id"));
1214 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1215 if (retidx
== types
.end()) {
1216 wxLogError("Could not find return type ID '%s'", retid
);
1220 ret
= wxType(retidx
->second
);
1222 wxLogError("Invalid return type '%s' for method '%s', with ID '%s'",
1223 retidx
->second
, name
, p
->GetAttribute("id"));
1228 // resolve argument types
1229 wxArgumentTypeArray argtypes
;
1230 wxXmlNode
*arg
= p
->GetChildren();
1233 if (arg
->GetName() == "Argument")
1235 unsigned long id
= 0;
1236 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1237 wxLogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1238 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1242 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1243 if (idx
== types
.end()) {
1244 wxLogError("Could not find argument type ID '%s'", id
);
1248 argtypes
.Add(wxArgumentType(idx
->second
,
1249 arg
->GetAttribute("default"),
1250 arg
->GetAttribute("name")));
1253 arg
= arg
->GetNext();
1256 m
.SetReturnType(ret
);
1258 m
.SetArgumentTypes(argtypes
);
1259 m
.SetConst(p
->GetAttribute("const") == "1");
1260 m
.SetStatic(p
->GetAttribute("static") == "1");
1262 // NOTE: gccxml is smart enough to mark as virtual those functions
1263 // which are declared virtual in base classes but don't have
1264 // the "virtual" keyword explicitely indicated in the derived
1265 // classes... so we don't need any further logic for virtuals
1267 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1268 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1269 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1271 // decode access specifier
1272 if (p
->GetAttribute("access") == "public")
1273 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1274 else if (p
->GetAttribute("access") == "protected")
1275 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1276 else if (p
->GetAttribute("access") == "private")
1277 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1280 wxLogError("The prototype '%s' is not valid!", m
.GetAsString());
1289 // ----------------------------------------------------------------------------
1290 // wxXmlDoxygenInterface global helpers
1291 // ----------------------------------------------------------------------------
1293 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1297 // consider the tree
1299 // <a><b>this</b> is a <b>string</b></a>
1308 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1309 // this function returns "this is a string"
1311 wxXmlNode
*ref
= n
->GetChildren();
1313 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1314 text
+= ref
->GetNodeContent();
1315 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1316 text
+= ref
->GetContent();
1318 wxLogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1320 ref
= ref
->GetNext();
1326 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1331 wxXmlNode
*p
= parent
->GetChildren();
1334 switch (p
->GetType())
1336 case wxXML_TEXT_NODE
:
1337 if (p
->GetContent() == name
)
1341 case wxXML_ELEMENT_NODE
:
1342 // recurse into this node...
1343 if (HasTextNodeContaining(p
, name
))
1358 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1363 const wxXmlNode
*p
= parent
->GetChildren();
1366 if (p
->GetName() == name
)
1369 // search recursively in the children of this node
1370 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1380 int GetAvailabilityFor(const wxXmlNode
*node
)
1382 // identify <onlyfor> custom XML tags
1383 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1385 return wxPORT_UNKNOWN
;
1387 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1388 int nAvail
= wxPORT_UNKNOWN
;
1389 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1391 if (!ports
[i
].StartsWith("wx")) {
1392 wxLogError("unexpected port ID '%s'", ports
[i
]);
1396 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1403 // ----------------------------------------------------------------------------
1404 // wxXmlDoxygenInterface
1405 // ----------------------------------------------------------------------------
1407 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1409 wxXmlDocument index
;
1410 wxXmlNode
*compound
;
1412 wxLogMessage("Parsing %s...", filename
);
1414 if (!index
.Load(filename
)) {
1415 wxLogError("can't load %s", filename
);
1419 // start processing the index:
1420 if (index
.GetRoot()->GetName() != "doxygenindex") {
1421 wxLogError("invalid root node for %s", filename
);
1426 NB: we may need in future to do a version-check here if the
1427 format of the XML generated by doxygen changes.
1428 For now (doxygen version 1.5.5), this check is not required
1429 since AFAIK the XML format never changed since it was introduced.
1432 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1434 // process files referenced by this index file
1435 compound
= index
.GetRoot()->GetChildren();
1438 if (compound
->GetName() == "compound" &&
1439 compound
->GetAttribute("kind") == "class")
1441 wxString refid
= compound
->GetAttribute("refid");
1443 wxFileName
fn(filename
);
1444 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1448 compound
= compound
->GetNext();
1452 if (!CheckConsistency())
1453 return false; // the check failed
1458 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1460 wxClassMemberIdHashMap parents
;
1466 wxLogMessage("Parsing %s...", filename
);
1468 if (!doc
.Load(filename
)) {
1469 wxLogError("can't load %s", filename
);
1473 // start processing this compound definition XML
1474 if (doc
.GetRoot()->GetName() != "doxygen") {
1475 wxLogError("invalid root node for %s", filename
);
1479 // build a list of wx classes
1480 child
= doc
.GetRoot()->GetChildren();
1483 if (child
->GetName() == "compounddef" &&
1484 child
->GetAttribute("kind") == "class")
1488 wxString absoluteFile
, header
;
1490 wxXmlNode
*subchild
= child
->GetChildren();
1493 wxString kind
= subchild
->GetAttribute("kind");
1495 // parse only public&protected functions:
1496 if (subchild
->GetName() == "sectiondef" &&
1497 (kind
== "public-func" || kind
== "protected-func"))
1500 wxXmlNode
*membernode
= subchild
->GetChildren();
1503 if (membernode
->GetName() == "memberdef" &&
1504 membernode
->GetAttribute("kind") == "function")
1508 if (!ParseMethod(membernode
, m
, header
)) {
1509 wxLogError("The method '%s' could not be added to class '%s'",
1510 m
.GetName(), klass
.GetName());
1514 if (kind
== "public-func")
1515 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1516 else if (kind
== "protected-func")
1517 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1518 else if (kind
== "private-func")
1519 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1521 if (absoluteFile
.IsEmpty())
1522 absoluteFile
= header
;
1523 else if (header
!= absoluteFile
)
1525 wxLogError("The method '%s' is documented in a different "
1526 "file from others (which belong to '%s') ?",
1527 header
, absoluteFile
);
1534 membernode
= membernode
->GetNext();
1537 // all methods of this class were taken from the header "absoluteFile":
1538 klass
.SetHeader(absoluteFile
);
1540 else if (subchild
->GetName() == "compoundname")
1542 klass
.SetName(subchild
->GetNodeContent());
1544 /*else if (subchild->GetName() == "includes")
1546 // NOTE: we'll get the header from the <location> tags
1547 // scattered inside <memberdef> tags instead of
1548 // this <includes> tag since it does not contain
1549 // the absolute path of the header
1551 klass.SetHeader(subchild->GetNodeContent());
1553 else if (subchild
->GetName() == "detaileddescription")
1555 // identify <onlyfor> custom XML tags
1556 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1558 else if (subchild
->GetName() == "basecompoundref")
1560 // add the name of this parent to the list of klass' parents
1561 klass
.AddParent(subchild
->GetNodeContent());
1564 subchild
= subchild
->GetNext();
1569 m_classes
.Add(klass
);
1571 wxLogWarning("discarding class '%s' with %d methods...",
1572 klass
.GetName(), klass
.GetMethodCount());
1575 child
= child
->GetNext();
1577 // give feedback to the user about the progress...
1578 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1584 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1586 wxArgumentTypeArray args
;
1589 wxXmlNode
*child
= p
->GetChildren();
1592 if (child
->GetName() == "name")
1593 m
.SetName(child
->GetNodeContent());
1594 else if (child
->GetName() == "type")
1595 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1596 else if (child
->GetName() == "param")
1598 wxString typestr
, namestr
, defstr
, arrstr
;
1599 wxXmlNode
*n
= child
->GetChildren();
1602 if (n
->GetName() == "type")
1603 // if the <type> node has children, they should be all TEXT and <ref> nodes
1604 // and we need to take the text they contain, in the order they appear
1605 typestr
= GetTextFromChildren(n
);
1606 else if (n
->GetName() == "declname")
1607 namestr
= GetTextFromChildren(n
);
1608 else if (n
->GetName() == "defval")
1609 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1610 else if (n
->GetName() == "array")
1611 arrstr
= GetTextFromChildren(n
);
1616 if (typestr
.IsEmpty()) {
1617 wxLogError("cannot find type node for a param in method '%s'", m
.GetName());
1621 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1623 // can we use preprocessor output to transform the default value
1624 // into the same form which gets processed by wxXmlGccInterface?
1625 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1626 if (it
!= m_preproc
.end())
1627 newarg
.SetDefaultValue(defstr
, it
->second
);
1631 else if (child
->GetName() == "location")
1634 if (child
->GetAttribute("line").ToLong(&line
))
1635 m
.SetLocation((int)line
);
1636 header
= child
->GetAttribute("file");
1638 else if (child
->GetName() == "detaileddescription")
1640 // when a method has a @deprecated tag inside its description,
1641 // Doxygen outputs somewhere nested inside <detaileddescription>
1642 // a <xreftitle>Deprecated</xreftitle> tag.
1643 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1645 // identify <onlyfor> custom XML tags
1646 m
.SetAvailability(GetAvailabilityFor(child
));
1649 child
= child
->GetNext();
1652 m
.SetArgumentTypes(args
);
1653 m
.SetConst(p
->GetAttribute("const")=="yes");
1654 m
.SetStatic(p
->GetAttribute("static")=="yes");
1656 // NOTE: Doxygen is smart enough to mark as virtual those functions
1657 // which are declared virtual in base classes but don't have
1658 // the "virtual" keyword explicitely indicated in the derived
1659 // classes... so we don't need any further logic for virtuals
1661 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1662 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1665 wxLogError("The prototype '%s' is not valid!", m
.GetAsString());