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(wxMethodArray
)
32 WX_DEFINE_OBJARRAY(wxClassArray
)
35 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
36 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
39 // defined in ifacecheck.cpp
40 extern bool g_verbose
;
44 // ----------------------------------------------------------------------------
46 // ----------------------------------------------------------------------------
50 void wxType::SetFromString(const wxString
& t
)
53 TODO: optimize the following code writing a single function
54 which works at char-level and does everything in a single pass
59 // [] is the same as * for gccxml
60 m_strType
.Replace("[]", "*");
61 m_strType
.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
63 // make sure the * and & operator always use the same spacing rules
64 // (to make sure GetAsString() output is always consistent)
65 m_strType
.Replace("*", "* ");
66 m_strType
.Replace("&", "& ");
67 m_strType
.Replace(" *", "*");
68 m_strType
.Replace(" &", "&");
70 while (m_strType
.Contains(" "))
71 m_strType
.Replace(" ", " "); // do it once again
73 m_strType
.Replace(" ,", ",");
75 m_strType
= m_strType
.Strip(wxString::both
);
78 bool wxType::IsOk() const
80 // NB: m_strType can contain the :: operator; think to e.g. the
81 // "reverse_iterator_impl<wxString::const_iterator>" type
82 // It can also contain commas, * and & operators etc
84 return !GetClean().IsEmpty();
87 wxString
wxType::GetClean() const
89 wxString
ret(m_strType
);
90 ret
.Replace("const", "");
91 ret
.Replace("static", "");
94 ret
.Replace("[]", "");
95 return ret
.Strip(wxString::both
);
98 bool wxType::operator==(const wxType
& m
) const
100 // brain-dead comparison:
102 if (GetClean() == m
.GetClean() &&
103 IsConst() == m
.IsConst() &&
104 IsStatic() == m
.IsStatic() &&
105 IsPointer() == m
.IsPointer() &&
106 IsReference() == m
.IsReference())
112 // ----------------------------------------------------------------------------
114 // ----------------------------------------------------------------------------
116 bool wxMethod::IsOk() const
118 // NOTE: m_retType can be a wxEmptyType, and means that this method
119 // is a ctor or a dtor.
120 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
121 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
125 if (m_strName
.IsEmpty())
128 // a function can't be both const and static or virtual and static!
129 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
130 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
134 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
135 if (!m_args
[i
].IsOk()) {
136 LogError("'%s' method has invalid %d-th argument type: %s",
137 m_strName
, i
, m_args
[i
].GetAsString());
141 // NB: the default value of the arguments can contain pretty much everything
142 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
143 // so we don't do any test on them.
148 void wxMethod::SetArgumentTypes(const wxTypeArray
& arr
, const wxArrayString
& defaults
)
150 wxASSERT(arr
.GetCount()==defaults
.GetCount());
153 m_argDefaults
=defaults
;
155 // in order to make valid&simple comparison on argument defaults,
156 // we reduce some of the multiple forms in which the same things may appear
158 for (unsigned int i
=0; i
<m_argDefaults
.GetCount(); i
++)
160 m_argDefaults
[i
].Replace("NULL", "0");
161 m_argDefaults
[i
].Replace("0u", "0");
165 bool wxMethod::operator==(const wxMethod
& m
) const
167 if (GetReturnType() != m
.GetReturnType() ||
168 GetName() != m
.GetName() ||
169 IsConst() != m
.IsConst() ||
170 IsStatic() != m
.IsStatic() ||
171 IsVirtual() != m
.IsVirtual() ||
172 IsPureVirtual() != m
.IsPureVirtual() ||
173 IsDeprecated() != m
.IsDeprecated())
176 if (m_args
.GetCount()!=m
.m_args
.GetCount())
179 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
180 if (m_args
[i
] != m
.m_args
[i
] || m_argDefaults
[i
] != m
.m_argDefaults
[i
])
186 wxString
wxMethod::GetAsString() const
190 if (m_retType
!=wxEmptyType
)
191 ret
+= m_retType
.GetAsString() + " ";
192 //else; this is a ctor or dtor
194 ret
+= m_strName
+ "(";
196 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
198 ret
+= m_args
[i
].GetAsString();
199 if (!m_argDefaults
[i
].IsEmpty())
200 ret
+= " = " + m_argDefaults
[i
];
204 if (m_args
.GetCount()>0)
205 ret
= ret
.Left(ret
.Len()-2);
212 ret
= "static " + ret
;
213 if (m_bVirtual
|| m_bPureVirtual
)
214 ret
= "virtual " + ret
;
218 // in doxygen headers we don't need wxDEPRECATED:
220 // ret = "wxDEPRECATED( " + ret + " )";
225 void wxMethod::Dump(wxTextOutputStream
& stream
) const
227 stream
<< "[" + m_retType
.GetAsString() + "]";
228 stream
<< "[" + m_strName
+ "]";
230 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
231 stream
<< "[" + m_args
[i
].GetAsString() + "=" + m_argDefaults
[i
] + "]";
238 stream
<< " VIRTUAL";
240 stream
<< " PURE-VIRTUAL";
242 stream
<< " DEPRECATED";
247 // ----------------------------------------------------------------------------
249 // ----------------------------------------------------------------------------
251 wxString
wxClass::GetNameWithoutTemplate() const
253 // NB: I'm not sure this is the right terminology for this function!
255 if (m_strName
.Contains("<"))
256 return m_strName
.Left(m_strName
.Find("<"));
260 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
262 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
263 // named wxWritableCharTypeBuffer, without the <...> part!
265 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
271 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
273 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
279 void wxClass::Dump(wxTextOutputStream
& out
) const
281 out
<< m_strName
+ "\n";
283 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
285 // dump all our methods
287 m_methods
[i
].Dump(out
);
294 bool wxClass::CheckConsistency() const
296 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
297 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
298 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
300 LogError("class %s has two methods with the same prototype: '%s'",
301 m_strName
, m_methods
[i
].GetAsString());
308 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
310 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
311 if (m_methods
[i
] == m
)
312 return &m_methods
[i
];
316 wxMethodPtrArray
wxClass::FindMethodNamed(const wxString
& name
) const
318 wxMethodPtrArray ret
;
320 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
321 if (m_methods
[i
].GetName() == name
)
322 ret
.Add(&m_methods
[i
]);
328 // ----------------------------------------------------------------------------
330 // ----------------------------------------------------------------------------
332 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
334 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
336 // sort alphabetically
337 return item1
->GetName().Cmp(item2
->GetName());
340 void wxXmlInterface::Dump(const wxString
& filename
)
342 wxFFileOutputStream
apioutput( filename
);
343 wxTextOutputStream
apiout( apioutput
);
345 // dump the classes in alphabetical order
346 wxSortedClassArray
sorted(CompareWxClassObjects
);
347 sorted
.Alloc(m_classes
.GetCount());
348 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
349 sorted
.Add(&m_classes
[i
]);
351 // now they have been sorted
352 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
353 sorted
[i
]->Dump(apiout
);
356 bool wxXmlInterface::CheckParseResults() const
358 // this check can be quite slow, so do it only for debug releases:
360 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
361 if (!m_classes
[i
].CheckConsistency())
368 // ----------------------------------------------------------------------------
369 // wxXmlGccInterface helper declarations
370 // ----------------------------------------------------------------------------
372 #define ATTRIB_CONST 1
373 #define ATTRIB_REFERENCE 2
374 #define ATTRIB_POINTER 4
375 #define ATTRIB_ARRAY 8
377 #define GCCXML_BASE 35
379 class toResolveTypeItem
382 toResolveTypeItem() { attribs
=0; }
383 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
384 : ref(refID
), attribs(attribint
) {}
386 unsigned long ref
, attribs
;
391 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
392 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
393 wxIntegerHash
, wxIntegerEqual
,
394 wxToResolveTypeHashMap
);
396 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
397 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
398 wxIntegerHash
, wxIntegerEqual
,
399 wxClassMemberIdHashMap
);
402 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
406 // utility to parse gccXML ID values;
407 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
408 // but is a little bit faster
409 bool getID(unsigned long *id
, const wxStringCharType
* str
)
411 wxStringCharType
*end
;
412 #if wxUSE_UNICODE_UTF8
413 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
415 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
418 // return true only if scan was stopped by the terminating NUL and
419 // if the string was not empty to start with and no under/overflow
421 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
428 // utility specialized to parse efficiently the gccXML list of IDs which occur
429 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
430 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
432 #if wxUSE_UNICODE_UTF8
433 size_t len
= strlen(str
);
435 size_t len
= wcslen(str
);
438 if (len
== 0 || str
[0] != '_')
441 const wxStringCharType
*curpos
= str
,
443 wxStringCharType
*nexttoken
;
447 // curpos always points to the underscore of the next token to parse:
448 #if wxUSE_UNICODE_UTF8
449 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
451 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
453 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
456 // advance current position
457 curpos
= nexttoken
+ 1;
459 // add this ID to the hashmap
460 wxClassMemberIdHashMap::value_type
v(id
, p
);
468 // ----------------------------------------------------------------------------
470 // ----------------------------------------------------------------------------
472 bool wxXmlGccInterface::Parse(const wxString
& filename
)
478 LogMessage("Parsing %s...", filename
);
480 if (!doc
.Load(filename
)) {
481 LogError("can't load %s", filename
);
485 // start processing the XML file
486 if (doc
.GetRoot()->GetName() != "GCC_XML") {
487 LogError("invalid root node for %s", filename
);
491 wxToResolveTypeHashMap toResolveTypes
;
492 wxClassMemberIdHashMap members
;
493 wxTypeIdHashMap types
;
494 wxTypeIdHashMap files
;
496 // prealloc quite a lot of memory!
497 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
499 // build a list of wx classes and in general of all existent types
500 child
= doc
.GetRoot()->GetChildren();
503 const wxString
& n
= child
->GetName();
505 unsigned long id
= 0;
506 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
508 // NOTE: <File> nodes can have an id == "f0"...
510 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
516 wxString cname
= child
->GetAttribute("name");
517 if (cname
.IsEmpty()) {
518 LogError("Invalid empty name for '%s' node", n
);
522 // only register wx classes (do remember also the IDs of their members)
523 if (cname
.StartsWith("wx"))
525 // NB: "file" attribute contains an ID value that we'll resolve later
526 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
528 const wxString
& ids
= child
->GetAttribute("members");
531 if (child
->GetAttribute("incomplete") != "1") {
532 LogError("Invalid member IDs for '%s' class node (ID %s)",
533 cname
, child
->GetAttribute("id"));
536 //else: don't warn the user; it looks like "incomplete" classes
537 // never have any member...
541 // decode the non-empty list of IDs:
542 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
543 LogError("Invalid member IDs for '%s' class node (ID %s)",
544 cname
, child
->GetAttribute("id"));
550 // register this class also as possible return/argument type:
553 else if (n
== "PointerType" || n
== "ReferenceType" ||
554 n
== "CvQualifiedType" || n
== "ArrayType")
556 unsigned long type
= 0;
557 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
558 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
562 unsigned long attr
= 0;
563 if (n
== "PointerType")
564 attr
= ATTRIB_POINTER
;
565 else if (n
== "ReferenceType")
566 attr
= ATTRIB_REFERENCE
;
567 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
569 else if (n
== "ArrayType")
572 // these nodes make reference to other types... we'll resolve them later
573 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
575 else if (n
== "FunctionType" || n
== "MethodType")
578 TODO: parsing FunctionType and MethodType nodes is not as easy
579 as for other "simple" types.
583 wxXmlNode
*arg
= child
->GetChildren();
586 if (arg
->GetName() == "Argument")
587 argstr
+= arg
->GetAttribute("type") + ", ";
588 arg
= arg
->GetNext();
591 if (argstr
.Len() > 0)
592 argstr
= argstr
.Left(argstr
.Len()-2);
594 // these nodes make reference to other types... we'll resolve them later
595 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
596 types
[id
] = child
->GetAttribute("returns") + "(" + argstr
+ ")";
598 else if (n
== "File")
600 if (!child
->GetAttribute("id").StartsWith("f")) {
601 LogError("Unexpected file ID: %s", id
);
605 // just ignore this node... all file IDs/names were already parsed
606 files
[id
] = child
->GetAttribute("name");
610 // we register everything else as a possible return/argument type:
611 const wxString
& name
= child
->GetAttribute("name");
616 //typeNames.Add(name);
621 // this may happen with unnamed structs/union, special ctors,
622 // or other exotic things which we are not interested to, since
623 // they're never used as return/argument types by wxWidgets methods
626 LogWarning("Type '%s' with ID '%s' does not have name attribute", n
, id
);
632 child
= child
->GetNext();
634 // give feedback to the user about the progress...
635 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
638 // some nodes with IDs referenced by methods as return/argument types, do reference
639 // in turn o ther nodes (see PointerType, ReferenceType and CvQualifierType above);
640 // thus we need to resolve their name iteratively:
641 while (toResolveTypes
.size()>0)
644 LogMessage("%d types were collected; %d types need yet to be resolved...",
645 types
.size(), toResolveTypes
.size());
647 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
648 i
!= toResolveTypes
.end();)
650 unsigned long id
= i
->first
;
651 unsigned long referenced
= i
->second
.ref
;
653 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
654 if (primary
!= types
.end())
656 // this to-resolve-type references a "primary" type
658 wxString newtype
= primary
->second
;
659 int attribs
= i
->second
.attribs
;
661 // attribs may contain a combination of ATTRIB_* flags:
662 if (attribs
& ATTRIB_CONST
)
663 newtype
= "const " + newtype
;
664 if (attribs
& ATTRIB_REFERENCE
)
665 newtype
= newtype
+ "&";
666 if (attribs
& ATTRIB_POINTER
)
667 newtype
= newtype
+ "*";
668 if (attribs
& ATTRIB_ARRAY
)
669 newtype
= newtype
+ "[]";
671 // add the resolved type to the list of "primary" types
674 // this one has been resolved; erase it through its iterator!
675 toResolveTypes
.erase(i
);
677 // now iterator i is invalid; assign it again to the beginning
678 i
= toResolveTypes
.begin();
682 // then search in the referenced types themselves:
683 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
684 if (idx2
!= toResolveTypes
.end())
686 // merge this to-resolve-type with the idx2->second type
687 i
->second
.ref
= idx2
->second
.ref
;
688 i
->second
.attribs
|= idx2
->second
.attribs
;
690 // this type will eventually be solved in the next while() iteration
695 LogError("Cannot solve '%s' reference type!", referenced
);
702 // resolve header names
703 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
705 unsigned long fileID
= 0;
706 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
707 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
712 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
713 if (idx
== files
.end())
716 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
719 m_classes
[i
].SetHeader(idx
->second
);
722 // build the list of the wx methods
723 child
= doc
.GetRoot()->GetChildren();
726 wxString n
= child
->GetName();
728 // only register public methods
729 if (child
->GetAttribute("access") == "public" &&
730 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
732 unsigned long id
= 0;
733 if (!getID(&id
, child
->GetAttribute("id"))) {
734 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
738 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
739 if (it
!= members
.end())
741 wxClass
*p
= it
->second
;
743 // this <Method> node is a method of the i-th class!
745 if (!ParseMethod(child
, types
, newfunc
))
748 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
749 LogError("The method '%s' does not seem to be a ctor for '%s'",
750 newfunc
.GetName(), p
->GetName());
753 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
754 LogError("The method '%s' does not seem to be a dtor for '%s'",
755 newfunc
.GetName(), p
->GetName());
759 p
->AddMethod(newfunc
);
763 child
= child
->GetNext();
765 // give feedback to the user about the progress...
766 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
770 if (!CheckParseResults())
776 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
777 const wxTypeIdHashMap
& types
,
781 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
782 if (p
->GetName() == "Destructor")
784 else if (p
->GetName() == "OperatorMethod")
785 name
= "operator" + name
;
787 // resolve return type
789 unsigned long retid
= 0;
790 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
792 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
793 LogError("Empty return ID for method '%s', with ID '%s'",
794 name
, p
->GetAttribute("id"));
800 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
801 if (retidx
== types
.end()) {
802 LogError("Could not find return type ID '%s'", retid
);
806 ret
= wxType(retidx
->second
);
808 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
809 retidx
->second
, name
, p
->GetAttribute("id"));
814 // resolve argument types
815 wxTypeArray argtypes
;
816 wxArrayString argdefs
;
817 wxXmlNode
*arg
= p
->GetChildren();
820 if (arg
->GetName() == "Argument")
822 unsigned long id
= 0;
823 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
824 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
825 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
829 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
830 if (idx
== types
.end()) {
831 LogError("Could not find argument type ID '%s'", id
);
835 argtypes
.Add(wxType(idx
->second
));
837 wxString def
= arg
->GetAttribute("default");
838 if (def
.Contains("wxGetTranslation"))
839 argdefs
.Add(wxEmptyString
); // TODO: wxGetTranslation gives problems to gccxml
844 arg
= arg
->GetNext();
847 m
.SetReturnType(ret
);
849 m
.SetArgumentTypes(argtypes
, argdefs
);
850 m
.SetConst(p
->GetAttribute("const") == "1");
851 m
.SetStatic(p
->GetAttribute("static") == "1");
852 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
853 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
854 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
857 LogError("The prototype '%s' is not valid!", m
.GetAsString());
865 // ----------------------------------------------------------------------------
866 // wxXmlDoxygenInterface
867 // ----------------------------------------------------------------------------
869 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
874 LogMessage("Parsing %s...", filename
);
876 if (!index
.Load(filename
)) {
877 LogError("can't load %s", filename
);
881 // start processing the index:
882 if (index
.GetRoot()->GetName() != "doxygenindex") {
883 LogError("invalid root node for %s", filename
);
887 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
889 // process files referenced by this index file
890 compound
= index
.GetRoot()->GetChildren();
893 if (compound
->GetName() == "compound" &&
894 compound
->GetAttribute("kind") == "class")
896 wxString refid
= compound
->GetAttribute("refid");
898 wxFileName
fn(filename
);
899 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
903 compound
= compound
->GetNext();
907 if (!CheckParseResults())
913 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
920 LogMessage("Parsing %s...", filename
);
922 if (!doc
.Load(filename
)) {
923 LogError("can't load %s", filename
);
927 // start processing this compound definition XML
928 if (doc
.GetRoot()->GetName() != "doxygen") {
929 LogError("invalid root node for %s", filename
);
933 // build a list of wx classes
934 child
= doc
.GetRoot()->GetChildren();
937 if (child
->GetName() == "compounddef" &&
938 child
->GetAttribute("kind") == "class")
942 wxString absoluteFile
, header
;
944 wxXmlNode
*subchild
= child
->GetChildren();
947 if (subchild
->GetName() == "sectiondef" &&
948 subchild
->GetAttribute("kind") == "public-func")
951 wxXmlNode
*membernode
= subchild
->GetChildren();
954 if (membernode
->GetName() == "memberdef" &&
955 membernode
->GetAttribute("kind") == "function")
959 if (ParseMethod(membernode
, m
, header
))
961 if (absoluteFile
.IsEmpty())
962 absoluteFile
= header
;
963 else if (header
!= absoluteFile
)
965 LogError("The method '%s' is documented in a different "
966 "file from others (which belong to '%s') ?",
967 header
, absoluteFile
);
975 membernode
= membernode
->GetNext();
978 // all methods of this class were taken from the header "absoluteFile":
979 klass
.SetHeader(absoluteFile
);
981 else if (subchild
->GetName() == "compoundname")
983 klass
.SetName(subchild
->GetNodeContent());
985 /*else if (subchild->GetName() == "includes")
987 // NOTE: we'll get the header from the <location> tags
988 // scattered inside <memberdef> tags instead of
989 // this <includes> tag since it does not contain
990 // the absolute path of the header
992 klass.SetHeader(subchild->GetNodeContent());
995 subchild
= subchild
->GetNext();
1000 m_classes
.Add(klass
);
1002 LogWarning("discarding class '%s' with %d methods...",
1003 klass
.GetName(), klass
.GetMethodCount());
1006 child
= child
->GetNext();
1008 // give feedback to the user about the progress...
1009 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1015 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1019 // consider the tree
1021 // <a><b>this</b> is a <b>string</b></a>
1030 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1031 // this function returns "this is a string"
1033 wxXmlNode
*ref
= n
->GetChildren();
1035 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1036 text
+= ref
->GetNodeContent();
1037 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1038 text
+= ref
->GetContent();
1040 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1042 ref
= ref
->GetNext();
1048 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1050 wxXmlNode
*p
= parent
->GetChildren();
1053 switch (p
->GetType())
1055 case wxXML_TEXT_NODE
:
1056 if (p
->GetContent() == name
)
1060 case wxXML_ELEMENT_NODE
:
1061 // recurse into this node...
1062 if (HasTextNodeContaining(p
, name
))
1077 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1083 wxXmlNode
*child
= p
->GetChildren();
1086 if (child
->GetName() == "name")
1087 m
.SetName(child
->GetNodeContent());
1088 else if (child
->GetName() == "type")
1089 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1090 else if (child
->GetName() == "param")
1092 wxString typestr
, defstr
, arrstr
;
1093 wxXmlNode
*n
= child
->GetChildren();
1096 if (n
->GetName() == "type")
1097 // if the <type> node has children, they should be all TEXT and <ref> nodes
1098 // and we need to take the text they contain, in the order they appear
1099 typestr
= GetTextFromChildren(n
);
1100 else if (n
->GetName() == "defval")
1101 // same for the <defval> node
1102 defstr
= GetTextFromChildren(n
);
1103 else if (n
->GetName() == "array")
1104 arrstr
= GetTextFromChildren(n
);
1109 if (typestr
.IsEmpty()) {
1110 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1114 args
.Add(wxType(typestr
+ arrstr
));
1117 else if (child
->GetName() == "location")
1120 if (child
->GetAttribute("line").ToLong(&line
))
1121 m
.SetLocation((int)line
);
1122 header
= child
->GetAttribute("file");
1124 else if (child
->GetName() == "detaileddescription")
1126 // when a method has a @deprecated tag inside its description,
1127 // Doxygen outputs somewhere nested inside <detaileddescription>
1128 // a <xreftitle>Deprecated</xreftitle> tag.
1129 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1132 child
= child
->GetNext();
1135 m
.SetArgumentTypes(args
, defs
);
1136 m
.SetConst(p
->GetAttribute("const")=="yes");
1137 m
.SetStatic(p
->GetAttribute("static")=="yes");
1138 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1139 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1142 LogError("The prototype '%s' is not valid!", m
.GetAsString());