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";
150 m_strDefaultValueForCmp.Replace("0", "NULL");
152 m_strDefaultValueForCmp.Replace("NULL", "0");
155 // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
156 // fix this to avoid false positives
157 m_strDefaultValueForCmp
.Replace("wxDateTime::", "");
158 m_strDefaultValueForCmp
.Replace("wxStockGDI::", ""); // same story for some other classes
161 if (m_strDefaultValueForCmp
.Contains("wxGetTranslation"))
162 m_strDefaultValueForCmp
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
165 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
167 if ((const wxType
&)(*this) != (const wxType
&)m
)
171 // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
172 // numbers; avoid false positives in this case!
173 if (m_strArgName
== m
.m_strArgName
&& m_strArgName
== "style" &&
174 (m_strDefaultValueForCmp
.IsNumber() || m
.m_strDefaultValueForCmp
.IsNumber()))
177 if (m_strDefaultValueForCmp
!= m
.m_strDefaultValueForCmp
)
179 // maybe the default values are numbers.
180 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
181 // To handle these cases, we try to convert the default value strings to numbers:
182 long def1val
, def2val
;
183 if (m_strDefaultValueForCmp
.ToLong(&def1val
, 0 /* auto-detect */) &&
184 m
.m_strDefaultValueForCmp
.ToLong(&def2val
, 0 /* auto-detect */))
186 if (def1val
== def2val
)
187 return true; // the default values match
191 LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
192 m_strType
, m_strDefaultValueForCmp
, m
.m_strType
, m
.m_strDefaultValueForCmp
);
196 // we deliberately avoid checks on the argument name
202 // ----------------------------------------------------------------------------
204 // ----------------------------------------------------------------------------
206 bool wxMethod::IsOk() const
208 // NOTE: m_retType can be a wxEmptyType, and means that this method
209 // is a ctor or a dtor.
210 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
211 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
215 if (m_strName
.IsEmpty())
218 // a function can't be both const and static or virtual and static!
219 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
220 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
224 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
226 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
227 if (!m_args
[i
].IsOk()) {
228 LogError("'%s' method has invalid %d-th argument type: %s",
229 m_strName
, i
+1, m_args
[i
].GetAsString());
233 // NB: the default value of the arguments can contain pretty much everything
234 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
235 // so we don't do any test on their contents
236 if (m_args
.GetCount()>0)
238 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
239 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
241 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
242 LogError("'%s' method has %d-th argument which has no default value "
243 "(while the previous one had one!)",
248 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
255 bool wxMethod::MatchesExceptForAttributes(const wxMethod
& m
) const
257 if (GetReturnType() != m
.GetReturnType() ||
258 GetName() != m
.GetName())
261 if (m_args
.GetCount()!=m
.m_args
.GetCount()) {
263 LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
264 m_strName
, m_args
.GetCount(), m_strName
, m
.m_args
.GetCount());
268 // compare argument types
269 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
270 if (m_args
[i
] != m
.m_args
[i
])
276 bool wxMethod::operator==(const wxMethod
& m
) const
279 if (IsConst() != m
.IsConst() ||
280 IsStatic() != m
.IsStatic() ||
281 IsVirtual() != m
.IsVirtual() ||
282 IsPureVirtual() != m
.IsPureVirtual() ||
283 IsDeprecated() != m
.IsDeprecated())
286 // check everything else
287 return MatchesExceptForAttributes(m
);
290 wxString
wxMethod::GetAsString(bool bWithArgumentNames
, bool bClean
, bool bDeprecated
) const
294 // NOTE: for return and argument types, never use wxType::GetAsCleanString
295 // since in that way we'd miss important decorators like &,*,const etc
297 if (m_retType
!=wxEmptyType
)
298 ret
+= m_retType
.GetAsString() + " ";
299 //else; this is a ctor or dtor
301 ret
+= m_strName
+ "(";
303 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
305 ret
+= m_args
[i
].GetAsString();
307 const wxString
& name
= m_args
[i
].GetArgumentName();
308 if (bWithArgumentNames
&& !name
.IsEmpty())
311 const wxString
& def
= bClean
?
312 m_args
[i
].GetDefaultCleanValue() : m_args
[i
].GetDefaultValue();
319 if (m_args
.GetCount()>0)
320 ret
= ret
.Left(ret
.Len()-2);
327 ret
= "static " + ret
;
328 if (m_bVirtual
|| m_bPureVirtual
)
329 ret
= "virtual " + ret
;
332 if (m_bDeprecated
&& bDeprecated
)
333 ret
+= " [deprecated]";
338 void wxMethod::Dump(wxTextOutputStream
& stream
) const
340 stream
<< "[" + m_retType
.GetAsString() + "]";
341 stream
<< "[" + m_strName
+ "]";
343 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
344 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
345 "=" + m_args
[i
].GetDefaultValue() + "]";
352 stream
<< " VIRTUAL";
354 stream
<< " PURE-VIRTUAL";
356 stream
<< " DEPRECATED";
361 // ----------------------------------------------------------------------------
363 // ----------------------------------------------------------------------------
365 wxString
wxClass::GetNameWithoutTemplate() const
367 // NB: I'm not sure this is the right terminology for this function!
369 if (m_strName
.Contains("<"))
370 return m_strName
.Left(m_strName
.Find("<"));
374 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
376 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
377 // named wxWritableCharTypeBuffer, without the <...> part!
379 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
385 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
387 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
393 void wxClass::Dump(wxTextOutputStream
& out
) const
395 out
<< m_strName
+ "\n";
397 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
399 // dump all our methods
401 m_methods
[i
].Dump(out
);
408 bool wxClass::CheckConsistency() const
410 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
411 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
412 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
414 LogError("class %s has two methods with the same prototype: '%s'",
415 m_strName
, m_methods
[i
].GetAsString());
417 ((wxClass
*)this)->m_methods
.RemoveAt(j
);
424 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
426 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
427 if (m_methods
[i
] == m
)
428 return &m_methods
[i
];
432 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
434 wxMethodPtrArray ret
;
436 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
437 if (m_methods
[i
].GetName() == name
)
438 ret
.Add(&m_methods
[i
]);
444 // ----------------------------------------------------------------------------
446 // ----------------------------------------------------------------------------
448 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
450 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
452 // sort alphabetically
453 return item1
->GetName().Cmp(item2
->GetName());
456 void wxXmlInterface::Dump(const wxString
& filename
)
458 wxFFileOutputStream
apioutput( filename
);
459 wxTextOutputStream
apiout( apioutput
);
461 // dump the classes in alphabetical order
462 wxSortedClassArray
sorted(CompareWxClassObjects
);
463 sorted
.Alloc(m_classes
.GetCount());
464 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
465 sorted
.Add(&m_classes
[i
]);
467 // now they have been sorted
468 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
469 sorted
[i
]->Dump(apiout
);
472 bool wxXmlInterface::CheckParseResults() const
474 // this check can be quite slow, so do it only for debug releases:
476 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
477 if (!m_classes
[i
].CheckConsistency())
484 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
488 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
489 if (m_classes
[i
].GetHeader() == headerfile
)
490 ret
.Add(&m_classes
[i
]);
496 // ----------------------------------------------------------------------------
497 // wxXmlGccInterface helper declarations
498 // ----------------------------------------------------------------------------
500 // or-able flags for a toResolveTypeItem->attrib:
501 #define ATTRIB_CONST 1
502 #define ATTRIB_REFERENCE 2
503 #define ATTRIB_POINTER 4
504 #define ATTRIB_ARRAY 8
506 #define GCCXML_BASE 35
508 class toResolveTypeItem
511 toResolveTypeItem() { attribs
=0; }
512 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
513 : ref(refID
), attribs(attribint
) {}
515 unsigned long ref
, // the referenced type's ID
516 attribs
; // the attributes of this reference
521 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
522 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
523 wxIntegerHash
, wxIntegerEqual
,
524 wxToResolveTypeHashMap
);
526 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
527 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
528 wxIntegerHash
, wxIntegerEqual
,
529 wxClassMemberIdHashMap
);
532 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
536 // utility to parse gccXML ID values;
537 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
538 // but is a little bit faster
539 bool getID(unsigned long *id
, const wxStringCharType
* str
)
541 wxStringCharType
*end
;
542 #if wxUSE_UNICODE_WCHAR
543 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
545 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
548 // return true only if scan was stopped by the terminating NUL and
549 // if the string was not empty to start with and no under/overflow
551 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
558 // utility specialized to parse efficiently the gccXML list of IDs which occur
559 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
560 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
562 #if wxUSE_UNICODE_WCHAR
563 size_t len
= wcslen(str
);
565 size_t len
= strlen(str
);
568 if (len
== 0 || str
[0] != '_')
571 const wxStringCharType
*curpos
= str
,
573 wxStringCharType
*nexttoken
;
577 // curpos always points to the underscore of the next token to parse:
578 #if wxUSE_UNICODE_WCHAR
579 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
581 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
583 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
586 // advance current position
587 curpos
= nexttoken
+ 1;
589 // add this ID to the hashmap
590 wxClassMemberIdHashMap::value_type
v(id
, p
);
598 // ----------------------------------------------------------------------------
600 // ----------------------------------------------------------------------------
602 bool wxXmlGccInterface::Parse(const wxString
& filename
)
608 LogMessage("Parsing %s...", filename
);
610 if (!doc
.Load(filename
)) {
611 LogError("can't load %s", filename
);
615 // start processing the XML file
616 if (doc
.GetRoot()->GetName() != "GCC_XML") {
617 LogError("invalid root node for %s", filename
);
621 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
624 #define MIN_REVISION 120
626 if (!version
.StartsWith("1."))
630 unsigned long rev
= 0;
631 if (!version
.Mid(2).ToULong(&rev
))
634 if (rev
< MIN_REVISION
)
640 LogError("The version of GCC-XML used for the creation of %s is too old; "
641 "the cvs_revision attribute of the root node reports '%s', "
642 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
646 wxToResolveTypeHashMap toResolveTypes
;
647 wxClassMemberIdHashMap members
;
648 wxTypeIdHashMap types
;
649 wxTypeIdHashMap files
;
650 wxTypeIdHashMap typedefs
;
652 // prealloc quite a lot of memory!
653 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
655 // build a list of wx classes and in general of all existent types
656 child
= doc
.GetRoot()->GetChildren();
659 const wxString
& n
= child
->GetName();
661 unsigned long id
= 0;
662 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
664 // NOTE: <File> nodes can have an id == "f0"...
666 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
672 wxString cname
= child
->GetAttribute("name");
673 if (cname
.IsEmpty()) {
674 LogError("Invalid empty name for '%s' node", n
);
678 // only register wx classes (do remember also the IDs of their members)
679 if (cname
.StartsWith("wx"))
681 // NB: "file" attribute contains an ID value that we'll resolve later
682 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
684 const wxString
& ids
= child
->GetAttribute("members");
687 if (child
->GetAttribute("incomplete") != "1") {
688 LogError("Invalid member IDs for '%s' class node: %s",
689 cname
, child
->GetAttribute("id"));
692 //else: don't warn the user; it looks like "incomplete" classes
693 // never have any member...
697 // decode the non-empty list of IDs:
698 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
699 LogError("Invalid member IDs for '%s' class node: %s",
700 cname
, child
->GetAttribute("id"));
706 // register this class also as possible return/argument type:
709 else if (n
== "Typedef")
711 unsigned long typeId
= 0;
712 if (!getID(&typeId
, child
->GetAttribute("type"))) {
713 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
717 // this typedef node tell us that every type referenced with the
718 // "typeId" ID should be called with another name:
719 wxString name
= child
->GetAttribute("name");
721 // save this typedef in a separate hashmap...
722 typedefs
[typeId
] = name
;
726 else if (n
== "PointerType" || n
== "ReferenceType" ||
727 n
== "CvQualifiedType" || n
== "ArrayType")
729 unsigned long type
= 0;
730 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
731 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
735 unsigned long attr
= 0;
736 if (n
== "PointerType")
737 attr
= ATTRIB_POINTER
;
738 else if (n
== "ReferenceType")
739 attr
= ATTRIB_REFERENCE
;
740 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
742 else if (n
== "ArrayType")
745 // these nodes make reference to other types... we'll resolve them later
746 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
748 else if (n
== "FunctionType" || n
== "MethodType")
751 TODO: parsing FunctionType and MethodType nodes is not as easy
752 as for other "simple" types.
756 wxXmlNode
*arg
= child
->GetChildren();
759 if (arg
->GetName() == "Argument")
760 argstr
+= arg
->GetAttribute("type") + ", ";
761 arg
= arg
->GetNext();
764 if (argstr
.Len() > 0)
765 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
767 // these nodes make reference to other types... we'll resolve them later
768 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
769 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
771 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
772 // to a typedef later...
774 else if (n
== "File")
776 if (!child
->GetAttribute("id").StartsWith("f")) {
777 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
781 // just ignore this node... all file IDs/names were already parsed
782 files
[id
] = child
->GetAttribute("name");
786 // we register everything else as a possible return/argument type:
787 const wxString
& name
= child
->GetAttribute("name");
792 //typeNames.Add(name);
797 // this may happen with unnamed structs/union, special ctors,
798 // or other exotic things which we are not interested to, since
799 // they're never used as return/argument types by wxWidgets methods
802 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
803 n
, child
->GetAttribute("id"));
809 child
= child
->GetNext();
811 // give feedback to the user about the progress...
812 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
815 // some nodes with IDs referenced by methods as return/argument types, do reference
816 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
817 // thus we need to resolve their name iteratively:
818 while (toResolveTypes
.size()>0)
821 LogMessage("%d types were collected; %d types need yet to be resolved...",
822 types
.size(), toResolveTypes
.size());
824 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
825 i
!= toResolveTypes
.end();)
827 unsigned long id
= i
->first
;
828 unsigned long referenced
= i
->second
.ref
;
830 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
831 if (primary
!= types
.end())
833 // this to-resolve-type references a "primary" type
835 wxString newtype
= primary
->second
;
836 int attribs
= i
->second
.attribs
;
838 // attribs may contain a combination of ATTRIB_* flags:
839 if (attribs
& ATTRIB_CONST
)
840 newtype
= "const " + newtype
;
841 if (attribs
& ATTRIB_REFERENCE
)
842 newtype
= newtype
+ "&";
843 if (attribs
& ATTRIB_POINTER
)
844 newtype
= newtype
+ "*";
845 if (attribs
& ATTRIB_ARRAY
)
846 newtype
= newtype
+ "[]";
848 // add the resolved type to the list of "primary" types
849 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
850 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
854 // this one has been resolved; erase it through its iterator!
855 toResolveTypes
.erase(i
);
857 // now iterator i is invalid; assign it again to the beginning
858 i
= toResolveTypes
.begin();
862 // then search in the referenced types themselves:
863 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
864 if (idx2
!= toResolveTypes
.end())
866 // merge this to-resolve-type with the idx2->second type
867 i
->second
.ref
= idx2
->second
.ref
;
868 i
->second
.attribs
|= idx2
->second
.attribs
;
870 // this type will eventually be solved in the next while() iteration
875 LogError("Cannot solve '%d' reference type!", referenced
);
882 // resolve header names
883 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
885 unsigned long fileID
= 0;
886 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
887 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
892 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
893 if (idx
== files
.end())
896 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
899 m_classes
[i
].SetHeader(idx
->second
);
902 // build the list of the wx methods
903 child
= doc
.GetRoot()->GetChildren();
906 wxString n
= child
->GetName();
908 // only register public methods
909 if (child
->GetAttribute("access") == "public" &&
910 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
912 unsigned long id
= 0;
913 if (!getID(&id
, child
->GetAttribute("id"))) {
914 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
918 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
919 if (it
!= members
.end())
921 wxClass
*p
= it
->second
;
923 // this <Method> node is a method of the i-th class!
925 if (!ParseMethod(child
, types
, newfunc
)) {
926 LogError("The method '%s' could not be added to class '%s'",
927 child
->GetAttribute("demangled"), p
->GetName());
931 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
932 LogError("The method '%s' does not seem to be a ctor for '%s'",
933 newfunc
.GetName(), p
->GetName());
936 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
937 LogError("The method '%s' does not seem to be a dtor for '%s'",
938 newfunc
.GetName(), p
->GetName());
942 p
->AddMethod(newfunc
);
946 child
= child
->GetNext();
948 // give feedback to the user about the progress...
949 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
952 if (!CheckParseResults())
958 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
959 const wxTypeIdHashMap
& types
,
963 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
964 if (p
->GetName() == "Destructor")
966 else if (p
->GetName() == "OperatorMethod")
967 name
= "operator" + name
;
969 // resolve return type
971 unsigned long retid
= 0;
972 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
974 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
975 LogError("Empty return ID for method '%s', with ID '%s'",
976 name
, p
->GetAttribute("id"));
982 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
983 if (retidx
== types
.end()) {
984 LogError("Could not find return type ID '%s'", retid
);
988 ret
= wxType(retidx
->second
);
990 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
991 retidx
->second
, name
, p
->GetAttribute("id"));
996 // resolve argument types
997 wxArgumentTypeArray argtypes
;
998 wxXmlNode
*arg
= p
->GetChildren();
1001 if (arg
->GetName() == "Argument")
1003 unsigned long id
= 0;
1004 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
1005 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
1006 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
1010 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
1011 if (idx
== types
.end()) {
1012 LogError("Could not find argument type ID '%s'", id
);
1016 argtypes
.Add(wxArgumentType(idx
->second
,
1017 arg
->GetAttribute("default"),
1018 arg
->GetAttribute("name")));
1021 arg
= arg
->GetNext();
1024 m
.SetReturnType(ret
);
1026 m
.SetArgumentTypes(argtypes
);
1027 m
.SetConst(p
->GetAttribute("const") == "1");
1028 m
.SetStatic(p
->GetAttribute("static") == "1");
1030 // NOTE: gccxml is smart enough to mark as virtual those functions
1031 // which are declared virtual in base classes but don't have
1032 // the "virtual" keyword explicitely indicated in the derived
1033 // classes... so we don't need any further logic for virtuals
1035 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
1036 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
1037 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
1040 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1049 // ----------------------------------------------------------------------------
1050 // wxXmlDoxygenInterface global helpers
1051 // ----------------------------------------------------------------------------
1053 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1057 // consider the tree
1059 // <a><b>this</b> is a <b>string</b></a>
1068 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1069 // this function returns "this is a string"
1071 wxXmlNode
*ref
= n
->GetChildren();
1073 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1074 text
+= ref
->GetNodeContent();
1075 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1076 text
+= ref
->GetContent();
1078 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1080 ref
= ref
->GetNext();
1086 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1091 wxXmlNode
*p
= parent
->GetChildren();
1094 switch (p
->GetType())
1096 case wxXML_TEXT_NODE
:
1097 if (p
->GetContent() == name
)
1101 case wxXML_ELEMENT_NODE
:
1102 // recurse into this node...
1103 if (HasTextNodeContaining(p
, name
))
1118 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1123 const wxXmlNode
*p
= parent
->GetChildren();
1126 if (p
->GetName() == name
)
1129 // search recursively in the children of this node
1130 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1140 int GetAvailabilityFor(const wxXmlNode
*node
)
1142 // identify <onlyfor> custom XML tags
1143 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1145 return wxPORT_UNKNOWN
;
1147 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1148 int nAvail
= wxPORT_UNKNOWN
;
1149 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1151 if (!ports
[i
].StartsWith("wx")) {
1152 LogError("unexpected port ID '%s'", ports
[i
]);
1156 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1163 // ----------------------------------------------------------------------------
1164 // wxXmlDoxygenInterface
1165 // ----------------------------------------------------------------------------
1167 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1169 wxXmlDocument index
;
1170 wxXmlNode
*compound
;
1172 LogMessage("Parsing %s...", filename
);
1174 if (!index
.Load(filename
)) {
1175 LogError("can't load %s", filename
);
1179 // start processing the index:
1180 if (index
.GetRoot()->GetName() != "doxygenindex") {
1181 LogError("invalid root node for %s", filename
);
1186 NB: we may need in future to do a version-check here if the
1187 format of the XML generated by doxygen changes.
1188 For now (doxygen version 1.5.5), this check is not required
1189 since AFAIK the XML format never changed since it was introduced.
1192 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1194 // process files referenced by this index file
1195 compound
= index
.GetRoot()->GetChildren();
1198 if (compound
->GetName() == "compound" &&
1199 compound
->GetAttribute("kind") == "class")
1201 wxString refid
= compound
->GetAttribute("refid");
1203 wxFileName
fn(filename
);
1204 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1208 compound
= compound
->GetNext();
1212 if (!CheckParseResults())
1218 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1225 LogMessage("Parsing %s...", filename
);
1227 if (!doc
.Load(filename
)) {
1228 LogError("can't load %s", filename
);
1232 // start processing this compound definition XML
1233 if (doc
.GetRoot()->GetName() != "doxygen") {
1234 LogError("invalid root node for %s", filename
);
1238 // build a list of wx classes
1239 child
= doc
.GetRoot()->GetChildren();
1242 if (child
->GetName() == "compounddef" &&
1243 child
->GetAttribute("kind") == "class")
1247 wxString absoluteFile
, header
;
1249 wxXmlNode
*subchild
= child
->GetChildren();
1252 if (subchild
->GetName() == "sectiondef" &&
1253 subchild
->GetAttribute("kind") == "public-func")
1256 wxXmlNode
*membernode
= subchild
->GetChildren();
1259 if (membernode
->GetName() == "memberdef" &&
1260 membernode
->GetAttribute("kind") == "function")
1264 if (!ParseMethod(membernode
, m
, header
)) {
1265 LogError("The method '%s' could not be added to class '%s'",
1266 m
.GetName(), klass
.GetName());
1270 if (absoluteFile
.IsEmpty())
1271 absoluteFile
= header
;
1272 else if (header
!= absoluteFile
)
1274 LogError("The method '%s' is documented in a different "
1275 "file from others (which belong to '%s') ?",
1276 header
, absoluteFile
);
1283 membernode
= membernode
->GetNext();
1286 // all methods of this class were taken from the header "absoluteFile":
1287 klass
.SetHeader(absoluteFile
);
1289 else if (subchild
->GetName() == "compoundname")
1291 klass
.SetName(subchild
->GetNodeContent());
1293 /*else if (subchild->GetName() == "includes")
1295 // NOTE: we'll get the header from the <location> tags
1296 // scattered inside <memberdef> tags instead of
1297 // this <includes> tag since it does not contain
1298 // the absolute path of the header
1300 klass.SetHeader(subchild->GetNodeContent());
1302 else if (subchild
->GetName() == "detaileddescription")
1304 // identify <onlyfor> custom XML tags
1305 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1308 subchild
= subchild
->GetNext();
1313 m_classes
.Add(klass
);
1315 LogWarning("discarding class '%s' with %d methods...",
1316 klass
.GetName(), klass
.GetMethodCount());
1319 child
= child
->GetNext();
1321 // give feedback to the user about the progress...
1322 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1328 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1330 wxArgumentTypeArray args
;
1333 wxXmlNode
*child
= p
->GetChildren();
1336 if (child
->GetName() == "name")
1337 m
.SetName(child
->GetNodeContent());
1338 else if (child
->GetName() == "type")
1339 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1340 else if (child
->GetName() == "param")
1342 wxString typestr
, namestr
, defstr
, arrstr
;
1343 wxXmlNode
*n
= child
->GetChildren();
1346 if (n
->GetName() == "type")
1347 // if the <type> node has children, they should be all TEXT and <ref> nodes
1348 // and we need to take the text they contain, in the order they appear
1349 typestr
= GetTextFromChildren(n
);
1350 else if (n
->GetName() == "declname")
1351 namestr
= GetTextFromChildren(n
);
1352 else if (n
->GetName() == "defval")
1353 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1354 else if (n
->GetName() == "array")
1355 arrstr
= GetTextFromChildren(n
);
1360 if (typestr
.IsEmpty()) {
1361 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1365 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1367 // can we use preprocessor output to transform the default value
1368 // into the same form which gets processed by wxXmlGccInterface?
1369 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1370 if (it
!= m_preproc
.end())
1371 newarg
.SetDefaultValue(defstr
, it
->second
);
1375 else if (child
->GetName() == "location")
1378 if (child
->GetAttribute("line").ToLong(&line
))
1379 m
.SetLocation((int)line
);
1380 header
= child
->GetAttribute("file");
1382 else if (child
->GetName() == "detaileddescription")
1384 // when a method has a @deprecated tag inside its description,
1385 // Doxygen outputs somewhere nested inside <detaileddescription>
1386 // a <xreftitle>Deprecated</xreftitle> tag.
1387 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1389 // identify <onlyfor> custom XML tags
1390 m
.SetAvailability(GetAvailabilityFor(child
));
1393 child
= child
->GetNext();
1396 m
.SetArgumentTypes(args
);
1397 m
.SetConst(p
->GetAttribute("const")=="yes");
1398 m
.SetStatic(p
->GetAttribute("static")=="yes");
1400 // NOTE: Doxygen is smart enough to mark as virtual those functions
1401 // which are declared virtual in base classes but don't have
1402 // the "virtual" keyword explicitely indicated in the derived
1403 // classes... so we don't need any further logic for virtuals
1405 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1406 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1409 LogError("The prototype '%s' is not valid!", m
.GetAsString());