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/arrimpl.cpp"
25 #include "wx/dynarray.h"
26 #include "wx/filename.h"
30 #include "xmlparser.h"
32 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
33 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
34 #define ESTIMATED_NUM_TYPES 50000 // used only by wxGccXmlInterface to prealloc mem
35 #define ESTIMATED_NUM_FILES 800 // used only by wxGccXmlInterface to prealloc mem
37 WX_DEFINE_OBJARRAY(wxTypeArray
)
38 WX_DEFINE_OBJARRAY(wxMethodArray
)
39 WX_DEFINE_OBJARRAY(wxClassArray
)
42 // declared in ifacecheck.cpp
43 extern bool g_verbose
;
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
53 void wxType::SetFromString(const wxString
& t
)
55 m_strType
= t
.Strip(wxString::both
);
57 // [] is the same as * for gccxml
58 m_strType
.Replace("[]", "*");
61 bool wxType::IsOk() const
63 // NB: m_strType can contain the :: operator; think to e.g. the
64 // "reverse_iterator_impl<wxString::const_iterator>" type
65 // It can also contain commas, * and & operators etc
67 return !GetClean().IsEmpty();
70 wxString
wxType::GetClean() const
72 wxString
ret(m_strType
);
73 ret
.Replace("const", "");
74 ret
.Replace("static", "");
77 ret
.Replace("[]", "");
78 return ret
.Strip(wxString::both
);
81 bool wxType::operator==(const wxType
& m
) const
83 // brain-dead comparison:
85 if (GetClean() == m
.GetClean() &&
86 IsConst() == m
.IsConst() &&
87 IsStatic() == m
.IsStatic() &&
88 IsPointer() == m
.IsPointer() &&
89 IsReference() == m
.IsReference())
95 // ----------------------------------------------------------------------------
97 // ----------------------------------------------------------------------------
99 bool wxMethod::IsOk() const
101 // NOTE: m_retType can be a wxEmptyType, and means that this method
102 // is a ctor or a dtor.
103 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
104 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
108 if (m_strName
.IsEmpty())
111 // a function can't be both const and static or virtual and static!
112 if ((m_bConst
&& m_bStatic
) || (m_bVirtual
&& m_bStatic
)) {
113 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
117 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
118 if (!m_args
[i
].IsOk()) {
119 LogError("'%s' method has invalid %d-th argument type: %s",
120 m_strName
, i
, m_args
[i
].GetAsString());
124 // NB: the default value of the arguments can contain pretty much everything
125 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
126 // so we don't do any test on them.
131 void wxMethod::SetArgumentTypes(const wxTypeArray
& arr
, const wxArrayString
& defaults
)
133 wxASSERT(arr
.GetCount()==defaults
.GetCount());
136 m_argDefaults
=defaults
;
138 // in order to make valid&simple comparison on argument defaults,
139 // we reduce some of the multiple forms in which the same things may appear
141 for (unsigned int i
=0; i
<m_argDefaults
.GetCount(); i
++)
143 m_argDefaults
[i
].Replace("NULL", "0");
144 m_argDefaults
[i
].Replace("0u", "0");
148 bool wxMethod::operator==(const wxMethod
& m
) const
150 if (GetReturnType() != m
.GetReturnType() ||
151 GetName() != m
.GetName() ||
152 IsConst() != m
.IsConst() ||
153 IsStatic() != m
.IsStatic() ||
154 IsVirtual() != m
.IsVirtual())
157 if (m_args
.GetCount()!=m
.m_args
.GetCount())
160 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
161 if (m_args
[i
] != m
.m_args
[i
] || m_argDefaults
[i
] != m
.m_argDefaults
[i
])
167 wxString
wxMethod::GetAsString() const
171 if (m_retType
!=wxEmptyType
)
172 ret
+= m_retType
.GetAsString() + " ";
173 //else; this is a ctor or dtor
175 ret
+= m_strName
+ "(";
177 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
179 ret
+= m_args
[i
].GetAsString();
180 if (!m_argDefaults
[i
].IsEmpty())
181 ret
+= " = " + m_argDefaults
[i
];
185 if (m_args
.GetCount()>0)
193 ret
= "static " + ret
;
195 ret
= "virtual " + ret
;
200 void wxMethod::Dump(wxTextOutputStream
& stream
) const
202 stream
<< "[" + m_retType
.GetAsString() + "]";
203 stream
<< "[" + m_strName
+ "]";
205 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
206 stream
<< "[" + m_args
[i
].GetAsString() + "=" + m_argDefaults
[i
] + "]";
213 stream
<< " VIRTUAL";
218 // ----------------------------------------------------------------------------
220 // ----------------------------------------------------------------------------
222 wxString
wxClass::GetNameWithoutTemplate() const
224 // NB: I'm not sure this is the right terminology for this function!
226 if (m_strName
.Contains("<"))
227 return m_strName
.Left(m_strName
.Find("<"));
231 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
233 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
234 // named wxWritableCharTypeBuffer, without the <...> part!
236 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
242 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
244 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
250 void wxClass::Dump(wxTextOutputStream
& out
) const
252 out
<< m_strName
+ "\n";
254 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
256 // dump all our methods
258 m_methods
[i
].Dump(out
);
265 bool wxClass::CheckConsistency() const
267 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
268 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
269 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
271 LogError("class %s has two methods with the same prototype: '%s'",
272 m_strName
, m_methods
[i
].GetAsString());
279 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
281 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
282 if (m_methods
[i
] == m
)
283 return &m_methods
[i
];
287 wxMethodPtrArray
wxClass::FindMethodNamed(const wxString
& name
) const
289 wxMethodPtrArray ret
;
291 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
292 if (m_methods
[i
].GetName() == name
)
293 ret
.Add(&m_methods
[i
]);
299 // ----------------------------------------------------------------------------
301 // ----------------------------------------------------------------------------
303 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
305 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
307 // sort alphabetically
308 return item1
->GetName().Cmp(item2
->GetName());
311 void wxXmlInterface::Dump(const wxString
& filename
)
313 wxFFileOutputStream
apioutput( filename
);
314 wxTextOutputStream
apiout( apioutput
);
316 // dump the classes in alphabetical order
317 wxSortedClassArray
sorted(CompareWxClassObjects
);
318 sorted
.Alloc(m_classes
.GetCount());
319 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
320 sorted
.Add(&m_classes
[i
]);
322 // now they have been sorted
323 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
324 sorted
[i
]->Dump(apiout
);
327 bool wxXmlInterface::CheckParseResults() const
329 // this check can be quite slow, so do it only for debug releases:
331 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
332 if (!m_classes
[i
].CheckConsistency())
339 // ----------------------------------------------------------------------------
340 // wxXmlGccInterface helper declarations
341 // ----------------------------------------------------------------------------
343 #define ATTRIB_CONST 1
344 #define ATTRIB_REFERENCE 2
345 #define ATTRIB_POINTER 4
346 #define ATTRIB_ARRAY 8
348 #define GCCXML_BASE 35
350 class toResolveTypeItem
353 toResolveTypeItem() { attribs
=0; }
354 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
355 : ref(refID
), attribs(attribint
) {}
357 unsigned long ref
, attribs
;
362 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
363 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
364 wxIntegerHash
, wxIntegerEqual
,
365 wxToResolveTypeHashMap
);
367 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
368 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
369 wxIntegerHash
, wxIntegerEqual
,
370 wxClassMemberIdHashMap
);
373 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
377 // utility to parse gccXML ID values;
378 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
379 // but is a little bit faster
380 bool getID(unsigned long *id
, const wxStringCharType
* str
)
382 wxStringCharType
*end
;
383 #if wxUSE_UNICODE_UTF8
384 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
386 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
389 // return true only if scan was stopped by the terminating NUL and
390 // if the string was not empty to start with and no under/overflow
392 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
399 // utility specialized to parse efficiently the gccXML list of IDs which occur
400 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
401 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
403 #if wxUSE_UNICODE_UTF8
404 size_t len
= strlen(str
);
406 size_t len
= wcslen(str
);
409 if (len
== 0 || str
[0] != '_')
412 const wxStringCharType
*curpos
= str
,
414 wxStringCharType
*nexttoken
;
418 // curpos always points to the underscore of the next token to parse:
419 #if wxUSE_UNICODE_UTF8
420 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
422 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
424 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
427 // advance current position
428 curpos
= nexttoken
+ 1;
430 // add this ID to the hashmap
431 wxClassMemberIdHashMap::value_type
v(id
, p
);
439 // ----------------------------------------------------------------------------
441 // ----------------------------------------------------------------------------
443 bool wxXmlGccInterface::Parse(const wxString
& filename
)
449 LogMessage("Parsing %s...", filename
);
451 if (!doc
.Load(filename
)) {
452 LogError("can't load %s", filename
);
456 // start processing the XML file
457 if (doc
.GetRoot()->GetName() != "GCC_XML") {
458 LogError("invalid root node for %s", filename
);
462 wxToResolveTypeHashMap toResolveTypes
;
463 //wxArrayString arrMemberIds;
464 wxClassMemberIdHashMap members
;
465 wxTypeIdHashMap types
;
466 wxTypeIdHashMap files
;
468 // prealloc quite a lot of memory!
469 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
470 //arrMemberIds.Alloc(ESTIMATED_NUM_TYPES);
472 // build a list of wx classes and in general of all existent types
473 child
= doc
.GetRoot()->GetChildren();
476 const wxString
& n
= child
->GetName();
478 unsigned long id
= 0;
479 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
481 // NOTE: <File> nodes can have an id == "f0"...
483 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
489 wxString cname
= child
->GetAttribute("name");
490 if (cname
.IsEmpty()) {
491 LogError("Invalid empty name for '%s' node", n
);
495 // only register wx classes (do remember also the IDs of their members)
496 if (cname
.StartsWith("wx"))
498 // NB: "file" attribute contains an ID value that we'll resolve later
499 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
501 const wxString
& ids
= child
->GetAttribute("members");
504 if (child
->GetAttribute("incomplete") != "1") {
505 LogError("Invalid member IDs for '%s' class node (ID %s)",
506 cname
, child
->GetAttribute("id"));
509 //else: don't warn the user; it looks like "incomplete" classes
510 // never have any member...
514 // decode the non-empty list of IDs:
515 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
516 LogError("Invalid member IDs for '%s' class node (ID %s)",
517 cname
, child
->GetAttribute("id"));
523 // register this class also as possible return/argument type:
526 else if (n
== "PointerType" || n
== "ReferenceType" ||
527 n
== "CvQualifiedType" || n
== "ArrayType")
529 unsigned long type
= 0;
530 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
531 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
535 unsigned long attr
= 0;
536 if (n
== "PointerType")
537 attr
= ATTRIB_POINTER
;
538 else if (n
== "ReferenceType")
539 attr
= ATTRIB_REFERENCE
;
540 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
542 else if (n
== "ArrayType")
545 // these nodes make reference to other types... we'll resolve them later
546 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
548 else if (n
== "FunctionType" || n
== "MethodType")
550 /* TODO: incomplete */
552 unsigned long ret
= 0;
553 if (!getID(&ret
, child
->GetAttribute("returns")) || ret
== 0) {
554 LogError("Invalid empty returns value for '%s' node", n
);
558 // these nodes make reference to other types... we'll resolve them later
559 toResolveTypes
[id
] = toResolveTypeItem(ret
, 0);
561 else if (n
== "File")
563 if (!child
->GetAttribute("id").StartsWith("f")) {
564 LogError("Unexpected file ID: %s", id
);
568 // just ignore this node... all file IDs/names were already parsed
569 files
[id
] = child
->GetAttribute("name");
573 // we register everything else as a possible return/argument type:
574 const wxString
& name
= child
->GetAttribute("name");
579 //typeNames.Add(name);
584 // this may happen with unnamed structs/union, special ctors,
585 // or other exotic things which we are not interested to, since
586 // they're never used as return/argument types by wxWidgets methods
589 LogWarning("Type '%s' with ID '%s' does not have name attribute", n
, id
);
595 child
= child
->GetNext();
597 // give feedback to the user about the progress...
598 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
601 // some nodes with IDs referenced by methods as return/argument types, do reference
602 // in turn o ther nodes (see PointerType, ReferenceType and CvQualifierType above);
603 // thus we need to resolve their name iteratively:
604 while (toResolveTypes
.size()>0)
607 LogMessage("%d types were collected; %d types need yet to be resolved...",
608 types
.size(), toResolveTypes
.size());
610 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
611 i
!= toResolveTypes
.end();)
613 unsigned long id
= i
->first
;
614 unsigned long referenced
= i
->second
.ref
;
616 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
617 if (primary
!= types
.end())
619 // this to-resolve-type references a "primary" type
622 int attribs
= i
->second
.attribs
;
624 if (attribs
& ATTRIB_CONST
)
625 newtype
= "const " + primary
->second
;
626 if (attribs
& ATTRIB_REFERENCE
)
627 newtype
= primary
->second
+ "&";
628 if (attribs
& ATTRIB_POINTER
)
629 newtype
= primary
->second
+ "*";
630 if (attribs
& ATTRIB_ARRAY
)
631 newtype
= primary
->second
+ "[]";
633 // add the resolved type to the list of "primary" types
636 // this one has been resolved; erase it through its iterator!
637 toResolveTypes
.erase(i
);
639 // now iterator i is invalid; assign it again to the beginning
640 i
= toResolveTypes
.begin();
644 // then search in the referenced types themselves:
645 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
646 if (idx2
!= toResolveTypes
.end())
648 // merge this to-resolve-type with the idx2->second type
649 i
->second
.ref
= idx2
->second
.ref
;
650 i
->second
.attribs
|= idx2
->second
.attribs
;
652 // this type will eventually be solved in the next while() iteration
658 LogError("Cannot solve '%s' reference type!", referenced
);
661 typeIds
.Add(toResolveTypeIds
[i
]);
662 typeNames
.Add("TOFIX");
664 // this one has been resolved!
665 toResolveTypeIds
.RemoveAt(i
);
666 toResolveRefType
.RemoveAt(i
);
667 toResolveAttrib
.RemoveAt(i
);
675 // resolve header names
676 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
678 unsigned long fileID
= 0;
679 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
680 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
685 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
686 if (idx
== files
.end())
689 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
692 m_classes
[i
].SetHeader(idx
->second
);
695 // build the list of the wx methods
696 child
= doc
.GetRoot()->GetChildren();
699 wxString n
= child
->GetName();
701 // only register public methods
702 if (child
->GetAttribute("access") == "public" &&
703 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
705 unsigned long id
= 0;
706 if (!getID(&id
, child
->GetAttribute("id"))) {
707 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
711 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
712 if (it
!= members
.end())
714 wxClass
*p
= it
->second
;
716 // this <Method> node is a method of the i-th class!
718 if (!ParseMethod(child
, types
, newfunc
))
721 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
722 LogError("The method '%s' does not seem to be a ctor for '%s'",
723 newfunc
.GetName(), p
->GetName());
726 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
727 LogError("The method '%s' does not seem to be a dtor for '%s'",
728 newfunc
.GetName(), p
->GetName());
732 p
->AddMethod(newfunc
);
736 child
= child
->GetNext();
738 // give feedback to the user about the progress...
739 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
743 if (!CheckParseResults())
749 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
750 const wxTypeIdHashMap
& types
,
754 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
755 if (p
->GetName() == "Destructor")
757 else if (p
->GetName() == "OperatorMethod")
758 name
= "operator" + name
;
760 // resolve return type
762 unsigned long retid
= 0;
763 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
765 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
766 LogError("Empty return ID for method '%s', with ID '%s'",
767 name
, p
->GetAttribute("id"));
773 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
774 if (retidx
== types
.end()) {
775 LogError("Could not find return type ID '%s'", retid
);
779 ret
= wxType(retidx
->second
);
781 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
782 retidx
->second
, name
, p
->GetAttribute("id"));
787 // resolve argument types
788 wxTypeArray argtypes
;
789 wxArrayString argdefs
;
790 wxXmlNode
*arg
= p
->GetChildren();
793 if (arg
->GetName() == "Argument")
795 unsigned long id
= 0;
796 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
797 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
798 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
802 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
803 if (idx
== types
.end()) {
804 LogError("Could not find argument type ID '%s'", id
);
808 argtypes
.Add(wxType(idx
->second
));
810 wxString def
= arg
->GetAttribute("default");
811 if (def
.Contains("wxGetTranslation"))
812 argdefs
.Add(wxEmptyString
); // TODO: wxGetTranslation gives problems to gccxml
817 arg
= arg
->GetNext();
820 m
.SetReturnType(ret
);
822 m
.SetArgumentTypes(argtypes
, argdefs
);
823 m
.SetConst(p
->GetAttribute("const") == "1");
824 m
.SetStatic(p
->GetAttribute("static") == "1");
825 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
828 LogError("The prototype '%s' is not valid!", m
.GetAsString());
836 // ----------------------------------------------------------------------------
837 // wxXmlDoxygenInterface
838 // ----------------------------------------------------------------------------
840 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
845 LogMessage("Parsing %s...", filename
);
847 if (!index
.Load(filename
)) {
848 LogError("can't load %s", filename
);
852 // start processing the index:
853 if (index
.GetRoot()->GetName() != "doxygenindex") {
854 LogError("invalid root node for %s", filename
);
858 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
860 // process files referenced by this index file
861 compound
= index
.GetRoot()->GetChildren();
864 if (compound
->GetName() == "compound" &&
865 compound
->GetAttribute("kind") == "class")
867 wxString refid
= compound
->GetAttribute("refid");
869 wxFileName
fn(filename
);
870 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
874 compound
= compound
->GetNext();
878 if (!CheckParseResults())
884 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
891 LogMessage("Parsing %s...", filename
);
893 if (!doc
.Load(filename
)) {
894 LogError("can't load %s", filename
);
898 // start processing this compound definition XML
899 if (doc
.GetRoot()->GetName() != "doxygen") {
900 LogError("invalid root node for %s", filename
);
904 // build a list of wx classes
905 child
= doc
.GetRoot()->GetChildren();
908 if (child
->GetName() == "compounddef" &&
909 child
->GetAttribute("kind") == "class")
913 wxString absoluteFile
, header
;
915 wxXmlNode
*subchild
= child
->GetChildren();
918 if (subchild
->GetName() == "sectiondef" &&
919 subchild
->GetAttribute("kind") == "public-func")
922 wxXmlNode
*membernode
= subchild
->GetChildren();
925 if (membernode
->GetName() == "memberdef" &&
926 membernode
->GetAttribute("kind") == "function")
930 if (ParseMethod(membernode
, m
, header
))
932 if (absoluteFile
.IsEmpty())
933 absoluteFile
= header
;
934 else if (header
!= absoluteFile
)
936 LogError("The method '%s' is documented in a different "
937 "file from others (which belong to '%s') ?",
938 header
, absoluteFile
);
946 membernode
= membernode
->GetNext();
949 // all methods of this class were taken from the header "absoluteFile":
950 klass
.SetHeader(absoluteFile
);
952 else if (subchild
->GetName() == "compoundname")
954 klass
.SetName(subchild
->GetNodeContent());
956 /*else if (subchild->GetName() == "includes")
958 // NOTE: we'll get the header from the <location> tags
959 // scattered inside <memberdef> tags instead of
960 // this <includes> tag since it does not contain
961 // the absolute path of the header
963 klass.SetHeader(subchild->GetNodeContent());
966 subchild
= subchild
->GetNext();
971 m_classes
.Add(klass
);
973 LogWarning("discarding class '%s' with %d methods...",
974 klass
.GetName(), klass
.GetMethodCount());
977 child
= child
->GetNext();
979 // give feedback to the user about the progress...
980 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
986 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
992 // <a><b>this</b> is a <b>string</b></a>
1001 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1002 // this function returns "this is a string"
1004 wxXmlNode
*ref
= n
->GetChildren();
1006 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1007 text
+= ref
->GetNodeContent();
1008 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1009 text
+= ref
->GetContent();
1011 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1013 ref
= ref
->GetNext();
1019 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1025 wxXmlNode
*child
= p
->GetChildren();
1028 if (child
->GetName() == "name")
1029 m
.SetName(child
->GetNodeContent());
1030 else if (child
->GetName() == "type")
1031 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1032 else if (child
->GetName() == "param")
1034 wxString typestr
, defstr
, arrstr
;
1035 wxXmlNode
*n
= child
->GetChildren();
1038 if (n
->GetName() == "type")
1039 // if the <type> node has children, they should be all TEXT and <ref> nodes
1040 // and we need to take the text they contain, in the order they appear
1041 typestr
= GetTextFromChildren(n
);
1042 else if (n
->GetName() == "defval")
1043 // same for the <defval> node
1044 defstr
= GetTextFromChildren(n
);
1045 else if (n
->GetName() == "array")
1046 arrstr
= GetTextFromChildren(n
);
1051 if (typestr
.IsEmpty()) {
1052 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1056 args
.Add(wxType(typestr
+ arrstr
));
1059 else if (child
->GetName() == "location")
1061 if (child
->GetAttribute("line").ToLong(&line
))
1062 m
.SetLocation((int)line
);
1063 header
= child
->GetAttribute("file");
1066 child
= child
->GetNext();
1069 m
.SetArgumentTypes(args
, defs
);
1070 m
.SetConst(p
->GetAttribute("const")=="yes");
1071 m
.SetStatic(p
->GetAttribute("static")=="yes");
1072 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1075 LogError("The prototype '%s' is not valid!", m
.GetAsString());