1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Parser of the API/interface XML files
4 // Author: Francesco Montorsi
7 // Copyright: (c) 2008 Francesco Montorsi
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
18 // for all others, include the necessary headers
23 #include "wx/xml/xml.h"
24 #include "wx/wfstream.h"
25 #include "wx/hashmap.h"
26 #include "wx/filename.h"
27 #include "xmlparser.h"
30 #include <wx/arrimpl.cpp>
31 WX_DEFINE_OBJARRAY(wxTypeArray
)
32 WX_DEFINE_OBJARRAY(wxArgumentTypeArray
)
33 WX_DEFINE_OBJARRAY(wxMethodArray
)
34 WX_DEFINE_OBJARRAY(wxClassArray
)
37 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
38 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
41 // defined in ifacecheck.cpp
42 extern bool g_verbose
;
45 bool g_bLogEnabled
= true;
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
55 void wxType::SetTypeFromString(const wxString
& t
)
58 TODO: optimize the following code writing a single function
59 which works at char-level and does everything in a single pass
62 // clean the type string
63 // ---------------------
67 // [] is the same as * for gccxml
68 m_strType
.Replace("[]", "*");
69 m_strType
.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
70 m_strType
.Replace("long unsigned int", "unsigned long");
72 // make sure the * and & operator always use the same spacing rules
73 // (to make sure GetAsString() output is always consistent)
74 m_strType
.Replace("*", "* ");
75 m_strType
.Replace("&", "& ");
76 m_strType
.Replace(" *", "*");
77 m_strType
.Replace(" &", "&");
79 while (m_strType
.Contains(" "))
80 m_strType
.Replace(" ", " "); // do it once again
82 m_strType
.Replace(" ,", ",");
85 m_strType
.Replace("_wxArraywxArrayStringBase", "const wxString&");
87 m_strType
= m_strType
.Strip(wxString::both
);
91 // clean the type string (this time for the comparison)
92 // ----------------------------------------------------
94 m_strTypeClean
= m_strType
; // begin with the already-cleaned string
95 m_strTypeClean
.Replace("const", "");
96 m_strTypeClean
.Replace("static", "");
97 m_strTypeClean
.Replace("*", "");
98 m_strTypeClean
.Replace("&", "");
99 m_strTypeClean
.Replace("[]", "");
100 m_strTypeClean
= m_strTypeClean
.Strip(wxString::both
);
102 // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
103 // need to be considered as the same type
104 if (m_strTypeClean
.EndsWith("Base"))
105 m_strTypeClean
= m_strTypeClean
.Left(m_strTypeClean
.Len()-4);
108 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
109 // fix this to avoid false positives
110 m_strTypeClean
.Replace("wxDateTime::", "");
111 m_strTypeClean
.Replace("wxStockGDI::", ""); // same story for some other classes
112 m_strTypeClean
.Replace("wxHelpEvent::", "");
113 m_strTypeClean
.Replace("wxWindowID", "int");
116 bool wxType::IsOk() const
118 // NB: m_strType can contain the :: operator; think to e.g. the
119 // "reverse_iterator_impl<wxString::const_iterator>" type
120 // It can also contain commas, * and & operators etc
122 return !m_strTypeClean
.IsEmpty();
125 bool wxType::operator==(const wxType
& m
) const
127 // brain-dead comparison:
129 if (m_strTypeClean
== m
.m_strTypeClean
&&
130 IsConst() == m
.IsConst() &&
131 IsStatic() == m
.IsStatic() &&
132 IsPointer() == m
.IsPointer() &&
133 IsReference() == m
.IsReference())
138 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");
178 // ADHOC-FIX: for wxConv* default values
179 p
->Replace("wxConvAuto(wxFONTENCODING_DEFAULT)", "wxConvAuto()");
180 p
->Replace("wxGet_wxConvUTF8()", "wxConvUTF8");
181 p
->Replace("wxGet_wxConvLocal()", "wxConvLocal");
185 // clean ONLY the default argument string specific for comparison
186 // --------------------------------------------------------------
188 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
189 m_strDefaultValueForCmp
.EndsWith(")"))
191 // get rid of the wxT() part
192 unsigned int len
= m_strDefaultValueForCmp
.Len();
193 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
197 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
198 // fix this to avoid false positives
199 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
200 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
201 m_strDefaultValueForCmp
.Replace("wxHelpEvent::", ""); // same story for some other classes
202 m_strDefaultValueForCmp
.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
205 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
206 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
209 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
211 if ((const wxType
&)(*this) != (const wxType
&)m
)
215 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
216 // numbers; avoid false positives in this case!
217 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
218 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
221 // fix for default values which were replaced by gcc-xml with their numeric values
222 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
223 if (m_strTypeClean
== "long" || m_strTypeClean
== "int")
225 if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) ||
226 (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx")))
229 wxLogMessage("Supposing '%s' default value to be the same of '%s'...",
230 m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
);
236 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
238 // maybe the default values are numbers.
239 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
240 // To handle these cases, we try to convert the default value strings to numbers:
241 long def1val
, def2val
;
242 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
243 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
245 if (def1val
== def2val
)
246 return true; // the default values match
250 wxLogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
251 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
255 // we deliberately avoid checks on the argument name
261 // ----------------------------------------------------------------------------
263 // ----------------------------------------------------------------------------
265 bool wxMethod::IsOk() const
267 // NOTE: m_retType can be a wxEmptyType, and means that this method
268 // is a ctor or a dtor.
269 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
270 wxLogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
274 if (m_strName
.IsEmpty())
277 // a function can't be both const and static or virtual and static!
278 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
279 wxLogError("'%s' method can't be both const/static or virtual/static", m_strName
);
283 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
285 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
286 if (!m_args
[i
].IsOk()) {
287 wxLogError("'%s' method has invalid %d-th argument type: %s",
288 m_strName
, i
+1, m_args
[i
].GetAsString());
292 // NB: the default value of the arguments can contain pretty much everything
293 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
294 // so we don't do any test on their contents
295 if (m_args
.GetCount()>0)
297 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
298 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
300 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
301 wxLogError("'%s' method has %d-th argument which has no default value "
302 "(while the previous one had one!)",
307 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
314 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
316 if (GetReturnType() != m
.GetReturnType() ||
317 GetName() != m
.GetName())
320 wxLogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m
.GetName());
324 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
326 wxLogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
327 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
331 // compare argument types
332 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
333 if (m_args
[i
] != m
.m_args
[i
])
339 bool wxMethod::ActsAsDefaultCtor() const
344 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
345 if (!m_args
[i
].HasDefaultValue())
351 bool wxMethod::operator==(const wxMethod
& m
) const
354 if (IsConst() != m
.IsConst() ||
355 IsStatic() != m
.IsStatic() ||
356 IsVirtual() != m
.IsVirtual() ||
357 IsPureVirtual() != m
.IsPureVirtual() ||
358 IsDeprecated() != m
.IsDeprecated() ||
359 GetAccessSpecifier() != m
.GetAccessSpecifier())
362 wxLogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m
.GetName());
367 // check everything else
368 return MatchesExceptForAttributes(m
);
371 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
372 bool bDeprecated
, bool bAccessSpec
) const
376 // NOTE: for return and argument types, never use wxType::GetAsCleanString
377 // since in that way we'd miss important decorators like &,*,const etc
379 if (m_retType
!=wxEmptyType
)
380 ret
+= m_retType
.GetAsString() + " ";
381 //else; this is a ctor or dtor
383 ret
+= m_strName
+ "(";
385 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
387 ret
+= m_args
[i
].GetAsString();
389 const wxString
& name
= m_args
[i
].GetArgumentName();
390 if (bWithArgumentNames
&& !name
.IsEmpty())
393 const wxString
& def
= bCleanDefaultValues
?
394 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
401 if (m_args
.GetCount()>0)
402 ret
= ret
.Left(ret
.Len()-2);
409 ret
= "static " + ret
;
410 if (m_bVirtual
|| m_bPureVirtual
)
411 ret
= "virtual " + ret
;
414 if (m_bDeprecated
&& bDeprecated
)
415 ret
+= " [deprecated]";
424 case wxMAS_PROTECTED
:
425 ret
+= " [protected]";
436 void wxMethod::Dump(wxTextOutputStream
& stream
) const
438 stream
<< "[" + m_retType
.GetAsString() + "]";
439 stream
<< "[" + m_strName
+ "]";
441 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
442 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
443 "=" + m_args
[i
].GetDefaultValue() + "]";
450 stream
<< " VIRTUAL";
452 stream
<< " PURE-VIRTUAL";
454 stream
<< " DEPRECATED";
459 // ----------------------------------------------------------------------------
461 // ----------------------------------------------------------------------------
463 wxString
wxClass::GetNameWithoutTemplate() const
465 // NB: I'm not sure this is the right terminology for this function!
467 if (m_strName
.Contains("<"))
468 return m_strName
.Left(m_strName
.Find("<"));
472 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
474 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
475 // named wxWritableCharTypeBuffer, without the <...> part!
477 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
483 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
485 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
491 void wxClass::Dump(wxTextOutputStream
& out
) const
493 out
<< m_strName
+ "\n";
495 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
497 // dump all our methods
499 m_methods
[i
].Dump(out
);
506 bool wxClass::CheckConsistency() const
508 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
509 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
510 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
512 wxLogError("class %s has two methods with the same prototype: '%s'",
513 m_strName
, m_methods
[i
].GetAsString());
517 //((wxClass*)this)->m_methods.RemoveAt(j);
524 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
526 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
527 if (m_methods
[i
] == m
)
528 return &m_methods
[i
];
532 const wxMethod
* wxClass::RecursiveUpwardFindMethod(const wxMethod
& m
,
533 const wxXmlInterface
* allclasses
) const
535 // first, search into *this
536 const wxMethod
* ret
= FindMethod(m
);
540 // then, search into its parents
541 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
543 // ignore non-wx-classes parents
544 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
545 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
547 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
549 wxLogError("Could not find parent '%s' of class '%s'...",
550 m_parents
[i
], GetName());
554 const wxMethod
*parentMethod
= parent
->RecursiveUpwardFindMethod(m
, allclasses
);
560 // could not find anything even in parent classes...
564 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
566 wxMethodPtrArray ret
;
568 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
569 if (m_methods
[i
].GetName() == name
)
570 ret
.Add(&m_methods
[i
]);
576 wxMethodPtrArray
wxClass::RecursiveUpwardFindMethodsNamed(const wxString
& name
,
577 const wxXmlInterface
* allclasses
) const
579 // first, search into *this
580 wxMethodPtrArray ret
= FindMethodsNamed(name
);
581 if (ret
.GetCount()>0)
582 return ret
; // stop here, don't look upward in the parents
584 // then, search into parents of this class
585 for (unsigned int i
=0; i
<m_parents
.GetCount(); i
++)
587 // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
588 if (m_parents
[i
].StartsWith("wx") && m_parents
[i
] != "wxScrolledT_Helper")
590 const wxClass
*parent
= allclasses
->FindClass(m_parents
[i
]);
592 wxLogError("Could not find parent '%s' of class '%s'...",
593 m_parents
[i
], GetName());
597 wxMethodPtrArray temp
= parent
->RecursiveUpwardFindMethodsNamed(name
, allclasses
);
598 WX_APPEND_ARRAY(ret
, temp
);
607 // ----------------------------------------------------------------------------
609 // ----------------------------------------------------------------------------
611 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
613 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
615 // sort alphabetically
616 return item1
->GetName().Cmp(item2
->GetName());
619 void wxXmlInterface::Dump(const wxString
& filename
)
621 wxFFileOutputStream
apioutput( filename
);
622 wxTextOutputStream
apiout( apioutput
);
624 // dump the classes in alphabetical order
625 wxSortedClassArray
sorted(CompareWxClassObjects
);
626 sorted
.Alloc(m_classes
.GetCount());
627 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
628 sorted
.Add(&m_classes
[i
]);
630 // now they have been sorted
631 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
632 sorted
[i
]->Dump(apiout
);
635 bool wxXmlInterface::CheckConsistency() const
637 // this check can be quite slow, so do it only for debug releases:
639 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
641 if (!m_classes
[i
].CheckConsistency())
644 for (unsigned int j
=0; j
<m_classes
.GetCount(); j
++)
645 if (i
!=j
&& m_classes
[i
].GetName() == m_classes
[j
].GetName())
647 wxLogError("two classes have the same name: %s",
648 m_classes
[i
].GetName());
657 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
661 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
662 if (m_classes
[i
].GetHeader() == headerfile
)
663 ret
.Add(&m_classes
[i
]);
669 // ----------------------------------------------------------------------------
670 // wxXmlGccInterface helper declarations
671 // ----------------------------------------------------------------------------
673 // or-able flags for a toResolveTypeItem->attrib:
674 #define ATTRIB_CONST 1
675 #define ATTRIB_REFERENCE 2
676 #define ATTRIB_POINTER 4
677 #define ATTRIB_ARRAY 8
679 // it may sound strange but gccxml, in order to produce shorter ID names
680 // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
681 // in order to be able to translate such strings into numbers using strtoul()
682 // we use as base 10 (possible digits) + 25 (possible characters) = 35
683 #define GCCXML_BASE 35
685 class toResolveTypeItem
688 toResolveTypeItem() { attribs
=0; }
689 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
690 : ref(refID
), attribs(attribint
) {}
692 unsigned long ref
, // the referenced type's ID
693 attribs
; // the attributes of this reference
698 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
699 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
700 wxIntegerHash
, wxIntegerEqual
,
701 wxToResolveTypeHashMap
);
703 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
704 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
705 wxIntegerHash
, wxIntegerEqual
,
706 wxClassMemberIdHashMap
);
710 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
714 // utility to parse gccXML ID values;
715 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
716 // but is a little bit faster
717 bool getID(unsigned long *id
, const wxString
& str
)
719 const wxStringCharType
* const start
= str
.wx_str()+1;
720 wxStringCharType
*end
;
721 #if wxUSE_UNICODE_WCHAR
722 unsigned long val
= wcstoul(start
, &end
, GCCXML_BASE
);
724 unsigned long val
= strtoul(start
, &end
, GCCXML_BASE
);
727 // return true only if scan was stopped by the terminating NUL and
728 // if the string was not empty to start with and no under/overflow
730 if ( *end
!= '\0' || end
== start
|| errno
== ERANGE
|| errno
== EINVAL
)
737 // utility specialized to parse efficiently the gccXML list of IDs which occur
738 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
739 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxString
& str
)
741 const wxStringCharType
* const start
= str
.wx_str();
742 #if wxUSE_UNICODE_WCHAR
743 size_t len
= wcslen(start
);
745 size_t len
= strlen(start
);
748 if (len
== 0 || start
[0] != '_')
751 const wxStringCharType
*curpos
= start
,
753 wxStringCharType
*nexttoken
;
757 // curpos always points to the underscore of the next token to parse:
758 #if wxUSE_UNICODE_WCHAR
759 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
761 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
763 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
766 // advance current position
767 curpos
= nexttoken
+ 1;
769 // add this ID to the hashmap
770 wxClassMemberIdHashMap::value_type
v(id
, p
);
778 // ----------------------------------------------------------------------------
780 // ----------------------------------------------------------------------------
782 bool wxXmlGccInterface::Parse(const wxString
& filename
)
788 wxLogMessage("Parsing %s...", filename
);
790 if (!doc
.Load(filename
)) {
791 wxLogError("can't load %s", filename
);
795 // start processing the XML file
796 if (doc
.GetRoot()->GetName() != "GCC_XML") {
797 wxLogError("invalid root node for %s", filename
);
801 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
804 #define MIN_REVISION 120
806 if (!version
.StartsWith("1."))
810 unsigned long rev
= 0;
811 if (!version
.Mid(2).ToULong(&rev
))
814 if (rev
< MIN_REVISION
)
820 wxLogError("The version of GCC-XML used for the creation of %s is too old; "
821 "the cvs_revision attribute of the root node reports '%s', "
822 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
826 wxToResolveTypeHashMap toResolveTypes
;
827 wxClassMemberIdHashMap members
;
828 wxTypeIdHashMap types
;
829 wxTypeIdHashMap files
;
830 wxTypeIdHashMap typedefs
;
832 // prealloc quite a lot of memory!
833 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
835 // build a list of wx classes and in general of all existent types
836 child
= doc
.GetRoot()->GetChildren();
839 const wxString
& n
= child
->GetName();
841 unsigned long id
= 0;
842 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
844 // NOTE: <File> nodes can have an id == "f0"...
846 wxLogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
852 wxString cname
= child
->GetAttribute("name");
853 if (cname
.IsEmpty()) {
854 wxLogError("Invalid empty name for '%s' node", n
);
858 // only register wx classes (do remember also the IDs of their members)
859 if (cname
.StartsWith("wx"))
861 // NB: "file" attribute contains an ID value that we'll resolve later
862 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
864 // the just-inserted class:
865 wxClass
*newClass
= &m_classes
.Last();
867 // now get a list of the base classes:
868 wxXmlNode
*baseNode
= child
->GetChildren();
871 // for now we store as "parents" only the parent IDs...
872 // later we will resolve them into full class names
873 if (baseNode
->GetName() == "Base")
874 newClass
->AddParent(baseNode
->GetAttribute("type"));
876 baseNode
= baseNode
->GetNext();
879 const wxString
& ids
= child
->GetAttribute("members");
882 if (child
->GetAttribute("incomplete") != "1") {
883 wxLogError("Invalid member IDs for '%s' class node: %s",
884 cname
, child
->GetAttribute("id"));
887 //else: don't warn the user; it looks like "incomplete" classes
888 // never have any member...
892 // decode the non-empty list of IDs:
893 if (!getMemberIDs(&members
, newClass
, ids
)) {
894 wxLogError("Invalid member IDs for '%s' class node: %s",
895 cname
, child
->GetAttribute("id"));
901 // register this class also as possible return/argument type:
904 else if (n
== "Typedef")
906 unsigned long typeId
= 0;
907 if (!getID(&typeId
, child
->GetAttribute("type"))) {
908 wxLogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
912 // this typedef node tell us that every type referenced with the
913 // "typeId" ID should be called with another name:
914 wxString name
= child
->GetAttribute("name");
916 // save this typedef in a separate hashmap...
917 typedefs
[typeId
] = name
;
921 else if (n
== "PointerType" || n
== "ReferenceType" ||
922 n
== "CvQualifiedType" || n
== "ArrayType")
924 unsigned long type
= 0;
925 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
926 wxLogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
930 unsigned long attr
= 0;
931 if (n
== "PointerType")
932 attr
= ATTRIB_POINTER
;
933 else if (n
== "ReferenceType")
934 attr
= ATTRIB_REFERENCE
;
935 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
937 else if (n
== "ArrayType")
940 // these nodes make reference to other types... we'll resolve them later
941 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
943 else if (n
== "FunctionType" || n
== "MethodType")
946 TODO: parsing FunctionType and MethodType nodes is not as easy
947 as for other "simple" types.
951 wxXmlNode
*arg
= child
->GetChildren();
954 if (arg
->GetName() == "Argument")
955 argstr
+= arg
->GetAttribute("type") + ", ";
956 arg
= arg
->GetNext();
959 if (argstr
.Len() > 0)
960 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
962 // these nodes make reference to other types... we'll resolve them later
963 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
964 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
966 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
967 // to a typedef later...
969 else if (n
== "File")
971 if (!child
->GetAttribute("id").StartsWith("f")) {
972 wxLogError("Unexpected file ID: %s", child
->GetAttribute("id"));
976 // just ignore this node... all file IDs/names were already parsed
977 files
[id
] = child
->GetAttribute("name");
981 // we register everything else as a possible return/argument type:
982 const wxString
& name
= child
->GetAttribute("name");
987 //typeNames.Add(name);
992 // this may happen with unnamed structs/union, special ctors,
993 // or other exotic things which we are not interested to, since
994 // they're never used as return/argument types by wxWidgets methods
997 wxLogWarning("Type node '%s' with ID '%s' does not have name attribute",
998 n
, child
->GetAttribute("id"));
1000 types
[id
] = "TOFIX";
1004 child
= child
->GetNext();
1006 // give feedback to the user about the progress...
1007 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1010 // some nodes with IDs referenced by methods as return/argument types, do reference
1011 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
1012 // thus we need to resolve their name iteratively:
1013 while (toResolveTypes
.size()>0)
1016 wxLogMessage("%d types were collected; %d types need yet to be resolved...",
1017 types
.size(), toResolveTypes
.size());
1019 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
1020 i
!= toResolveTypes
.end();)
1022 unsigned long id
= i
->first
;
1023 unsigned long referenced
= i
->second
.ref
;
1025 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
1026 if (primary
!= types
.end())
1028 // this to-resolve-type references a "primary" type
1030 wxString newtype
= primary
->second
;
1031 int attribs
= i
->second
.attribs
;
1033 // attribs may contain a combination of ATTRIB_* flags:
1034 if (attribs
& ATTRIB_CONST
)
1035 newtype
= "const " + newtype
;
1036 if (attribs
& ATTRIB_REFERENCE
)
1037 newtype
= newtype
+ "&";
1038 if (attribs
& ATTRIB_POINTER
)
1039 newtype
= newtype
+ "*";
1040 if (attribs
& ATTRIB_ARRAY
)
1041 newtype
= newtype
+ "[]";
1043 // add the resolved type to the list of "primary" types
1044 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
1045 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
1047 types
[id
] = newtype
;
1049 // this one has been resolved; erase it through its iterator!
1050 toResolveTypes
.erase(i
);
1052 // now iterator i is invalid; assign it again to the beginning
1053 i
= toResolveTypes
.begin();
1057 // then search in the referenced types themselves:
1058 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
1059 if (idx2
!= toResolveTypes
.end())
1061 // merge this to-resolve-type with the idx2->second type
1062 i
->second
.ref
= idx2
->second
.ref
;
1063 i
->second
.attribs
|= idx2
->second
.attribs
;
1065 // this type will eventually be solved in the next while() iteration
1070 wxLogError("Cannot solve '%d' reference type!", referenced
);
1077 // resolve header names
1078 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1080 unsigned long fileID
= 0;
1081 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
1082 wxLogError("invalid header id: %s", m_classes
[i
].GetHeader());
1087 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
1088 if (idx
== files
.end())
1090 // this is an error!
1091 wxLogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
1094 m_classes
[i
].SetHeader(idx
->second
);
1097 // resolve parent names
1098 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
1100 for (unsigned int k
=0; k
<m_classes
[i
].GetParentCount(); k
++)
1104 if (!getID(&id
, m_classes
[i
].GetParent(k
))) {
1105 wxLogError("invalid parent class ID for '%s'", m_classes
[i
].GetName());
1109 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1110 if (idx
== types
.end())
1112 // this is an error!
1113 wxLogError("couldn't find parent class ID '%d'", id
);
1116 // replace k-th parent with its true name:
1117 m_classes
[i
].SetParent(k
, idx
->second
);
1121 // build the list of the wx methods
1122 child
= doc
.GetRoot()->GetChildren();
1125 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
1127 // only register public&protected methods
1128 if ((acc
== "public" || acc
== "protected") &&
1129 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
1131 unsigned long id
= 0;
1132 if (!getID(&id
, child
->GetAttribute("id"))) {
1133 wxLogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
1137 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
1138 if (it
!= members
.end())
1140 wxClass
*p
= it
->second
;
1142 // this <Method> node is a method of the i-th class!
1144 if (!ParseMethod(child
, types
, newfunc
)) {
1145 wxLogError("The method '%s' could not be added to class '%s'",
1146 child
->GetAttribute("demangled"), p
->GetName());
1150 // do some additional check that we can do only here:
1152 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
1153 wxLogError("The method '%s' does not seem to be a ctor for '%s'",
1154 newfunc
.GetName(), p
->GetName());
1157 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
1158 wxLogError("The method '%s' does not seem to be a dtor for '%s'",
1159 newfunc
.GetName(), p
->GetName());
1163 p
->AddMethod(newfunc
);
1167 child
= child
->GetNext();
1169 // give feedback to the user about the progress...
1170 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1173 if (!CheckConsistency())
1174 return false; // the check failed
1179 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1180 const wxTypeIdHashMap
& types
,
1183 // get the real name
1184 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1185 if (p
->GetName() == "Destructor")
1187 else if (p
->GetName() == "OperatorMethod")
1188 name
= "operator" + name
;
1190 // resolve return type
1192 unsigned long retid
= 0;
1193 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1195 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1196 wxLogError("Empty return ID for method '%s', with ID '%s'",
1197 name
, p
->GetAttribute("id"));
1203 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1204 if (retidx
== types
.end()) {
1205 wxLogError("Could not find return type ID '%s'", retid
);
1209 ret
= wxType(retidx
->second
);
1211 wxLogError("Invalid return type '%s' for method '%s', with ID '%s'",
1212 retidx
->second
, name
, p
->GetAttribute("id"));
1217 // resolve argument types
1218 wxArgumentTypeArray argtypes
;
1219 wxXmlNode
*arg
= p
->GetChildren();
1222 if (arg
->GetName() == "Argument")
1224 unsigned long id
= 0;
1225 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1226 wxLogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1227 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1231 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1232 if (idx
== types
.end()) {
1233 wxLogError("Could not find argument type ID '%s'", id
);
1237 argtypes
.Add(wxArgumentType(idx
->second
,
1238 arg
->GetAttribute("default"),
1239 arg
->GetAttribute("name")));
1242 arg
= arg
->GetNext();
1245 m
.SetReturnType(ret
);
1247 m
.SetArgumentTypes(argtypes
);
1248 m
.SetConst(p
->GetAttribute("const") == "1");
1249 m
.SetStatic(p
->GetAttribute("static") == "1");
1251 // NOTE: gccxml is smart enough to mark as virtual those functions
1252 // which are declared virtual in base classes but don't have
1253 // the "virtual" keyword explicitely indicated in the derived
1254 // classes... so we don't need any further logic for virtuals
1256 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1257 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1258 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1260 // decode access specifier
1261 if (p
->GetAttribute("access") == "public")
1262 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1263 else if (p
->GetAttribute("access") == "protected")
1264 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1265 else if (p
->GetAttribute("access") == "private")
1266 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1269 wxLogError("The prototype '%s' is not valid!", m
.GetAsString());
1278 // ----------------------------------------------------------------------------
1279 // wxXmlDoxygenInterface global helpers
1280 // ----------------------------------------------------------------------------
1282 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1286 // consider the tree
1288 // <a><b>this</b> is a <b>string</b></a>
1297 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1298 // this function returns "this is a string"
1300 wxXmlNode
*ref
= n
->GetChildren();
1302 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1303 text
+= ref
->GetNodeContent();
1304 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1305 text
+= ref
->GetContent();
1307 wxLogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1309 ref
= ref
->GetNext();
1315 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1320 wxXmlNode
*p
= parent
->GetChildren();
1323 switch (p
->GetType())
1325 case wxXML_TEXT_NODE
:
1326 if (p
->GetContent() == name
)
1330 case wxXML_ELEMENT_NODE
:
1331 // recurse into this node...
1332 if (HasTextNodeContaining(p
, name
))
1347 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1352 const wxXmlNode
*p
= parent
->GetChildren();
1355 if (p
->GetName() == name
)
1358 // search recursively in the children of this node
1359 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1369 int GetAvailabilityFor(const wxXmlNode
*node
)
1371 // identify <onlyfor> custom XML tags
1372 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1374 return wxPORT_UNKNOWN
;
1376 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1377 int nAvail
= wxPORT_UNKNOWN
;
1378 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1380 if (!ports
[i
].StartsWith("wx")) {
1381 wxLogError("unexpected port ID '%s'", ports
[i
]);
1385 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1392 // ----------------------------------------------------------------------------
1393 // wxXmlDoxygenInterface
1394 // ----------------------------------------------------------------------------
1396 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1398 wxXmlDocument index
;
1399 wxXmlNode
*compound
;
1401 wxLogMessage("Parsing %s...", filename
);
1403 if (!index
.Load(filename
)) {
1404 wxLogError("can't load %s", filename
);
1408 // start processing the index:
1409 if (index
.GetRoot()->GetName() != "doxygenindex") {
1410 wxLogError("invalid root node for %s", filename
);
1415 NB: we may need in future to do a version-check here if the
1416 format of the XML generated by doxygen changes.
1417 For now (doxygen version 1.5.5), this check is not required
1418 since AFAIK the XML format never changed since it was introduced.
1421 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1423 // process files referenced by this index file
1424 compound
= index
.GetRoot()->GetChildren();
1427 if (compound
->GetName() == "compound" &&
1428 compound
->GetAttribute("kind") == "class")
1430 wxString refid
= compound
->GetAttribute("refid");
1432 wxFileName
fn(filename
);
1433 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1437 compound
= compound
->GetNext();
1441 if (!CheckConsistency())
1442 return false; // the check failed
1447 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1449 wxClassMemberIdHashMap parents
;
1455 wxLogMessage("Parsing %s...", filename
);
1457 if (!doc
.Load(filename
)) {
1458 wxLogError("can't load %s", filename
);
1462 // start processing this compound definition XML
1463 if (doc
.GetRoot()->GetName() != "doxygen") {
1464 wxLogError("invalid root node for %s", filename
);
1468 // build a list of wx classes
1469 child
= doc
.GetRoot()->GetChildren();
1472 if (child
->GetName() == "compounddef" &&
1473 child
->GetAttribute("kind") == "class")
1477 wxString absoluteFile
, header
;
1479 wxXmlNode
*subchild
= child
->GetChildren();
1482 wxString kind
= subchild
->GetAttribute("kind");
1484 // parse only public&protected functions:
1485 if (subchild
->GetName() == "sectiondef" &&
1486 (kind
== "public-func" || kind
== "protected-func"))
1489 wxXmlNode
*membernode
= subchild
->GetChildren();
1492 if (membernode
->GetName() == "memberdef" &&
1493 membernode
->GetAttribute("kind") == "function")
1497 if (!ParseMethod(membernode
, m
, header
)) {
1498 wxLogError("The method '%s' could not be added to class '%s'",
1499 m
.GetName(), klass
.GetName());
1503 if (kind
== "public-func")
1504 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1505 else if (kind
== "protected-func")
1506 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1507 else if (kind
== "private-func")
1508 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1510 if (absoluteFile
.IsEmpty())
1511 absoluteFile
= header
;
1512 else if (header
!= absoluteFile
)
1514 wxLogError("The method '%s' is documented in a different "
1515 "file from others (which belong to '%s') ?",
1516 header
, absoluteFile
);
1523 membernode
= membernode
->GetNext();
1526 // all methods of this class were taken from the header "absoluteFile":
1527 klass
.SetHeader(absoluteFile
);
1529 else if (subchild
->GetName() == "compoundname")
1531 klass
.SetName(subchild
->GetNodeContent());
1533 /*else if (subchild->GetName() == "includes")
1535 // NOTE: we'll get the header from the <location> tags
1536 // scattered inside <memberdef> tags instead of
1537 // this <includes> tag since it does not contain
1538 // the absolute path of the header
1540 klass.SetHeader(subchild->GetNodeContent());
1542 else if (subchild
->GetName() == "detaileddescription")
1544 // identify <onlyfor> custom XML tags
1545 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1547 else if (subchild
->GetName() == "basecompoundref")
1549 // add the name of this parent to the list of klass' parents
1550 klass
.AddParent(subchild
->GetNodeContent());
1553 subchild
= subchild
->GetNext();
1558 m_classes
.Add(klass
);
1560 wxLogWarning("discarding class '%s' with %d methods...",
1561 klass
.GetName(), klass
.GetMethodCount());
1564 child
= child
->GetNext();
1566 // give feedback to the user about the progress...
1567 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1573 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1575 wxArgumentTypeArray args
;
1578 wxXmlNode
*child
= p
->GetChildren();
1581 if (child
->GetName() == "name")
1582 m
.SetName(child
->GetNodeContent());
1583 else if (child
->GetName() == "type")
1584 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1585 else if (child
->GetName() == "param")
1587 wxString typestr
, namestr
, defstr
, arrstr
;
1588 wxXmlNode
*n
= child
->GetChildren();
1591 if (n
->GetName() == "type")
1592 // if the <type> node has children, they should be all TEXT and <ref> nodes
1593 // and we need to take the text they contain, in the order they appear
1594 typestr
= GetTextFromChildren(n
);
1595 else if (n
->GetName() == "declname")
1596 namestr
= GetTextFromChildren(n
);
1597 else if (n
->GetName() == "defval")
1598 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1599 else if (n
->GetName() == "array")
1600 arrstr
= GetTextFromChildren(n
);
1605 if (typestr
.IsEmpty()) {
1606 wxLogError("cannot find type node for a param in method '%s'", m
.GetName());
1610 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1612 // can we use preprocessor output to transform the default value
1613 // into the same form which gets processed by wxXmlGccInterface?
1614 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1615 if (it
!= m_preproc
.end())
1616 newarg
.SetDefaultValue(defstr
, it
->second
);
1620 else if (child
->GetName() == "location")
1623 if (child
->GetAttribute("line").ToLong(&line
))
1624 m
.SetLocation((int)line
);
1625 header
= child
->GetAttribute("file");
1627 else if (child
->GetName() == "detaileddescription")
1629 // when a method has a @deprecated tag inside its description,
1630 // Doxygen outputs somewhere nested inside <detaileddescription>
1631 // a <xreftitle>Deprecated</xreftitle> tag.
1632 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1634 // identify <onlyfor> custom XML tags
1635 m
.SetAvailability(GetAvailabilityFor(child
));
1638 child
= child
->GetNext();
1641 m
.SetArgumentTypes(args
);
1642 m
.SetConst(p
->GetAttribute("const")=="yes");
1643 m
.SetStatic(p
->GetAttribute("static")=="yes");
1645 // NOTE: Doxygen is smart enough to mark as virtual those functions
1646 // which are declared virtual in base classes but don't have
1647 // the "virtual" keyword explicitely indicated in the derived
1648 // classes... so we don't need any further logic for virtuals
1650 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1651 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1654 wxLogError("The prototype '%s' is not valid!", m
.GetAsString());