1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Parser of the API/interface XML files
4 // Author: Francesco Montorsi
7 // Copyright: (c) 2008 Francesco Montorsi
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
18 // for all others, include the necessary headers
22 #include "wx/xml/xml.h"
23 #include "wx/wfstream.h"
24 #include "wx/hashmap.h"
25 #include "wx/filename.h"
26 #include "xmlparser.h"
29 #include <wx/arrimpl.cpp>
30 WX_DEFINE_OBJARRAY(wxTypeArray
)
31 WX_DEFINE_OBJARRAY(wxArgumentTypeArray
)
32 WX_DEFINE_OBJARRAY(wxMethodArray
)
33 WX_DEFINE_OBJARRAY(wxClassArray
)
36 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
37 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
40 // defined in ifacecheck.cpp
41 extern bool g_verbose
;
44 bool g_bLogEnabled
= true;
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
54 void wxType::SetTypeFromString(const wxString
& t
)
57 TODO: optimize the following code writing a single function
58 which works at char-level and does everything in a single pass
63 // [] is the same as * for gccxml
64 m_strType
.Replace("[]", "*");
65 m_strType
.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
67 // make sure the * and & operator always use the same spacing rules
68 // (to make sure GetAsString() output is always consistent)
69 m_strType
.Replace("*", "* ");
70 m_strType
.Replace("&", "& ");
71 m_strType
.Replace(" *", "*");
72 m_strType
.Replace(" &", "&");
74 while (m_strType
.Contains(" "))
75 m_strType
.Replace(" ", " "); // do it once again
77 m_strType
.Replace(" ,", ",");
79 m_strType
= m_strType
.Strip(wxString::both
);
81 // now set the clean version
82 m_strTypeClean
= m_strType
;
83 m_strTypeClean
.Replace("const", "");
84 m_strTypeClean
.Replace("static", "");
85 m_strTypeClean
.Replace("*", "");
86 m_strTypeClean
.Replace("&", "");
87 m_strTypeClean
.Replace("[]", "");
88 m_strTypeClean
= m_strTypeClean
.Strip(wxString::both
);
90 // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
91 // need to be considered as the same type
92 if (m_strTypeClean
.EndsWith("Base"))
93 m_strTypeClean
= m_strTypeClean
.Left(m_strTypeClean
.Len()-4);
96 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
97 // fix this to avoid false positives
98 m_strTypeClean
.Replace("wxDateTime::", "");
99 m_strTypeClean
.Replace("wxStockGDI::", ""); // same story for some other classes
100 m_strTypeClean
.Replace("wxHelpEvent::", "");
103 bool wxType::IsOk() const
105 // NB: m_strType can contain the :: operator; think to e.g. the
106 // "reverse_iterator_impl<wxString::const_iterator>" type
107 // It can also contain commas, * and & operators etc
109 return !m_strTypeClean
.IsEmpty();
112 bool wxType::operator==(const wxType
& m
) const
114 // brain-dead comparison:
116 if (m_strTypeClean
== m
.m_strTypeClean
&&
117 IsConst() == m
.IsConst() &&
118 IsStatic() == m
.IsStatic() &&
119 IsPointer() == m
.IsPointer() &&
120 IsReference() == m
.IsReference())
124 LogMessage("Type '%s' does not match type '%s'", m_strType
, m
.m_strType
);
130 // ----------------------------------------------------------------------------
132 // ----------------------------------------------------------------------------
134 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
136 m_strDefaultValue
= defval
.Strip(wxString::both
);
137 m_strDefaultValueForCmp
= defvalForCmp
.IsEmpty() ? m_strDefaultValue
: defvalForCmp
.Strip(wxString::both
);
139 // adjust aesthetic form of DefaultValue for the modify mode of ifacecheck:
140 // we may need to write it out in an interface header
141 if (m_strDefaultValue
== "0u")
142 m_strDefaultValue
= "0";
144 // in order to make valid&simple comparison on argument defaults,
145 // we reduce some of the multiple forms in which the same things may appear
147 if (m_strDefaultValueForCmp
== "0u")
148 m_strDefaultValueForCmp
= "0";
150 // fix for unicode strings:
151 m_strDefaultValueForCmp
.Replace("\\000\\000\\000", "");
153 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
154 m_strDefaultValueForCmp
.EndsWith(")"))
156 // get rid of the wxT() part
157 unsigned int len
= m_strDefaultValueForCmp
.Len();
158 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
163 m_strDefaultValueForCmp.Replace("0", "NULL");
165 m_strDefaultValueForCmp.Replace("NULL", "0");
168 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
169 // fix this to avoid false positives
170 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
171 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
172 m_strDefaultValueForCmp
.Replace("wxHelpEvent::", ""); // same story for some other classes
174 m_strDefaultValueForCmp
.Replace("wxGet_wxConvLocal()", "wxConvLocal");
176 m_strDefaultValueForCmp
.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
179 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
180 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
183 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
185 if ((const wxType
&)(*this) != (const wxType
&)m
)
189 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
190 // numbers; avoid false positives in this case!
191 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
192 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
195 // fix for default values which were replaced by gcc-xml with their numeric values
196 // (at this point we know that m_strTypeClean == m.m_strTypeClean):
197 if (m_strTypeClean
== "long" || m_strTypeClean
== "int")
199 if ((m_strDefaultValueForCmp
.IsNumber() && m
.m_strDefaultValueForCmp
.StartsWith("wx")) ||
200 (m
.m_strDefaultValueForCmp
.IsNumber() && m_strDefaultValueForCmp
.StartsWith("wx")))
203 LogMessage("Supposing '%s' default value to be the same of '%s'...",
204 m_strDefaultValueForCmp
, m
.m_strDefaultValueForCmp
);
210 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
212 // maybe the default values are numbers.
213 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
214 // To handle these cases, we try to convert the default value strings to numbers:
215 long def1val
, def2val
;
216 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
217 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
219 if (def1val
== def2val
)
220 return true; // the default values match
224 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
225 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
229 // we deliberately avoid checks on the argument name
235 // ----------------------------------------------------------------------------
237 // ----------------------------------------------------------------------------
239 bool wxMethod::IsOk() const
241 // NOTE: m_retType can be a wxEmptyType, and means that this method
242 // is a ctor or a dtor.
243 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
244 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
248 if (m_strName
.IsEmpty())
251 // a function can't be both const and static or virtual and static!
252 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
253 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
257 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
259 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
260 if (!m_args
[i
].IsOk()) {
261 LogError("'%s' method has invalid %d-th argument type: %s",
262 m_strName
, i
+1, m_args
[i
].GetAsString());
266 // NB: the default value of the arguments can contain pretty much everything
267 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
268 // so we don't do any test on their contents
269 if (m_args
.GetCount()>0)
271 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
272 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
274 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
275 LogError("'%s' method has %d-th argument which has no default value "
276 "(while the previous one had one!)",
281 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
288 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
290 if (GetReturnType() != m
.GetReturnType() ||
291 GetName() != m
.GetName())
294 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
296 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
297 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
301 // compare argument types
302 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
303 if (m_args
[i
] != m
.m_args
[i
])
309 bool wxMethod::operator==(const wxMethod
& m
) const
312 if (IsConst() != m
.IsConst() ||
313 IsStatic() != m
.IsStatic() ||
314 IsVirtual() != m
.IsVirtual() ||
315 IsPureVirtual() != m
.IsPureVirtual() ||
316 IsDeprecated() != m
.IsDeprecated() ||
317 GetAccessSpecifier() != m
.GetAccessSpecifier())
320 // check everything else
321 return MatchesExceptForAttributes(m
);
324 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
325 bool bDeprecated
, bool bAccessSpec
) const
329 // NOTE: for return and argument types, never use wxType::GetAsCleanString
330 // since in that way we'd miss important decorators like &,*,const etc
332 if (m_retType
!=wxEmptyType
)
333 ret
+= m_retType
.GetAsString() + " ";
334 //else; this is a ctor or dtor
336 ret
+= m_strName
+ "(";
338 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
340 ret
+= m_args
[i
].GetAsString();
342 const wxString
& name
= m_args
[i
].GetArgumentName();
343 if (bWithArgumentNames
&& !name
.IsEmpty())
346 const wxString
& def
= bCleanDefaultValues
?
347 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
354 if (m_args
.GetCount()>0)
355 ret
= ret
.Left(ret
.Len()-2);
362 ret
= "static " + ret
;
363 if (m_bVirtual
|| m_bPureVirtual
)
364 ret
= "virtual " + ret
;
367 if (m_bDeprecated
&& bDeprecated
)
368 ret
+= " [deprecated]";
377 case wxMAS_PROTECTED
:
378 ret
+= " [protected]";
389 void wxMethod::Dump(wxTextOutputStream
& stream
) const
391 stream
<< "[" + m_retType
.GetAsString() + "]";
392 stream
<< "[" + m_strName
+ "]";
394 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
395 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
396 "=" + m_args
[i
].GetDefaultValue() + "]";
403 stream
<< " VIRTUAL";
405 stream
<< " PURE-VIRTUAL";
407 stream
<< " DEPRECATED";
412 // ----------------------------------------------------------------------------
414 // ----------------------------------------------------------------------------
416 wxString
wxClass::GetNameWithoutTemplate() const
418 // NB: I'm not sure this is the right terminology for this function!
420 if (m_strName
.Contains("<"))
421 return m_strName
.Left(m_strName
.Find("<"));
425 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
427 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
428 // named wxWritableCharTypeBuffer, without the <...> part!
430 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
436 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
438 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
444 void wxClass::Dump(wxTextOutputStream
& out
) const
446 out
<< m_strName
+ "\n";
448 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
450 // dump all our methods
452 m_methods
[i
].Dump(out
);
459 bool wxClass::CheckConsistency() const
461 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
462 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
463 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
465 LogError("class %s has two methods with the same prototype: '%s'",
466 m_strName
, m_methods
[i
].GetAsString());
468 ((wxClass
*)this)->m_methods
.RemoveAt(j
);
475 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
477 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
478 if (m_methods
[i
] == m
)
479 return &m_methods
[i
];
483 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
485 wxMethodPtrArray ret
;
487 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
488 if (m_methods
[i
].GetName() == name
)
489 ret
.Add(&m_methods
[i
]);
495 // ----------------------------------------------------------------------------
497 // ----------------------------------------------------------------------------
499 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
501 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
503 // sort alphabetically
504 return item1
->GetName().Cmp(item2
->GetName());
507 void wxXmlInterface::Dump(const wxString
& filename
)
509 wxFFileOutputStream
apioutput( filename
);
510 wxTextOutputStream
apiout( apioutput
);
512 // dump the classes in alphabetical order
513 wxSortedClassArray
sorted(CompareWxClassObjects
);
514 sorted
.Alloc(m_classes
.GetCount());
515 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
516 sorted
.Add(&m_classes
[i
]);
518 // now they have been sorted
519 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
520 sorted
[i
]->Dump(apiout
);
523 bool wxXmlInterface::CheckParseResults() const
525 // this check can be quite slow, so do it only for debug releases:
527 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
528 if (!m_classes
[i
].CheckConsistency())
535 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
539 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
540 if (m_classes
[i
].GetHeader() == headerfile
)
541 ret
.Add(&m_classes
[i
]);
547 // ----------------------------------------------------------------------------
548 // wxXmlGccInterface helper declarations
549 // ----------------------------------------------------------------------------
551 // or-able flags for a toResolveTypeItem->attrib:
552 #define ATTRIB_CONST 1
553 #define ATTRIB_REFERENCE 2
554 #define ATTRIB_POINTER 4
555 #define ATTRIB_ARRAY 8
557 #define GCCXML_BASE 35
559 class toResolveTypeItem
562 toResolveTypeItem() { attribs
=0; }
563 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
564 : ref(refID
), attribs(attribint
) {}
566 unsigned long ref
, // the referenced type's ID
567 attribs
; // the attributes of this reference
572 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
573 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
574 wxIntegerHash
, wxIntegerEqual
,
575 wxToResolveTypeHashMap
);
577 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
578 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
579 wxIntegerHash
, wxIntegerEqual
,
580 wxClassMemberIdHashMap
);
583 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
587 // utility to parse gccXML ID values;
588 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
589 // but is a little bit faster
590 bool getID(unsigned long *id
, const wxStringCharType
* str
)
592 wxStringCharType
*end
;
593 #if wxUSE_UNICODE_WCHAR
594 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
596 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
599 // return true only if scan was stopped by the terminating NUL and
600 // if the string was not empty to start with and no under/overflow
602 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
609 // utility specialized to parse efficiently the gccXML list of IDs which occur
610 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
611 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
613 #if wxUSE_UNICODE_WCHAR
614 size_t len
= wcslen(str
);
616 size_t len
= strlen(str
);
619 if (len
== 0 || str
[0] != '_')
622 const wxStringCharType
*curpos
= str
,
624 wxStringCharType
*nexttoken
;
628 // curpos always points to the underscore of the next token to parse:
629 #if wxUSE_UNICODE_WCHAR
630 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
632 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
634 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
637 // advance current position
638 curpos
= nexttoken
+ 1;
640 // add this ID to the hashmap
641 wxClassMemberIdHashMap::value_type
v(id
, p
);
649 // ----------------------------------------------------------------------------
651 // ----------------------------------------------------------------------------
653 bool wxXmlGccInterface::Parse(const wxString
& filename
)
659 LogMessage("Parsing %s...", filename
);
661 if (!doc
.Load(filename
)) {
662 LogError("can't load %s", filename
);
666 // start processing the XML file
667 if (doc
.GetRoot()->GetName() != "GCC_XML") {
668 LogError("invalid root node for %s", filename
);
672 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
675 #define MIN_REVISION 120
677 if (!version
.StartsWith("1."))
681 unsigned long rev
= 0;
682 if (!version
.Mid(2).ToULong(&rev
))
685 if (rev
< MIN_REVISION
)
691 LogError("The version of GCC-XML used for the creation of %s is too old; "
692 "the cvs_revision attribute of the root node reports '%s', "
693 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
697 wxToResolveTypeHashMap toResolveTypes
;
698 wxClassMemberIdHashMap members
;
699 wxTypeIdHashMap types
;
700 wxTypeIdHashMap files
;
701 wxTypeIdHashMap typedefs
;
703 // prealloc quite a lot of memory!
704 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
706 // build a list of wx classes and in general of all existent types
707 child
= doc
.GetRoot()->GetChildren();
710 const wxString
& n
= child
->GetName();
712 unsigned long id
= 0;
713 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
715 // NOTE: <File> nodes can have an id == "f0"...
717 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
723 wxString cname
= child
->GetAttribute("name");
724 if (cname
.IsEmpty()) {
725 LogError("Invalid empty name for '%s' node", n
);
729 // only register wx classes (do remember also the IDs of their members)
730 if (cname
.StartsWith("wx"))
732 // NB: "file" attribute contains an ID value that we'll resolve later
733 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
735 const wxString
& ids
= child
->GetAttribute("members");
738 if (child
->GetAttribute("incomplete") != "1") {
739 LogError("Invalid member IDs for '%s' class node: %s",
740 cname
, child
->GetAttribute("id"));
743 //else: don't warn the user; it looks like "incomplete" classes
744 // never have any member...
748 // decode the non-empty list of IDs:
749 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
750 LogError("Invalid member IDs for '%s' class node: %s",
751 cname
, child
->GetAttribute("id"));
757 // register this class also as possible return/argument type:
760 else if (n
== "Typedef")
762 unsigned long typeId
= 0;
763 if (!getID(&typeId
, child
->GetAttribute("type"))) {
764 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
768 // this typedef node tell us that every type referenced with the
769 // "typeId" ID should be called with another name:
770 wxString name
= child
->GetAttribute("name");
772 // save this typedef in a separate hashmap...
773 typedefs
[typeId
] = name
;
777 else if (n
== "PointerType" || n
== "ReferenceType" ||
778 n
== "CvQualifiedType" || n
== "ArrayType")
780 unsigned long type
= 0;
781 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
782 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
786 unsigned long attr
= 0;
787 if (n
== "PointerType")
788 attr
= ATTRIB_POINTER
;
789 else if (n
== "ReferenceType")
790 attr
= ATTRIB_REFERENCE
;
791 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
793 else if (n
== "ArrayType")
796 // these nodes make reference to other types... we'll resolve them later
797 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
799 else if (n
== "FunctionType" || n
== "MethodType")
802 TODO: parsing FunctionType and MethodType nodes is not as easy
803 as for other "simple" types.
807 wxXmlNode
*arg
= child
->GetChildren();
810 if (arg
->GetName() == "Argument")
811 argstr
+= arg
->GetAttribute("type") + ", ";
812 arg
= arg
->GetNext();
815 if (argstr
.Len() > 0)
816 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
818 // these nodes make reference to other types... we'll resolve them later
819 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
820 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
822 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
823 // to a typedef later...
825 else if (n
== "File")
827 if (!child
->GetAttribute("id").StartsWith("f")) {
828 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
832 // just ignore this node... all file IDs/names were already parsed
833 files
[id
] = child
->GetAttribute("name");
837 // we register everything else as a possible return/argument type:
838 const wxString
& name
= child
->GetAttribute("name");
843 //typeNames.Add(name);
848 // this may happen with unnamed structs/union, special ctors,
849 // or other exotic things which we are not interested to, since
850 // they're never used as return/argument types by wxWidgets methods
853 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
854 n
, child
->GetAttribute("id"));
860 child
= child
->GetNext();
862 // give feedback to the user about the progress...
863 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
866 // some nodes with IDs referenced by methods as return/argument types, do reference
867 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
868 // thus we need to resolve their name iteratively:
869 while (toResolveTypes
.size()>0)
872 LogMessage("%d types were collected; %d types need yet to be resolved...",
873 types
.size(), toResolveTypes
.size());
875 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
876 i
!= toResolveTypes
.end();)
878 unsigned long id
= i
->first
;
879 unsigned long referenced
= i
->second
.ref
;
881 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
882 if (primary
!= types
.end())
884 // this to-resolve-type references a "primary" type
886 wxString newtype
= primary
->second
;
887 int attribs
= i
->second
.attribs
;
889 // attribs may contain a combination of ATTRIB_* flags:
890 if (attribs
& ATTRIB_CONST
)
891 newtype
= "const " + newtype
;
892 if (attribs
& ATTRIB_REFERENCE
)
893 newtype
= newtype
+ "&";
894 if (attribs
& ATTRIB_POINTER
)
895 newtype
= newtype
+ "*";
896 if (attribs
& ATTRIB_ARRAY
)
897 newtype
= newtype
+ "[]";
899 // add the resolved type to the list of "primary" types
900 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
901 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
905 // this one has been resolved; erase it through its iterator!
906 toResolveTypes
.erase(i
);
908 // now iterator i is invalid; assign it again to the beginning
909 i
= toResolveTypes
.begin();
913 // then search in the referenced types themselves:
914 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
915 if (idx2
!= toResolveTypes
.end())
917 // merge this to-resolve-type with the idx2->second type
918 i
->second
.ref
= idx2
->second
.ref
;
919 i
->second
.attribs
|= idx2
->second
.attribs
;
921 // this type will eventually be solved in the next while() iteration
926 LogError("Cannot solve '%d' reference type!", referenced
);
933 // resolve header names
934 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
936 unsigned long fileID
= 0;
937 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
938 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
943 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
944 if (idx
== files
.end())
947 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
950 m_classes
[i
].SetHeader(idx
->second
);
953 // build the list of the wx methods
954 child
= doc
.GetRoot()->GetChildren();
957 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
959 // only register public&protected methods
960 if ((acc
== "public" || acc
== "protected") &&
961 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
963 unsigned long id
= 0;
964 if (!getID(&id
, child
->GetAttribute("id"))) {
965 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
969 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
970 if (it
!= members
.end())
972 wxClass
*p
= it
->second
;
974 // this <Method> node is a method of the i-th class!
976 if (!ParseMethod(child
, types
, newfunc
)) {
977 LogError("The method '%s' could not be added to class '%s'",
978 child
->GetAttribute("demangled"), p
->GetName());
982 // do some additional check that we can do only here:
984 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
985 LogError("The method '%s' does not seem to be a ctor for '%s'",
986 newfunc
.GetName(), p
->GetName());
989 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
990 LogError("The method '%s' does not seem to be a dtor for '%s'",
991 newfunc
.GetName(), p
->GetName());
995 p
->AddMethod(newfunc
);
999 child
= child
->GetNext();
1001 // give feedback to the user about the progress...
1002 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1005 if (!CheckParseResults())
1011 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
1012 const wxTypeIdHashMap
& types
,
1015 // get the real name
1016 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
1017 if (p
->GetName() == "Destructor")
1019 else if (p
->GetName() == "OperatorMethod")
1020 name
= "operator" + name
;
1022 // resolve return type
1024 unsigned long retid
= 0;
1025 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1027 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1028 LogError("Empty return ID for method '%s', with ID '%s'",
1029 name
, p
->GetAttribute("id"));
1035 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1036 if (retidx
== types
.end()) {
1037 LogError("Could not find return type ID '%s'", retid
);
1041 ret
= wxType(retidx
->second
);
1043 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1044 retidx
->second
, name
, p
->GetAttribute("id"));
1049 // resolve argument types
1050 wxArgumentTypeArray argtypes
;
1051 wxXmlNode
*arg
= p
->GetChildren();
1054 if (arg
->GetName() == "Argument")
1056 unsigned long id
= 0;
1057 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1058 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1059 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1063 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1064 if (idx
== types
.end()) {
1065 LogError("Could not find argument type ID '%s'", id
);
1069 argtypes
.Add(wxArgumentType(idx
->second
,
1070 arg
->GetAttribute("default"),
1071 arg
->GetAttribute("name")));
1074 arg
= arg
->GetNext();
1077 m
.SetReturnType(ret
);
1079 m
.SetArgumentTypes(argtypes
);
1080 m
.SetConst(p
->GetAttribute("const") == "1");
1081 m
.SetStatic(p
->GetAttribute("static") == "1");
1083 // NOTE: gccxml is smart enough to mark as virtual those functions
1084 // which are declared virtual in base classes but don't have
1085 // the "virtual" keyword explicitely indicated in the derived
1086 // classes... so we don't need any further logic for virtuals
1088 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1089 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1090 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1092 // decode access specifier
1093 if (p
->GetAttribute("access") == "public")
1094 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1095 else if (p
->GetAttribute("access") == "protected")
1096 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1097 else if (p
->GetAttribute("access") == "private")
1098 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1101 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1110 // ----------------------------------------------------------------------------
1111 // wxXmlDoxygenInterface global helpers
1112 // ----------------------------------------------------------------------------
1114 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1118 // consider the tree
1120 // <a><b>this</b> is a <b>string</b></a>
1129 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1130 // this function returns "this is a string"
1132 wxXmlNode
*ref
= n
->GetChildren();
1134 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1135 text
+= ref
->GetNodeContent();
1136 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1137 text
+= ref
->GetContent();
1139 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1141 ref
= ref
->GetNext();
1147 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1152 wxXmlNode
*p
= parent
->GetChildren();
1155 switch (p
->GetType())
1157 case wxXML_TEXT_NODE
:
1158 if (p
->GetContent() == name
)
1162 case wxXML_ELEMENT_NODE
:
1163 // recurse into this node...
1164 if (HasTextNodeContaining(p
, name
))
1179 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1184 const wxXmlNode
*p
= parent
->GetChildren();
1187 if (p
->GetName() == name
)
1190 // search recursively in the children of this node
1191 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1201 int GetAvailabilityFor(const wxXmlNode
*node
)
1203 // identify <onlyfor> custom XML tags
1204 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1206 return wxPORT_UNKNOWN
;
1208 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1209 int nAvail
= wxPORT_UNKNOWN
;
1210 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1212 if (!ports
[i
].StartsWith("wx")) {
1213 LogError("unexpected port ID '%s'", ports
[i
]);
1217 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1224 // ----------------------------------------------------------------------------
1225 // wxXmlDoxygenInterface
1226 // ----------------------------------------------------------------------------
1228 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1230 wxXmlDocument index
;
1231 wxXmlNode
*compound
;
1233 LogMessage("Parsing %s...", filename
);
1235 if (!index
.Load(filename
)) {
1236 LogError("can't load %s", filename
);
1240 // start processing the index:
1241 if (index
.GetRoot()->GetName() != "doxygenindex") {
1242 LogError("invalid root node for %s", filename
);
1247 NB: we may need in future to do a version-check here if the
1248 format of the XML generated by doxygen changes.
1249 For now (doxygen version 1.5.5), this check is not required
1250 since AFAIK the XML format never changed since it was introduced.
1253 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1255 // process files referenced by this index file
1256 compound
= index
.GetRoot()->GetChildren();
1259 if (compound
->GetName() == "compound" &&
1260 compound
->GetAttribute("kind") == "class")
1262 wxString refid
= compound
->GetAttribute("refid");
1264 wxFileName
fn(filename
);
1265 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1269 compound
= compound
->GetNext();
1273 if (!CheckParseResults())
1279 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1286 LogMessage("Parsing %s...", filename
);
1288 if (!doc
.Load(filename
)) {
1289 LogError("can't load %s", filename
);
1293 // start processing this compound definition XML
1294 if (doc
.GetRoot()->GetName() != "doxygen") {
1295 LogError("invalid root node for %s", filename
);
1299 // build a list of wx classes
1300 child
= doc
.GetRoot()->GetChildren();
1303 if (child
->GetName() == "compounddef" &&
1304 child
->GetAttribute("kind") == "class")
1308 wxString absoluteFile
, header
;
1310 wxXmlNode
*subchild
= child
->GetChildren();
1313 wxString kind
= subchild
->GetAttribute("kind");
1315 // parse only public&protected functions:
1316 if (subchild
->GetName() == "sectiondef" &&
1317 (kind
== "public-func" || kind
== "protected-func"))
1320 wxXmlNode
*membernode
= subchild
->GetChildren();
1323 if (membernode
->GetName() == "memberdef" &&
1324 membernode
->GetAttribute("kind") == "function")
1328 if (!ParseMethod(membernode
, m
, header
)) {
1329 LogError("The method '%s' could not be added to class '%s'",
1330 m
.GetName(), klass
.GetName());
1334 if (kind
== "public-func")
1335 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1336 else if (kind
== "protected-func")
1337 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1338 else if (kind
== "private-func")
1339 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1341 if (absoluteFile
.IsEmpty())
1342 absoluteFile
= header
;
1343 else if (header
!= absoluteFile
)
1345 LogError("The method '%s' is documented in a different "
1346 "file from others (which belong to '%s') ?",
1347 header
, absoluteFile
);
1354 membernode
= membernode
->GetNext();
1357 // all methods of this class were taken from the header "absoluteFile":
1358 klass
.SetHeader(absoluteFile
);
1360 else if (subchild
->GetName() == "compoundname")
1362 klass
.SetName(subchild
->GetNodeContent());
1364 /*else if (subchild->GetName() == "includes")
1366 // NOTE: we'll get the header from the <location> tags
1367 // scattered inside <memberdef> tags instead of
1368 // this <includes> tag since it does not contain
1369 // the absolute path of the header
1371 klass.SetHeader(subchild->GetNodeContent());
1373 else if (subchild
->GetName() == "detaileddescription")
1375 // identify <onlyfor> custom XML tags
1376 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1379 subchild
= subchild
->GetNext();
1384 m_classes
.Add(klass
);
1386 LogWarning("discarding class '%s' with %d methods...",
1387 klass
.GetName(), klass
.GetMethodCount());
1390 child
= child
->GetNext();
1392 // give feedback to the user about the progress...
1393 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1399 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1401 wxArgumentTypeArray args
;
1404 wxXmlNode
*child
= p
->GetChildren();
1407 if (child
->GetName() == "name")
1408 m
.SetName(child
->GetNodeContent());
1409 else if (child
->GetName() == "type")
1410 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1411 else if (child
->GetName() == "param")
1413 wxString typestr
, namestr
, defstr
, arrstr
;
1414 wxXmlNode
*n
= child
->GetChildren();
1417 if (n
->GetName() == "type")
1418 // if the <type> node has children, they should be all TEXT and <ref> nodes
1419 // and we need to take the text they contain, in the order they appear
1420 typestr
= GetTextFromChildren(n
);
1421 else if (n
->GetName() == "declname")
1422 namestr
= GetTextFromChildren(n
);
1423 else if (n
->GetName() == "defval")
1424 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1425 else if (n
->GetName() == "array")
1426 arrstr
= GetTextFromChildren(n
);
1431 if (typestr
.IsEmpty()) {
1432 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1436 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1438 // can we use preprocessor output to transform the default value
1439 // into the same form which gets processed by wxXmlGccInterface?
1440 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1441 if (it
!= m_preproc
.end())
1442 newarg
.SetDefaultValue(defstr
, it
->second
);
1446 else if (child
->GetName() == "location")
1449 if (child
->GetAttribute("line").ToLong(&line
))
1450 m
.SetLocation((int)line
);
1451 header
= child
->GetAttribute("file");
1453 else if (child
->GetName() == "detaileddescription")
1455 // when a method has a @deprecated tag inside its description,
1456 // Doxygen outputs somewhere nested inside <detaileddescription>
1457 // a <xreftitle>Deprecated</xreftitle> tag.
1458 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1460 // identify <onlyfor> custom XML tags
1461 m
.SetAvailability(GetAvailabilityFor(child
));
1464 child
= child
->GetNext();
1467 m
.SetArgumentTypes(args
);
1468 m
.SetConst(p
->GetAttribute("const")=="yes");
1469 m
.SetStatic(p
->GetAttribute("static")=="yes");
1471 // NOTE: Doxygen is smart enough to mark as virtual those functions
1472 // which are declared virtual in base classes but don't have
1473 // the "virtual" keyword explicitely indicated in the derived
1474 // classes... so we don't need any further logic for virtuals
1476 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1477 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1480 LogError("The prototype '%s' is not valid!", m
.GetAsString());