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
102 bool wxType::IsOk() const
104 // NB: m_strType can contain the :: operator; think to e.g. the
105 // "reverse_iterator_impl<wxString::const_iterator>" type
106 // It can also contain commas, * and & operators etc
108 return !m_strTypeClean
.IsEmpty();
111 bool wxType::operator==(const wxType
& m
) const
113 // brain-dead comparison:
115 if (m_strTypeClean
== m
.m_strTypeClean
&&
116 IsConst() == m
.IsConst() &&
117 IsStatic() == m
.IsStatic() &&
118 IsPointer() == m
.IsPointer() &&
119 IsReference() == m
.IsReference())
123 LogMessage("Type '%s' does not match type '%s'", m_strType
, m
.m_strType
);
129 // ----------------------------------------------------------------------------
131 // ----------------------------------------------------------------------------
133 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
135 m_strDefaultValue
= defval
.Strip(wxString::both
);
136 m_strDefaultValueForCmp
= defvalForCmp
.IsEmpty() ? m_strDefaultValue
: defvalForCmp
.Strip(wxString::both
);
138 // adjust aesthetic form of DefaultValue for the modify mode of ifacecheck:
139 // we may need to write it out in an interface header
140 if (m_strDefaultValue
== "0u")
141 m_strDefaultValue
= "0";
143 // in order to make valid&simple comparison on argument defaults,
144 // we reduce some of the multiple forms in which the same things may appear
146 if (m_strDefaultValueForCmp
== "0u")
147 m_strDefaultValueForCmp
= "0";
149 // fix for unicode strings:
150 m_strDefaultValueForCmp
.Replace("\\000\\000\\000", "");
152 if (m_strDefaultValueForCmp
.StartsWith("wxT(") &&
153 m_strDefaultValueForCmp
.EndsWith(")"))
155 // get rid of the wxT() part
156 unsigned int len
= m_strDefaultValueForCmp
.Len();
157 m_strDefaultValueForCmp
= m_strDefaultValueForCmp
.Mid(4,len
-5);
162 m_strDefaultValueForCmp.Replace("0", "NULL");
164 m_strDefaultValueForCmp.Replace("NULL", "0");
167 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
168 // fix this to avoid false positives
169 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
170 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
173 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
174 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
177 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
179 if ((const wxType
&)(*this) != (const wxType
&)m
)
183 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
184 // numbers; avoid false positives in this case!
185 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
186 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
189 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
191 // maybe the default values are numbers.
192 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
193 // To handle these cases, we try to convert the default value strings to numbers:
194 long def1val
, def2val
;
195 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
196 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
198 if (def1val
== def2val
)
199 return true; // the default values match
203 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
204 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
208 // we deliberately avoid checks on the argument name
214 // ----------------------------------------------------------------------------
216 // ----------------------------------------------------------------------------
218 bool wxMethod::IsOk() const
220 // NOTE: m_retType can be a wxEmptyType, and means that this method
221 // is a ctor or a dtor.
222 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
223 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
227 if (m_strName
.IsEmpty())
230 // a function can't be both const and static or virtual and static!
231 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
232 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
236 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
238 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
239 if (!m_args
[i
].IsOk()) {
240 LogError("'%s' method has invalid %d-th argument type: %s",
241 m_strName
, i
+1, m_args
[i
].GetAsString());
245 // NB: the default value of the arguments can contain pretty much everything
246 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
247 // so we don't do any test on their contents
248 if (m_args
.GetCount()>0)
250 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
251 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
253 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
254 LogError("'%s' method has %d-th argument which has no default value "
255 "(while the previous one had one!)",
260 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
267 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
269 if (GetReturnType() != m
.GetReturnType() ||
270 GetName() != m
.GetName())
273 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
275 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
276 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
280 // compare argument types
281 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
282 if (m_args
[i
] != m
.m_args
[i
])
288 bool wxMethod::operator==(const wxMethod
& m
) const
291 if (IsConst() != m
.IsConst() ||
292 IsStatic() != m
.IsStatic() ||
293 IsVirtual() != m
.IsVirtual() ||
294 IsPureVirtual() != m
.IsPureVirtual() ||
295 IsDeprecated() != m
.IsDeprecated() ||
296 GetAccessSpecifier() != m
.GetAccessSpecifier())
299 // check everything else
300 return MatchesExceptForAttributes(m
);
303 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bCleanDefaultValues
,
304 bool bDeprecated
, bool bAccessSpec
) const
308 // NOTE: for return and argument types, never use wxType::GetAsCleanString
309 // since in that way we'd miss important decorators like &,*,const etc
311 if (m_retType
!=wxEmptyType
)
312 ret
+= m_retType
.GetAsString() + " ";
313 //else; this is a ctor or dtor
315 ret
+= m_strName
+ "(";
317 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
319 ret
+= m_args
[i
].GetAsString();
321 const wxString
& name
= m_args
[i
].GetArgumentName();
322 if (bWithArgumentNames
&& !name
.IsEmpty())
325 const wxString
& def
= bCleanDefaultValues
?
326 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
333 if (m_args
.GetCount()>0)
334 ret
= ret
.Left(ret
.Len()-2);
341 ret
= "static " + ret
;
342 if (m_bVirtual
|| m_bPureVirtual
)
343 ret
= "virtual " + ret
;
346 if (m_bDeprecated
&& bDeprecated
)
347 ret
+= " [deprecated]";
356 case wxMAS_PROTECTED
:
357 ret
+= " [protected]";
368 void wxMethod::Dump(wxTextOutputStream
& stream
) const
370 stream
<< "[" + m_retType
.GetAsString() + "]";
371 stream
<< "[" + m_strName
+ "]";
373 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
374 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
375 "=" + m_args
[i
].GetDefaultValue() + "]";
382 stream
<< " VIRTUAL";
384 stream
<< " PURE-VIRTUAL";
386 stream
<< " DEPRECATED";
391 // ----------------------------------------------------------------------------
393 // ----------------------------------------------------------------------------
395 wxString
wxClass::GetNameWithoutTemplate() const
397 // NB: I'm not sure this is the right terminology for this function!
399 if (m_strName
.Contains("<"))
400 return m_strName
.Left(m_strName
.Find("<"));
404 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
406 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
407 // named wxWritableCharTypeBuffer, without the <...> part!
409 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
415 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
417 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
423 void wxClass::Dump(wxTextOutputStream
& out
) const
425 out
<< m_strName
+ "\n";
427 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
429 // dump all our methods
431 m_methods
[i
].Dump(out
);
438 bool wxClass::CheckConsistency() const
440 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
441 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
442 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
444 LogError("class %s has two methods with the same prototype: '%s'",
445 m_strName
, m_methods
[i
].GetAsString());
447 ((wxClass
*)this)->m_methods
.RemoveAt(j
);
454 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
456 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
457 if (m_methods
[i
] == m
)
458 return &m_methods
[i
];
462 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
464 wxMethodPtrArray ret
;
466 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
467 if (m_methods
[i
].GetName() == name
)
468 ret
.Add(&m_methods
[i
]);
474 // ----------------------------------------------------------------------------
476 // ----------------------------------------------------------------------------
478 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
480 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
482 // sort alphabetically
483 return item1
->GetName().Cmp(item2
->GetName());
486 void wxXmlInterface::Dump(const wxString
& filename
)
488 wxFFileOutputStream
apioutput( filename
);
489 wxTextOutputStream
apiout( apioutput
);
491 // dump the classes in alphabetical order
492 wxSortedClassArray
sorted(CompareWxClassObjects
);
493 sorted
.Alloc(m_classes
.GetCount());
494 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
495 sorted
.Add(&m_classes
[i
]);
497 // now they have been sorted
498 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
499 sorted
[i
]->Dump(apiout
);
502 bool wxXmlInterface::CheckParseResults() const
504 // this check can be quite slow, so do it only for debug releases:
506 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
507 if (!m_classes
[i
].CheckConsistency())
514 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
518 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
519 if (m_classes
[i
].GetHeader() == headerfile
)
520 ret
.Add(&m_classes
[i
]);
526 // ----------------------------------------------------------------------------
527 // wxXmlGccInterface helper declarations
528 // ----------------------------------------------------------------------------
530 // or-able flags for a toResolveTypeItem->attrib:
531 #define ATTRIB_CONST 1
532 #define ATTRIB_REFERENCE 2
533 #define ATTRIB_POINTER 4
534 #define ATTRIB_ARRAY 8
536 #define GCCXML_BASE 35
538 class toResolveTypeItem
541 toResolveTypeItem() { attribs
=0; }
542 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
543 : ref(refID
), attribs(attribint
) {}
545 unsigned long ref
, // the referenced type's ID
546 attribs
; // the attributes of this reference
551 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
552 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
553 wxIntegerHash
, wxIntegerEqual
,
554 wxToResolveTypeHashMap
);
556 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
557 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
558 wxIntegerHash
, wxIntegerEqual
,
559 wxClassMemberIdHashMap
);
562 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
566 // utility to parse gccXML ID values;
567 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
568 // but is a little bit faster
569 bool getID(unsigned long *id
, const wxStringCharType
* str
)
571 wxStringCharType
*end
;
572 #if wxUSE_UNICODE_WCHAR
573 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
575 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
578 // return true only if scan was stopped by the terminating NUL and
579 // if the string was not empty to start with and no under/overflow
581 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
588 // utility specialized to parse efficiently the gccXML list of IDs which occur
589 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
590 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
592 #if wxUSE_UNICODE_WCHAR
593 size_t len
= wcslen(str
);
595 size_t len
= strlen(str
);
598 if (len
== 0 || str
[0] != '_')
601 const wxStringCharType
*curpos
= str
,
603 wxStringCharType
*nexttoken
;
607 // curpos always points to the underscore of the next token to parse:
608 #if wxUSE_UNICODE_WCHAR
609 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
611 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
613 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
616 // advance current position
617 curpos
= nexttoken
+ 1;
619 // add this ID to the hashmap
620 wxClassMemberIdHashMap::value_type
v(id
, p
);
628 // ----------------------------------------------------------------------------
630 // ----------------------------------------------------------------------------
632 bool wxXmlGccInterface::Parse(const wxString
& filename
)
638 LogMessage("Parsing %s...", filename
);
640 if (!doc
.Load(filename
)) {
641 LogError("can't load %s", filename
);
645 // start processing the XML file
646 if (doc
.GetRoot()->GetName() != "GCC_XML") {
647 LogError("invalid root node for %s", filename
);
651 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
654 #define MIN_REVISION 120
656 if (!version
.StartsWith("1."))
660 unsigned long rev
= 0;
661 if (!version
.Mid(2).ToULong(&rev
))
664 if (rev
< MIN_REVISION
)
670 LogError("The version of GCC-XML used for the creation of %s is too old; "
671 "the cvs_revision attribute of the root node reports '%s', "
672 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
676 wxToResolveTypeHashMap toResolveTypes
;
677 wxClassMemberIdHashMap members
;
678 wxTypeIdHashMap types
;
679 wxTypeIdHashMap files
;
680 wxTypeIdHashMap typedefs
;
682 // prealloc quite a lot of memory!
683 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
685 // build a list of wx classes and in general of all existent types
686 child
= doc
.GetRoot()->GetChildren();
689 const wxString
& n
= child
->GetName();
691 unsigned long id
= 0;
692 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
694 // NOTE: <File> nodes can have an id == "f0"...
696 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
702 wxString cname
= child
->GetAttribute("name");
703 if (cname
.IsEmpty()) {
704 LogError("Invalid empty name for '%s' node", n
);
708 // only register wx classes (do remember also the IDs of their members)
709 if (cname
.StartsWith("wx"))
711 // NB: "file" attribute contains an ID value that we'll resolve later
712 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
714 const wxString
& ids
= child
->GetAttribute("members");
717 if (child
->GetAttribute("incomplete") != "1") {
718 LogError("Invalid member IDs for '%s' class node: %s",
719 cname
, child
->GetAttribute("id"));
722 //else: don't warn the user; it looks like "incomplete" classes
723 // never have any member...
727 // decode the non-empty list of IDs:
728 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
729 LogError("Invalid member IDs for '%s' class node: %s",
730 cname
, child
->GetAttribute("id"));
736 // register this class also as possible return/argument type:
739 else if (n
== "Typedef")
741 unsigned long typeId
= 0;
742 if (!getID(&typeId
, child
->GetAttribute("type"))) {
743 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
747 // this typedef node tell us that every type referenced with the
748 // "typeId" ID should be called with another name:
749 wxString name
= child
->GetAttribute("name");
751 // save this typedef in a separate hashmap...
752 typedefs
[typeId
] = name
;
756 else if (n
== "PointerType" || n
== "ReferenceType" ||
757 n
== "CvQualifiedType" || n
== "ArrayType")
759 unsigned long type
= 0;
760 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
761 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
765 unsigned long attr
= 0;
766 if (n
== "PointerType")
767 attr
= ATTRIB_POINTER
;
768 else if (n
== "ReferenceType")
769 attr
= ATTRIB_REFERENCE
;
770 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
772 else if (n
== "ArrayType")
775 // these nodes make reference to other types... we'll resolve them later
776 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
778 else if (n
== "FunctionType" || n
== "MethodType")
781 TODO: parsing FunctionType and MethodType nodes is not as easy
782 as for other "simple" types.
786 wxXmlNode
*arg
= child
->GetChildren();
789 if (arg
->GetName() == "Argument")
790 argstr
+= arg
->GetAttribute("type") + ", ";
791 arg
= arg
->GetNext();
794 if (argstr
.Len() > 0)
795 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
797 // these nodes make reference to other types... we'll resolve them later
798 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
799 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
801 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
802 // to a typedef later...
804 else if (n
== "File")
806 if (!child
->GetAttribute("id").StartsWith("f")) {
807 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
811 // just ignore this node... all file IDs/names were already parsed
812 files
[id
] = child
->GetAttribute("name");
816 // we register everything else as a possible return/argument type:
817 const wxString
& name
= child
->GetAttribute("name");
822 //typeNames.Add(name);
827 // this may happen with unnamed structs/union, special ctors,
828 // or other exotic things which we are not interested to, since
829 // they're never used as return/argument types by wxWidgets methods
832 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
833 n
, child
->GetAttribute("id"));
839 child
= child
->GetNext();
841 // give feedback to the user about the progress...
842 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
845 // some nodes with IDs referenced by methods as return/argument types, do reference
846 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
847 // thus we need to resolve their name iteratively:
848 while (toResolveTypes
.size()>0)
851 LogMessage("%d types were collected; %d types need yet to be resolved...",
852 types
.size(), toResolveTypes
.size());
854 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
855 i
!= toResolveTypes
.end();)
857 unsigned long id
= i
->first
;
858 unsigned long referenced
= i
->second
.ref
;
860 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
861 if (primary
!= types
.end())
863 // this to-resolve-type references a "primary" type
865 wxString newtype
= primary
->second
;
866 int attribs
= i
->second
.attribs
;
868 // attribs may contain a combination of ATTRIB_* flags:
869 if (attribs
& ATTRIB_CONST
)
870 newtype
= "const " + newtype
;
871 if (attribs
& ATTRIB_REFERENCE
)
872 newtype
= newtype
+ "&";
873 if (attribs
& ATTRIB_POINTER
)
874 newtype
= newtype
+ "*";
875 if (attribs
& ATTRIB_ARRAY
)
876 newtype
= newtype
+ "[]";
878 // add the resolved type to the list of "primary" types
879 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
880 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
884 // this one has been resolved; erase it through its iterator!
885 toResolveTypes
.erase(i
);
887 // now iterator i is invalid; assign it again to the beginning
888 i
= toResolveTypes
.begin();
892 // then search in the referenced types themselves:
893 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
894 if (idx2
!= toResolveTypes
.end())
896 // merge this to-resolve-type with the idx2->second type
897 i
->second
.ref
= idx2
->second
.ref
;
898 i
->second
.attribs
|= idx2
->second
.attribs
;
900 // this type will eventually be solved in the next while() iteration
905 LogError("Cannot solve '%d' reference type!", referenced
);
912 // resolve header names
913 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
915 unsigned long fileID
= 0;
916 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
917 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
922 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
923 if (idx
== files
.end())
926 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
929 m_classes
[i
].SetHeader(idx
->second
);
932 // build the list of the wx methods
933 child
= doc
.GetRoot()->GetChildren();
936 wxString n
= child
->GetName(), acc
= child
->GetAttribute("access");
938 // only register public&protected methods
939 if ((acc
== "public" || acc
== "protected") &&
940 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
942 unsigned long id
= 0;
943 if (!getID(&id
, child
->GetAttribute("id"))) {
944 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
948 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
949 if (it
!= members
.end())
951 wxClass
*p
= it
->second
;
953 // this <Method> node is a method of the i-th class!
955 if (!ParseMethod(child
, types
, newfunc
)) {
956 LogError("The method '%s' could not be added to class '%s'",
957 child
->GetAttribute("demangled"), p
->GetName());
961 // do some additional check that we can do only here:
963 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
964 LogError("The method '%s' does not seem to be a ctor for '%s'",
965 newfunc
.GetName(), p
->GetName());
968 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
969 LogError("The method '%s' does not seem to be a dtor for '%s'",
970 newfunc
.GetName(), p
->GetName());
974 p
->AddMethod(newfunc
);
978 child
= child
->GetNext();
980 // give feedback to the user about the progress...
981 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
984 if (!CheckParseResults())
990 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
991 const wxTypeIdHashMap
& types
,
995 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
996 if (p
->GetName() == "Destructor")
998 else if (p
->GetName() == "OperatorMethod")
999 name
= "operator" + name
;
1001 // resolve return type
1003 unsigned long retid
= 0;
1004 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
1006 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
1007 LogError("Empty return ID for method '%s', with ID '%s'",
1008 name
, p
->GetAttribute("id"));
1014 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
1015 if (retidx
== types
.end()) {
1016 LogError("Could not find return type ID '%s'", retid
);
1020 ret
= wxType(retidx
->second
);
1022 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
1023 retidx
->second
, name
, p
->GetAttribute("id"));
1028 // resolve argument types
1029 wxArgumentTypeArray argtypes
;
1030 wxXmlNode
*arg
= p
->GetChildren();
1033 if (arg
->GetName() == "Argument")
1035 unsigned long id
= 0;
1036 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1037 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1038 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1042 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1043 if (idx
== types
.end()) {
1044 LogError("Could not find argument type ID '%s'", id
);
1048 argtypes
.Add(wxArgumentType(idx
->second
,
1049 arg
->GetAttribute("default"),
1050 arg
->GetAttribute("name")));
1053 arg
= arg
->GetNext();
1056 m
.SetReturnType(ret
);
1058 m
.SetArgumentTypes(argtypes
);
1059 m
.SetConst(p
->GetAttribute("const") == "1");
1060 m
.SetStatic(p
->GetAttribute("static") == "1");
1062 // NOTE: gccxml is smart enough to mark as virtual those functions
1063 // which are declared virtual in base classes but don't have
1064 // the "virtual" keyword explicitely indicated in the derived
1065 // classes... so we don't need any further logic for virtuals
1067 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1068 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1069 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1071 // decode access specifier
1072 if (p
->GetAttribute("access") == "public")
1073 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1074 else if (p
->GetAttribute("access") == "protected")
1075 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1076 else if (p
->GetAttribute("access") == "private")
1077 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1080 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1089 // ----------------------------------------------------------------------------
1090 // wxXmlDoxygenInterface global helpers
1091 // ----------------------------------------------------------------------------
1093 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1097 // consider the tree
1099 // <a><b>this</b> is a <b>string</b></a>
1108 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1109 // this function returns "this is a string"
1111 wxXmlNode
*ref
= n
->GetChildren();
1113 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1114 text
+= ref
->GetNodeContent();
1115 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1116 text
+= ref
->GetContent();
1118 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1120 ref
= ref
->GetNext();
1126 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1131 wxXmlNode
*p
= parent
->GetChildren();
1134 switch (p
->GetType())
1136 case wxXML_TEXT_NODE
:
1137 if (p
->GetContent() == name
)
1141 case wxXML_ELEMENT_NODE
:
1142 // recurse into this node...
1143 if (HasTextNodeContaining(p
, name
))
1158 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1163 const wxXmlNode
*p
= parent
->GetChildren();
1166 if (p
->GetName() == name
)
1169 // search recursively in the children of this node
1170 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1180 int GetAvailabilityFor(const wxXmlNode
*node
)
1182 // identify <onlyfor> custom XML tags
1183 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1185 return wxPORT_UNKNOWN
;
1187 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1188 int nAvail
= wxPORT_UNKNOWN
;
1189 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1191 if (!ports
[i
].StartsWith("wx")) {
1192 LogError("unexpected port ID '%s'", ports
[i
]);
1196 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1203 // ----------------------------------------------------------------------------
1204 // wxXmlDoxygenInterface
1205 // ----------------------------------------------------------------------------
1207 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1209 wxXmlDocument index
;
1210 wxXmlNode
*compound
;
1212 LogMessage("Parsing %s...", filename
);
1214 if (!index
.Load(filename
)) {
1215 LogError("can't load %s", filename
);
1219 // start processing the index:
1220 if (index
.GetRoot()->GetName() != "doxygenindex") {
1221 LogError("invalid root node for %s", filename
);
1226 NB: we may need in future to do a version-check here if the
1227 format of the XML generated by doxygen changes.
1228 For now (doxygen version 1.5.5), this check is not required
1229 since AFAIK the XML format never changed since it was introduced.
1232 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1234 // process files referenced by this index file
1235 compound
= index
.GetRoot()->GetChildren();
1238 if (compound
->GetName() == "compound" &&
1239 compound
->GetAttribute("kind") == "class")
1241 wxString refid
= compound
->GetAttribute("refid");
1243 wxFileName
fn(filename
);
1244 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1248 compound
= compound
->GetNext();
1252 if (!CheckParseResults())
1258 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1265 LogMessage("Parsing %s...", filename
);
1267 if (!doc
.Load(filename
)) {
1268 LogError("can't load %s", filename
);
1272 // start processing this compound definition XML
1273 if (doc
.GetRoot()->GetName() != "doxygen") {
1274 LogError("invalid root node for %s", filename
);
1278 // build a list of wx classes
1279 child
= doc
.GetRoot()->GetChildren();
1282 if (child
->GetName() == "compounddef" &&
1283 child
->GetAttribute("kind") == "class")
1287 wxString absoluteFile
, header
;
1289 wxXmlNode
*subchild
= child
->GetChildren();
1292 wxString kind
= subchild
->GetAttribute("kind");
1294 // parse only public&protected functions:
1295 if (subchild
->GetName() == "sectiondef" &&
1296 (kind
== "public-func" || kind
== "protected-func"))
1299 wxXmlNode
*membernode
= subchild
->GetChildren();
1302 if (membernode
->GetName() == "memberdef" &&
1303 membernode
->GetAttribute("kind") == "function")
1307 if (!ParseMethod(membernode
, m
, header
)) {
1308 LogError("The method '%s' could not be added to class '%s'",
1309 m
.GetName(), klass
.GetName());
1313 if (kind
== "public-func")
1314 m
.SetAccessSpecifier(wxMAS_PUBLIC
);
1315 else if (kind
== "protected-func")
1316 m
.SetAccessSpecifier(wxMAS_PROTECTED
);
1317 else if (kind
== "private-func")
1318 m
.SetAccessSpecifier(wxMAS_PRIVATE
);
1320 if (absoluteFile
.IsEmpty())
1321 absoluteFile
= header
;
1322 else if (header
!= absoluteFile
)
1324 LogError("The method '%s' is documented in a different "
1325 "file from others (which belong to '%s') ?",
1326 header
, absoluteFile
);
1333 membernode
= membernode
->GetNext();
1336 // all methods of this class were taken from the header "absoluteFile":
1337 klass
.SetHeader(absoluteFile
);
1339 else if (subchild
->GetName() == "compoundname")
1341 klass
.SetName(subchild
->GetNodeContent());
1343 /*else if (subchild->GetName() == "includes")
1345 // NOTE: we'll get the header from the <location> tags
1346 // scattered inside <memberdef> tags instead of
1347 // this <includes> tag since it does not contain
1348 // the absolute path of the header
1350 klass.SetHeader(subchild->GetNodeContent());
1352 else if (subchild
->GetName() == "detaileddescription")
1354 // identify <onlyfor> custom XML tags
1355 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1358 subchild
= subchild
->GetNext();
1363 m_classes
.Add(klass
);
1365 LogWarning("discarding class '%s' with %d methods...",
1366 klass
.GetName(), klass
.GetMethodCount());
1369 child
= child
->GetNext();
1371 // give feedback to the user about the progress...
1372 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1378 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1380 wxArgumentTypeArray args
;
1383 wxXmlNode
*child
= p
->GetChildren();
1386 if (child
->GetName() == "name")
1387 m
.SetName(child
->GetNodeContent());
1388 else if (child
->GetName() == "type")
1389 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1390 else if (child
->GetName() == "param")
1392 wxString typestr
, namestr
, defstr
, arrstr
;
1393 wxXmlNode
*n
= child
->GetChildren();
1396 if (n
->GetName() == "type")
1397 // if the <type> node has children, they should be all TEXT and <ref> nodes
1398 // and we need to take the text they contain, in the order they appear
1399 typestr
= GetTextFromChildren(n
);
1400 else if (n
->GetName() == "declname")
1401 namestr
= GetTextFromChildren(n
);
1402 else if (n
->GetName() == "defval")
1403 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1404 else if (n
->GetName() == "array")
1405 arrstr
= GetTextFromChildren(n
);
1410 if (typestr
.IsEmpty()) {
1411 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1415 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1417 // can we use preprocessor output to transform the default value
1418 // into the same form which gets processed by wxXmlGccInterface?
1419 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1420 if (it
!= m_preproc
.end())
1421 newarg
.SetDefaultValue(defstr
, it
->second
);
1425 else if (child
->GetName() == "location")
1428 if (child
->GetAttribute("line").ToLong(&line
))
1429 m
.SetLocation((int)line
);
1430 header
= child
->GetAttribute("file");
1432 else if (child
->GetName() == "detaileddescription")
1434 // when a method has a @deprecated tag inside its description,
1435 // Doxygen outputs somewhere nested inside <detaileddescription>
1436 // a <xreftitle>Deprecated</xreftitle> tag.
1437 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1439 // identify <onlyfor> custom XML tags
1440 m
.SetAvailability(GetAvailabilityFor(child
));
1443 child
= child
->GetNext();
1446 m
.SetArgumentTypes(args
);
1447 m
.SetConst(p
->GetAttribute("const")=="yes");
1448 m
.SetStatic(p
->GetAttribute("static")=="yes");
1450 // NOTE: Doxygen is smart enough to mark as virtual those functions
1451 // which are declared virtual in base classes but don't have
1452 // the "virtual" keyword explicitely indicated in the derived
1453 // classes... so we don't need any further logic for virtuals
1455 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1456 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1459 LogError("The prototype '%s' is not valid!", m
.GetAsString());