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
;
45 // ----------------------------------------------------------------------------
47 // ----------------------------------------------------------------------------
51 void wxType::SetTypeFromString(const wxString
& t
)
54 TODO: optimize the following code writing a single function
55 which works at char-level and does everything in a single pass
60 // [] is the same as * for gccxml
61 m_strType
.Replace("[]", "*");
62 m_strType
.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
64 // make sure the * and & operator always use the same spacing rules
65 // (to make sure GetAsString() output is always consistent)
66 m_strType
.Replace("*", "* ");
67 m_strType
.Replace("&", "& ");
68 m_strType
.Replace(" *", "*");
69 m_strType
.Replace(" &", "&");
71 while (m_strType
.Contains(" "))
72 m_strType
.Replace(" ", " "); // do it once again
74 m_strType
.Replace(" ,", ",");
76 m_strType
= m_strType
.Strip(wxString::both
);
78 // now set the clean version
79 m_strTypeClean
= m_strType
;
80 m_strTypeClean
.Replace("const", "");
81 m_strTypeClean
.Replace("static", "");
82 m_strTypeClean
.Replace("*", "");
83 m_strTypeClean
.Replace("&", "");
84 m_strTypeClean
.Replace("[]", "");
85 m_strTypeClean
= m_strTypeClean
.Strip(wxString::both
);
87 // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
88 // need to be considered as the same type
89 if (m_strTypeClean
.EndsWith("Base"))
90 m_strTypeClean
= m_strTypeClean
.Left(m_strTypeClean
.Len()-4);
93 bool wxType::IsOk() const
95 // NB: m_strType can contain the :: operator; think to e.g. the
96 // "reverse_iterator_impl<wxString::const_iterator>" type
97 // It can also contain commas, * and & operators etc
99 return !m_strTypeClean
.IsEmpty();
102 bool wxType::operator==(const wxType
& m
) const
104 // brain-dead comparison:
106 if (m_strTypeClean
== m
.m_strTypeClean
&&
107 IsConst() == m
.IsConst() &&
108 IsStatic() == m
.IsStatic() &&
109 IsPointer() == m
.IsPointer() &&
110 IsReference() == m
.IsReference())
117 // ----------------------------------------------------------------------------
119 // ----------------------------------------------------------------------------
121 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
123 m_strDefaultValue
=defval
.Strip(wxString::both
);
124 m_strDefaultValueForCmp
=defvalForCmp
.Strip(wxString::both
);
126 // in order to make valid&simple comparison on argument defaults,
127 // we reduce some of the multiple forms in which the same things may appear
129 if (m_strDefaultValue
== "0u")
130 m_strDefaultValue
= "0";
133 m_strDefaultValue.Replace("0", "NULL");
135 m_strDefaultValue.Replace("NULL", "0");
138 if (m_strDefaultValue
.Contains("wxGetTranslation"))
139 m_strDefaultValue
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
142 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
144 if ((const wxType
&)(*this) != (const wxType
&)m
)
147 const wxString
& def1
= m_strDefaultValueForCmp
.IsEmpty() ? m_strDefaultValue
: m_strDefaultValueForCmp
;
148 const wxString
& def2
= m
.m_strDefaultValueForCmp
.IsEmpty() ? m
.m_strDefaultValue
: m
.m_strDefaultValueForCmp
;
152 // maybe the default values are numbers.
153 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
154 // To handle these cases, we try to convert the default value strings to numbers:
155 long def1val
, def2val
;
156 if (def1
.ToLong(&def1val
, 0 /* auto-detect */) &&
157 def2
.ToLong(&def2val
, 0 /* auto-detect */))
159 if (def1val
== def2val
)
160 return true; // the default values match
166 // we deliberately avoid checks on the argument name
172 // ----------------------------------------------------------------------------
174 // ----------------------------------------------------------------------------
176 bool wxMethod::IsOk() const
178 // NOTE: m_retType can be a wxEmptyType, and means that this method
179 // is a ctor or a dtor.
180 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
181 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
185 if (m_strName
.IsEmpty())
188 // a function can't be both const and static or virtual and static!
189 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
190 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
194 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
196 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
197 if (!m_args
[i
].IsOk()) {
198 LogError("'%s' method has invalid %d-th argument type: %s",
199 m_strName
, i
+1, m_args
[i
].GetAsString());
203 // NB: the default value of the arguments can contain pretty much everything
204 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
205 // so we don't do any test on their contents
206 if (m_args
.GetCount()>0)
208 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
209 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
211 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
212 LogError("'%s' method has %d-th argument which has no default value "
213 "(while the previous one had one!)",
218 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
225 bool wxMethod::operator==(const wxMethod
& m
) const
227 if (GetReturnType() != m
.GetReturnType() ||
228 GetName() != m
.GetName() ||
229 IsConst() != m
.IsConst() ||
230 IsStatic() != m
.IsStatic() ||
231 IsVirtual() != m
.IsVirtual() ||
232 IsPureVirtual() != m
.IsPureVirtual() ||
233 IsDeprecated() != m
.IsDeprecated())
236 if (m_args
.GetCount()!=m
.m_args
.GetCount())
239 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
240 if (m_args
[i
] != m
.m_args
[i
])
246 wxString
wxMethod::GetAsString(bool bWithArgumentNames
) const
250 if (m_retType
!=wxEmptyType
)
251 ret
+= m_retType
.GetAsString() + " ";
252 //else; this is a ctor or dtor
254 ret
+= m_strName
+ "(";
256 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
258 ret
+= m_args
[i
].GetAsString();
260 const wxString
& name
= m_args
[i
].GetArgumentName();
261 if (bWithArgumentNames
&& !name
.IsEmpty())
264 const wxString
& def
= m_args
[i
].GetDefaultValue();
271 if (m_args
.GetCount()>0)
272 ret
= ret
.Left(ret
.Len()-2);
279 ret
= "static " + ret
;
280 if (m_bVirtual
|| m_bPureVirtual
)
281 ret
= "virtual " + ret
;
285 // in doxygen headers we don't need wxDEPRECATED:
287 // ret = "wxDEPRECATED( " + ret + " )";
292 void wxMethod::Dump(wxTextOutputStream
& stream
) const
294 stream
<< "[" + m_retType
.GetAsString() + "]";
295 stream
<< "[" + m_strName
+ "]";
297 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
298 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
299 "=" + m_args
[i
].GetDefaultValue() + "]";
306 stream
<< " VIRTUAL";
308 stream
<< " PURE-VIRTUAL";
310 stream
<< " DEPRECATED";
315 // ----------------------------------------------------------------------------
317 // ----------------------------------------------------------------------------
319 wxString
wxClass::GetNameWithoutTemplate() const
321 // NB: I'm not sure this is the right terminology for this function!
323 if (m_strName
.Contains("<"))
324 return m_strName
.Left(m_strName
.Find("<"));
328 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
330 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
331 // named wxWritableCharTypeBuffer, without the <...> part!
333 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
339 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
341 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
347 void wxClass::Dump(wxTextOutputStream
& out
) const
349 out
<< m_strName
+ "\n";
351 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
353 // dump all our methods
355 m_methods
[i
].Dump(out
);
362 bool wxClass::CheckConsistency() const
364 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
365 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
366 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
368 LogError("class %s has two methods with the same prototype: '%s'",
369 m_strName
, m_methods
[i
].GetAsString());
371 ((wxClass
*)this)->m_methods
.RemoveAt(j
);
378 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
380 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
381 if (m_methods
[i
] == m
)
382 return &m_methods
[i
];
386 wxMethodPtrArray
wxClass::FindMethodsNamed(const wxString
& name
) const
388 wxMethodPtrArray ret
;
390 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
391 if (m_methods
[i
].GetName() == name
)
392 ret
.Add(&m_methods
[i
]);
398 // ----------------------------------------------------------------------------
400 // ----------------------------------------------------------------------------
402 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
404 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
406 // sort alphabetically
407 return item1
->GetName().Cmp(item2
->GetName());
410 void wxXmlInterface::Dump(const wxString
& filename
)
412 wxFFileOutputStream
apioutput( filename
);
413 wxTextOutputStream
apiout( apioutput
);
415 // dump the classes in alphabetical order
416 wxSortedClassArray
sorted(CompareWxClassObjects
);
417 sorted
.Alloc(m_classes
.GetCount());
418 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
419 sorted
.Add(&m_classes
[i
]);
421 // now they have been sorted
422 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
423 sorted
[i
]->Dump(apiout
);
426 bool wxXmlInterface::CheckParseResults() const
428 // this check can be quite slow, so do it only for debug releases:
430 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
431 if (!m_classes
[i
].CheckConsistency())
438 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
442 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
443 if (m_classes
[i
].GetHeader() == headerfile
)
444 ret
.Add(&m_classes
[i
]);
450 // ----------------------------------------------------------------------------
451 // wxXmlGccInterface helper declarations
452 // ----------------------------------------------------------------------------
454 // or-able flags for a toResolveTypeItem->attrib:
455 #define ATTRIB_CONST 1
456 #define ATTRIB_REFERENCE 2
457 #define ATTRIB_POINTER 4
458 #define ATTRIB_ARRAY 8
460 #define GCCXML_BASE 35
462 class toResolveTypeItem
465 toResolveTypeItem() { attribs
=0; }
466 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
467 : ref(refID
), attribs(attribint
) {}
469 unsigned long ref
, // the referenced type's ID
470 attribs
; // the attributes of this reference
475 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
476 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
477 wxIntegerHash
, wxIntegerEqual
,
478 wxToResolveTypeHashMap
);
480 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
481 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
482 wxIntegerHash
, wxIntegerEqual
,
483 wxClassMemberIdHashMap
);
486 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
490 // utility to parse gccXML ID values;
491 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
492 // but is a little bit faster
493 bool getID(unsigned long *id
, const wxStringCharType
* str
)
495 wxStringCharType
*end
;
496 #if wxUSE_UNICODE_WCHAR
497 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
499 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
502 // return true only if scan was stopped by the terminating NUL and
503 // if the string was not empty to start with and no under/overflow
505 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
512 // utility specialized to parse efficiently the gccXML list of IDs which occur
513 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
514 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
516 #if wxUSE_UNICODE_WCHAR
517 size_t len
= wcslen(str
);
519 size_t len
= strlen(str
);
522 if (len
== 0 || str
[0] != '_')
525 const wxStringCharType
*curpos
= str
,
527 wxStringCharType
*nexttoken
;
531 // curpos always points to the underscore of the next token to parse:
532 #if wxUSE_UNICODE_WCHAR
533 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
535 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
537 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
540 // advance current position
541 curpos
= nexttoken
+ 1;
543 // add this ID to the hashmap
544 wxClassMemberIdHashMap::value_type
v(id
, p
);
552 // ----------------------------------------------------------------------------
554 // ----------------------------------------------------------------------------
556 bool wxXmlGccInterface::Parse(const wxString
& filename
)
562 LogMessage("Parsing %s...", filename
);
564 if (!doc
.Load(filename
)) {
565 LogError("can't load %s", filename
);
569 // start processing the XML file
570 if (doc
.GetRoot()->GetName() != "GCC_XML") {
571 LogError("invalid root node for %s", filename
);
575 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
578 #define MIN_REVISION 120
580 if (!version
.StartsWith("1."))
584 unsigned long rev
= 0;
585 if (!version
.Mid(2).ToULong(&rev
))
588 if (rev
< MIN_REVISION
)
594 LogError("The version of GCC-XML used for the creation of %s is too old; "
595 "the cvs_revision attribute of the root node reports '%s', "
596 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
600 wxToResolveTypeHashMap toResolveTypes
;
601 wxClassMemberIdHashMap members
;
602 wxTypeIdHashMap types
;
603 wxTypeIdHashMap files
;
604 wxTypeIdHashMap typedefs
;
606 // prealloc quite a lot of memory!
607 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
609 // build a list of wx classes and in general of all existent types
610 child
= doc
.GetRoot()->GetChildren();
613 const wxString
& n
= child
->GetName();
615 unsigned long id
= 0;
616 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
618 // NOTE: <File> nodes can have an id == "f0"...
620 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
626 wxString cname
= child
->GetAttribute("name");
627 if (cname
.IsEmpty()) {
628 LogError("Invalid empty name for '%s' node", n
);
632 // only register wx classes (do remember also the IDs of their members)
633 if (cname
.StartsWith("wx"))
635 // NB: "file" attribute contains an ID value that we'll resolve later
636 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
638 const wxString
& ids
= child
->GetAttribute("members");
641 if (child
->GetAttribute("incomplete") != "1") {
642 LogError("Invalid member IDs for '%s' class node: %s",
643 cname
, child
->GetAttribute("id"));
646 //else: don't warn the user; it looks like "incomplete" classes
647 // never have any member...
651 // decode the non-empty list of IDs:
652 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
653 LogError("Invalid member IDs for '%s' class node: %s",
654 cname
, child
->GetAttribute("id"));
660 // register this class also as possible return/argument type:
663 else if (n
== "Typedef")
665 unsigned long typeId
= 0;
666 if (!getID(&typeId
, child
->GetAttribute("type"))) {
667 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
671 // this typedef node tell us that every type referenced with the
672 // "typeId" ID should be called with another name:
673 wxString name
= child
->GetAttribute("name");
675 // save this typedef in a separate hashmap...
676 typedefs
[typeId
] = name
;
680 else if (n
== "PointerType" || n
== "ReferenceType" ||
681 n
== "CvQualifiedType" || n
== "ArrayType")
683 unsigned long type
= 0;
684 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
685 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
689 unsigned long attr
= 0;
690 if (n
== "PointerType")
691 attr
= ATTRIB_POINTER
;
692 else if (n
== "ReferenceType")
693 attr
= ATTRIB_REFERENCE
;
694 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
696 else if (n
== "ArrayType")
699 // these nodes make reference to other types... we'll resolve them later
700 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
702 else if (n
== "FunctionType" || n
== "MethodType")
705 TODO: parsing FunctionType and MethodType nodes is not as easy
706 as for other "simple" types.
710 wxXmlNode
*arg
= child
->GetChildren();
713 if (arg
->GetName() == "Argument")
714 argstr
+= arg
->GetAttribute("type") + ", ";
715 arg
= arg
->GetNext();
718 if (argstr
.Len() > 0)
719 argstr
= argstr
.Left(argstr
.Len()-2); // remove final comma
721 // these nodes make reference to other types... we'll resolve them later
722 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
723 //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
725 types
[id
] = "TOFIX"; // typically this type will be "fixed" thanks
726 // to a typedef later...
728 else if (n
== "File")
730 if (!child
->GetAttribute("id").StartsWith("f")) {
731 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
735 // just ignore this node... all file IDs/names were already parsed
736 files
[id
] = child
->GetAttribute("name");
740 // we register everything else as a possible return/argument type:
741 const wxString
& name
= child
->GetAttribute("name");
746 //typeNames.Add(name);
751 // this may happen with unnamed structs/union, special ctors,
752 // or other exotic things which we are not interested to, since
753 // they're never used as return/argument types by wxWidgets methods
756 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
757 n
, child
->GetAttribute("id"));
763 child
= child
->GetNext();
765 // give feedback to the user about the progress...
766 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
769 // some nodes with IDs referenced by methods as return/argument types, do reference
770 // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
771 // thus we need to resolve their name iteratively:
772 while (toResolveTypes
.size()>0)
775 LogMessage("%d types were collected; %d types need yet to be resolved...",
776 types
.size(), toResolveTypes
.size());
778 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
779 i
!= toResolveTypes
.end();)
781 unsigned long id
= i
->first
;
782 unsigned long referenced
= i
->second
.ref
;
784 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
785 if (primary
!= types
.end())
787 // this to-resolve-type references a "primary" type
789 wxString newtype
= primary
->second
;
790 int attribs
= i
->second
.attribs
;
792 // attribs may contain a combination of ATTRIB_* flags:
793 if (attribs
& ATTRIB_CONST
)
794 newtype
= "const " + newtype
;
795 if (attribs
& ATTRIB_REFERENCE
)
796 newtype
= newtype
+ "&";
797 if (attribs
& ATTRIB_POINTER
)
798 newtype
= newtype
+ "*";
799 if (attribs
& ATTRIB_ARRAY
)
800 newtype
= newtype
+ "[]";
802 // add the resolved type to the list of "primary" types
803 if (newtype
.Contains("TOFIX") && typedefs
[id
] != "")
804 types
[id
] = typedefs
[id
]; // better use a typedef for this type!
808 // this one has been resolved; erase it through its iterator!
809 toResolveTypes
.erase(i
);
811 // now iterator i is invalid; assign it again to the beginning
812 i
= toResolveTypes
.begin();
816 // then search in the referenced types themselves:
817 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
818 if (idx2
!= toResolveTypes
.end())
820 // merge this to-resolve-type with the idx2->second type
821 i
->second
.ref
= idx2
->second
.ref
;
822 i
->second
.attribs
|= idx2
->second
.attribs
;
824 // this type will eventually be solved in the next while() iteration
829 LogError("Cannot solve '%d' reference type!", referenced
);
836 // resolve header names
837 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
839 unsigned long fileID
= 0;
840 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
841 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
846 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
847 if (idx
== files
.end())
850 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
853 m_classes
[i
].SetHeader(idx
->second
);
856 // build the list of the wx methods
857 child
= doc
.GetRoot()->GetChildren();
860 wxString n
= child
->GetName();
862 // only register public methods
863 if (child
->GetAttribute("access") == "public" &&
864 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
866 unsigned long id
= 0;
867 if (!getID(&id
, child
->GetAttribute("id"))) {
868 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
872 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
873 if (it
!= members
.end())
875 wxClass
*p
= it
->second
;
877 // this <Method> node is a method of the i-th class!
879 if (!ParseMethod(child
, types
, newfunc
)) {
880 LogError("The method '%s' could not be added to class '%s'",
881 child
->GetAttribute("demangled"), p
->GetName());
885 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
886 LogError("The method '%s' does not seem to be a ctor for '%s'",
887 newfunc
.GetName(), p
->GetName());
890 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
891 LogError("The method '%s' does not seem to be a dtor for '%s'",
892 newfunc
.GetName(), p
->GetName());
896 p
->AddMethod(newfunc
);
900 child
= child
->GetNext();
902 // give feedback to the user about the progress...
903 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
906 if (!CheckParseResults())
912 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
913 const wxTypeIdHashMap
& types
,
917 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
918 if (p
->GetName() == "Destructor")
920 else if (p
->GetName() == "OperatorMethod")
921 name
= "operator" + name
;
923 // resolve return type
925 unsigned long retid
= 0;
926 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
928 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
929 LogError("Empty return ID for method '%s', with ID '%s'",
930 name
, p
->GetAttribute("id"));
936 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
937 if (retidx
== types
.end()) {
938 LogError("Could not find return type ID '%s'", retid
);
942 ret
= wxType(retidx
->second
);
944 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
945 retidx
->second
, name
, p
->GetAttribute("id"));
950 // resolve argument types
951 wxArgumentTypeArray argtypes
;
952 wxXmlNode
*arg
= p
->GetChildren();
955 if (arg
->GetName() == "Argument")
957 unsigned long id
= 0;
958 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
959 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
960 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
964 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
965 if (idx
== types
.end()) {
966 LogError("Could not find argument type ID '%s'", id
);
970 argtypes
.Add(wxArgumentType(idx
->second
, arg
->GetAttribute("default")));
973 arg
= arg
->GetNext();
976 m
.SetReturnType(ret
);
978 m
.SetArgumentTypes(argtypes
);
979 m
.SetConst(p
->GetAttribute("const") == "1");
980 m
.SetStatic(p
->GetAttribute("static") == "1");
982 // NOTE: gccxml is smart enough to mark as virtual those functions
983 // which are declared virtual in base classes but don't have
984 // the "virtual" keyword explicitely indicated in the derived
985 // classes... so we don't need any further logic for virtuals
987 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
988 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
989 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
992 LogError("The prototype '%s' is not valid!", m
.GetAsString());
1001 // ----------------------------------------------------------------------------
1002 // wxXmlDoxygenInterface global helpers
1003 // ----------------------------------------------------------------------------
1005 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1009 // consider the tree
1011 // <a><b>this</b> is a <b>string</b></a>
1020 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1021 // this function returns "this is a string"
1023 wxXmlNode
*ref
= n
->GetChildren();
1025 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1026 text
+= ref
->GetNodeContent();
1027 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1028 text
+= ref
->GetContent();
1030 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1032 ref
= ref
->GetNext();
1038 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1043 wxXmlNode
*p
= parent
->GetChildren();
1046 switch (p
->GetType())
1048 case wxXML_TEXT_NODE
:
1049 if (p
->GetContent() == name
)
1053 case wxXML_ELEMENT_NODE
:
1054 // recurse into this node...
1055 if (HasTextNodeContaining(p
, name
))
1070 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1075 const wxXmlNode
*p
= parent
->GetChildren();
1078 if (p
->GetName() == name
)
1081 // search recursively in the children of this node
1082 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1092 int GetAvailabilityFor(const wxXmlNode
*node
)
1094 // identify <onlyfor> custom XML tags
1095 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1097 return wxPORT_UNKNOWN
;
1099 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1100 int nAvail
= wxPORT_UNKNOWN
;
1101 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1103 if (!ports
[i
].StartsWith("wx")) {
1104 LogError("unexpected port ID '%s'", ports
[i
]);
1108 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1115 // ----------------------------------------------------------------------------
1116 // wxXmlDoxygenInterface
1117 // ----------------------------------------------------------------------------
1119 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1121 wxXmlDocument index
;
1122 wxXmlNode
*compound
;
1124 LogMessage("Parsing %s...", filename
);
1126 if (!index
.Load(filename
)) {
1127 LogError("can't load %s", filename
);
1131 // start processing the index:
1132 if (index
.GetRoot()->GetName() != "doxygenindex") {
1133 LogError("invalid root node for %s", filename
);
1138 NB: we may need in future to do a version-check here if the
1139 format of the XML generated by doxygen changes.
1140 For now (doxygen version 1.5.5), this check is not required
1141 since AFAIK the XML format never changed since it was introduced.
1144 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1146 // process files referenced by this index file
1147 compound
= index
.GetRoot()->GetChildren();
1150 if (compound
->GetName() == "compound" &&
1151 compound
->GetAttribute("kind") == "class")
1153 wxString refid
= compound
->GetAttribute("refid");
1155 wxFileName
fn(filename
);
1156 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1160 compound
= compound
->GetNext();
1164 if (!CheckParseResults())
1170 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1177 LogMessage("Parsing %s...", filename
);
1179 if (!doc
.Load(filename
)) {
1180 LogError("can't load %s", filename
);
1184 // start processing this compound definition XML
1185 if (doc
.GetRoot()->GetName() != "doxygen") {
1186 LogError("invalid root node for %s", filename
);
1190 // build a list of wx classes
1191 child
= doc
.GetRoot()->GetChildren();
1194 if (child
->GetName() == "compounddef" &&
1195 child
->GetAttribute("kind") == "class")
1199 wxString absoluteFile
, header
;
1201 wxXmlNode
*subchild
= child
->GetChildren();
1204 if (subchild
->GetName() == "sectiondef" &&
1205 subchild
->GetAttribute("kind") == "public-func")
1208 wxXmlNode
*membernode
= subchild
->GetChildren();
1211 if (membernode
->GetName() == "memberdef" &&
1212 membernode
->GetAttribute("kind") == "function")
1216 if (!ParseMethod(membernode
, m
, header
)) {
1217 LogError("The method '%s' could not be added to class '%s'",
1218 m
.GetName(), klass
.GetName());
1222 if (absoluteFile
.IsEmpty())
1223 absoluteFile
= header
;
1224 else if (header
!= absoluteFile
)
1226 LogError("The method '%s' is documented in a different "
1227 "file from others (which belong to '%s') ?",
1228 header
, absoluteFile
);
1235 membernode
= membernode
->GetNext();
1238 // all methods of this class were taken from the header "absoluteFile":
1239 klass
.SetHeader(absoluteFile
);
1241 else if (subchild
->GetName() == "compoundname")
1243 klass
.SetName(subchild
->GetNodeContent());
1245 /*else if (subchild->GetName() == "includes")
1247 // NOTE: we'll get the header from the <location> tags
1248 // scattered inside <memberdef> tags instead of
1249 // this <includes> tag since it does not contain
1250 // the absolute path of the header
1252 klass.SetHeader(subchild->GetNodeContent());
1254 else if (subchild
->GetName() == "detaileddescription")
1256 // identify <onlyfor> custom XML tags
1257 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1260 subchild
= subchild
->GetNext();
1265 m_classes
.Add(klass
);
1267 LogWarning("discarding class '%s' with %d methods...",
1268 klass
.GetName(), klass
.GetMethodCount());
1271 child
= child
->GetNext();
1273 // give feedback to the user about the progress...
1274 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1280 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1282 wxArgumentTypeArray args
;
1285 wxXmlNode
*child
= p
->GetChildren();
1288 if (child
->GetName() == "name")
1289 m
.SetName(child
->GetNodeContent());
1290 else if (child
->GetName() == "type")
1291 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1292 else if (child
->GetName() == "param")
1294 wxString typestr
, namestr
, defstr
, arrstr
;
1295 wxXmlNode
*n
= child
->GetChildren();
1298 if (n
->GetName() == "type")
1299 // if the <type> node has children, they should be all TEXT and <ref> nodes
1300 // and we need to take the text they contain, in the order they appear
1301 typestr
= GetTextFromChildren(n
);
1302 else if (n
->GetName() == "declname")
1303 namestr
= GetTextFromChildren(n
);
1304 else if (n
->GetName() == "defval")
1305 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1306 else if (n
->GetName() == "array")
1307 arrstr
= GetTextFromChildren(n
);
1312 if (typestr
.IsEmpty()) {
1313 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1317 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1319 // can we use preprocessor output to transform the default value
1320 // into the same form which gets processed by wxXmlGccInterface?
1321 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1322 if (it
!= m_preproc
.end())
1323 newarg
.SetDefaultValue(defstr
, it
->second
);
1327 else if (child
->GetName() == "location")
1330 if (child
->GetAttribute("line").ToLong(&line
))
1331 m
.SetLocation((int)line
);
1332 header
= child
->GetAttribute("file");
1334 else if (child
->GetName() == "detaileddescription")
1336 // when a method has a @deprecated tag inside its description,
1337 // Doxygen outputs somewhere nested inside <detaileddescription>
1338 // a <xreftitle>Deprecated</xreftitle> tag.
1339 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1341 // identify <onlyfor> custom XML tags
1342 m
.SetAvailability(GetAvailabilityFor(child
));
1345 child
= child
->GetNext();
1348 m
.SetArgumentTypes(args
);
1349 m
.SetConst(p
->GetAttribute("const")=="yes");
1350 m
.SetStatic(p
->GetAttribute("static")=="yes");
1352 // NOTE: Doxygen is smart enough to mark as virtual those functions
1353 // which are declared virtual in base classes but don't have
1354 // the "virtual" keyword explicitely indicated in the derived
1355 // classes... so we don't need any further logic for virtuals
1357 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1358 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1361 LogError("The prototype '%s' is not valid!", m
.GetAsString());