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
);
88 bool wxType::IsOk() const
90 // NB: m_strType can contain the :: operator; think to e.g. the
91 // "reverse_iterator_impl<wxString::const_iterator>" type
92 // It can also contain commas, * and & operators etc
94 return !m_strTypeClean
.IsEmpty();
97 bool wxType::operator==(const wxType
& m
) const
99 // brain-dead comparison:
101 if (m_strTypeClean
== m
.m_strTypeClean
&&
102 IsConst() == m
.IsConst() &&
103 IsStatic() == m
.IsStatic() &&
104 IsPointer() == m
.IsPointer() &&
105 IsReference() == m
.IsReference())
112 // ----------------------------------------------------------------------------
114 // ----------------------------------------------------------------------------
116 void wxArgumentType::SetDefaultValue(const wxString
& defval
, const wxString
& defvalForCmp
)
118 m_strDefaultValue
=defval
.Strip(wxString::both
);
119 m_strDefaultValueForCmp
=defvalForCmp
.Strip(wxString::both
);
121 // in order to make valid&simple comparison on argument defaults,
122 // we reduce some of the multiple forms in which the same things may appear
124 if (m_strDefaultValue
== "0u")
125 m_strDefaultValue
= "0";
128 m_strDefaultValue.Replace("0", "NULL");
130 m_strDefaultValue.Replace("NULL", "0");
133 if (m_strDefaultValue
.Contains("wxGetTranslation"))
134 m_strDefaultValue
= "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
137 bool wxArgumentType::operator==(const wxArgumentType
& m
) const
139 if ((const wxType
&)(*this) != (const wxType
&)m
)
142 const wxString
& def1
= m_strDefaultValueForCmp
.IsEmpty() ? m_strDefaultValue
: m_strDefaultValueForCmp
;
143 const wxString
& def2
= m
.m_strDefaultValueForCmp
.IsEmpty() ? m
.m_strDefaultValue
: m
.m_strDefaultValueForCmp
;
147 // maybe the default values are numbers.
148 // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
149 // To handle these cases, we try to convert the default value strings to numbers:
150 long def1val
, def2val
;
151 if (def1
.ToLong(&def1val
, 0 /* auto-detect */) &&
152 def2
.ToLong(&def2val
, 0 /* auto-detect */))
154 if (def1val
== def2val
)
155 return true; // the default values match
161 // we deliberately avoid checks on the argument name
167 // ----------------------------------------------------------------------------
169 // ----------------------------------------------------------------------------
171 bool wxMethod::IsOk() const
173 // NOTE: m_retType can be a wxEmptyType, and means that this method
174 // is a ctor or a dtor.
175 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
176 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
180 if (m_strName
.IsEmpty())
183 // a function can't be both const and static or virtual and static!
184 if ((m_bConst
&& m_bStatic
) || ((m_bVirtual
|| m_bPureVirtual
) && m_bStatic
)) {
185 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
189 wxASSERT(!m_bPureVirtual
|| (m_bPureVirtual
&& m_bVirtual
));
191 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
192 if (!m_args
[i
].IsOk()) {
193 LogError("'%s' method has invalid %d-th argument type: %s",
194 m_strName
, i
+1, m_args
[i
].GetAsString());
198 // NB: the default value of the arguments can contain pretty much everything
199 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
200 // so we don't do any test on their contents
201 if (m_args
.GetCount()>0)
203 bool previousArgHasDefault
= m_args
[0].HasDefaultValue();
204 for (unsigned int i
=1; i
<m_args
.GetCount(); i
++)
206 if (previousArgHasDefault
&& !m_args
[i
].HasDefaultValue()) {
207 LogError("'%s' method has %d-th argument which has no default value "
208 "(while the previous one had one!)",
213 previousArgHasDefault
= m_args
[i
].HasDefaultValue();
220 bool wxMethod::operator==(const wxMethod
& m
) const
222 if (GetReturnType() != m
.GetReturnType() ||
223 GetName() != m
.GetName() ||
224 IsConst() != m
.IsConst() ||
225 IsStatic() != m
.IsStatic() ||
226 IsVirtual() != m
.IsVirtual() ||
227 IsPureVirtual() != m
.IsPureVirtual() ||
228 IsDeprecated() != m
.IsDeprecated())
231 if (m_args
.GetCount()!=m
.m_args
.GetCount())
234 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
235 if (m_args
[i
] != m
.m_args
[i
])
241 wxString
wxMethod::GetAsString(bool bWithArgumentNames
) const
245 if (m_retType
!=wxEmptyType
)
246 ret
+= m_retType
.GetAsString() + " ";
247 //else; this is a ctor or dtor
249 ret
+= m_strName
+ "(";
251 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
253 ret
+= m_args
[i
].GetAsString();
255 const wxString
& name
= m_args
[i
].GetArgumentName();
256 if (bWithArgumentNames
&& !name
.IsEmpty())
259 const wxString
& def
= m_args
[i
].GetDefaultValue();
266 if (m_args
.GetCount()>0)
267 ret
= ret
.Left(ret
.Len()-2);
274 ret
= "static " + ret
;
275 if (m_bVirtual
|| m_bPureVirtual
)
276 ret
= "virtual " + ret
;
280 // in doxygen headers we don't need wxDEPRECATED:
282 // ret = "wxDEPRECATED( " + ret + " )";
287 void wxMethod::Dump(wxTextOutputStream
& stream
) const
289 stream
<< "[" + m_retType
.GetAsString() + "]";
290 stream
<< "[" + m_strName
+ "]";
292 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
293 stream
<< "[" + m_args
[i
].GetAsString() + " " + m_args
[i
].GetArgumentName() +
294 "=" + m_args
[i
].GetDefaultValue() + "]";
301 stream
<< " VIRTUAL";
303 stream
<< " PURE-VIRTUAL";
305 stream
<< " DEPRECATED";
310 // ----------------------------------------------------------------------------
312 // ----------------------------------------------------------------------------
314 wxString
wxClass::GetNameWithoutTemplate() const
316 // NB: I'm not sure this is the right terminology for this function!
318 if (m_strName
.Contains("<"))
319 return m_strName
.Left(m_strName
.Find("<"));
323 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
325 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
326 // named wxWritableCharTypeBuffer, without the <...> part!
328 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
334 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
336 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
342 void wxClass::Dump(wxTextOutputStream
& out
) const
344 out
<< m_strName
+ "\n";
346 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
348 // dump all our methods
350 m_methods
[i
].Dump(out
);
357 bool wxClass::CheckConsistency() const
359 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
360 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
361 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
363 LogError("class %s has two methods with the same prototype: '%s'",
364 m_strName
, m_methods
[i
].GetAsString());
371 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
373 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
374 if (m_methods
[i
] == m
)
375 return &m_methods
[i
];
379 wxMethodPtrArray
wxClass::FindMethodNamed(const wxString
& name
) const
381 wxMethodPtrArray ret
;
383 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
384 if (m_methods
[i
].GetName() == name
)
385 ret
.Add(&m_methods
[i
]);
391 // ----------------------------------------------------------------------------
393 // ----------------------------------------------------------------------------
395 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
397 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
399 // sort alphabetically
400 return item1
->GetName().Cmp(item2
->GetName());
403 void wxXmlInterface::Dump(const wxString
& filename
)
405 wxFFileOutputStream
apioutput( filename
);
406 wxTextOutputStream
apiout( apioutput
);
408 // dump the classes in alphabetical order
409 wxSortedClassArray
sorted(CompareWxClassObjects
);
410 sorted
.Alloc(m_classes
.GetCount());
411 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
412 sorted
.Add(&m_classes
[i
]);
414 // now they have been sorted
415 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
416 sorted
[i
]->Dump(apiout
);
419 bool wxXmlInterface::CheckParseResults() const
421 // this check can be quite slow, so do it only for debug releases:
423 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
424 if (!m_classes
[i
].CheckConsistency())
431 wxClassPtrArray
wxXmlInterface::FindClassesDefinedIn(const wxString
& headerfile
) const
435 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
436 if (m_classes
[i
].GetHeader() == headerfile
)
437 ret
.Add(&m_classes
[i
]);
443 // ----------------------------------------------------------------------------
444 // wxXmlGccInterface helper declarations
445 // ----------------------------------------------------------------------------
447 #define ATTRIB_CONST 1
448 #define ATTRIB_REFERENCE 2
449 #define ATTRIB_POINTER 4
450 #define ATTRIB_ARRAY 8
452 #define GCCXML_BASE 35
454 class toResolveTypeItem
457 toResolveTypeItem() { attribs
=0; }
458 toResolveTypeItem(unsigned int refID
, unsigned int attribint
)
459 : ref(refID
), attribs(attribint
) {}
461 unsigned long ref
, attribs
;
466 // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
467 WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem
,
468 wxIntegerHash
, wxIntegerEqual
,
469 wxToResolveTypeHashMap
);
471 // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
472 WX_DECLARE_HASH_MAP( unsigned long, wxClass
*,
473 wxIntegerHash
, wxIntegerEqual
,
474 wxClassMemberIdHashMap
);
477 typedef std::map
<unsigned long, toResolveTypeItem
> wxToResolveTypeHashMap
;
481 // utility to parse gccXML ID values;
482 // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
483 // but is a little bit faster
484 bool getID(unsigned long *id
, const wxStringCharType
* str
)
486 wxStringCharType
*end
;
487 #if wxUSE_UNICODE_WCHAR
488 unsigned long val
= wcstoul(str
+1, &end
, GCCXML_BASE
);
490 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
493 // return true only if scan was stopped by the terminating NUL and
494 // if the string was not empty to start with and no under/overflow
496 if ( *end
!= '\0' || end
== str
+1 || errno
== ERANGE
|| errno
== EINVAL
)
503 // utility specialized to parse efficiently the gccXML list of IDs which occur
504 // in nodes like <Class> ones... i.e. numeric values separed by " _" token
505 bool getMemberIDs(wxClassMemberIdHashMap
* map
, wxClass
* p
, const wxStringCharType
* str
)
507 #if wxUSE_UNICODE_WCHAR
508 size_t len
= wcslen(str
);
510 size_t len
= strlen(str
);
513 if (len
== 0 || str
[0] != '_')
516 const wxStringCharType
*curpos
= str
,
518 wxStringCharType
*nexttoken
;
522 // curpos always points to the underscore of the next token to parse:
523 #if wxUSE_UNICODE_WCHAR
524 unsigned long id
= wcstoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
526 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
528 if ( *nexttoken
!= ' ' || errno
== ERANGE
|| errno
== EINVAL
)
531 // advance current position
532 curpos
= nexttoken
+ 1;
534 // add this ID to the hashmap
535 wxClassMemberIdHashMap::value_type
v(id
, p
);
543 // ----------------------------------------------------------------------------
545 // ----------------------------------------------------------------------------
547 bool wxXmlGccInterface::Parse(const wxString
& filename
)
553 LogMessage("Parsing %s...", filename
);
555 if (!doc
.Load(filename
)) {
556 LogError("can't load %s", filename
);
560 // start processing the XML file
561 if (doc
.GetRoot()->GetName() != "GCC_XML") {
562 LogError("invalid root node for %s", filename
);
566 wxString version
= doc
.GetRoot()->GetAttribute("cvs_revision");
569 #define MIN_REVISION 120
571 if (!version
.StartsWith("1."))
575 unsigned long rev
= 0;
576 if (!version
.Mid(2).ToULong(&rev
))
579 if (rev
< MIN_REVISION
)
585 LogError("The version of GCC-XML used for the creation of %s is too old; "
586 "the cvs_revision attribute of the root node reports '%s', "
587 "minimal required is 1.%d.", filename
, version
, MIN_REVISION
);
591 wxToResolveTypeHashMap toResolveTypes
;
592 wxClassMemberIdHashMap members
;
593 wxTypeIdHashMap types
;
594 wxTypeIdHashMap files
;
596 // prealloc quite a lot of memory!
597 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
599 // build a list of wx classes and in general of all existent types
600 child
= doc
.GetRoot()->GetChildren();
603 const wxString
& n
= child
->GetName();
605 unsigned long id
= 0;
606 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
608 // NOTE: <File> nodes can have an id == "f0"...
610 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
616 wxString cname
= child
->GetAttribute("name");
617 if (cname
.IsEmpty()) {
618 LogError("Invalid empty name for '%s' node", n
);
622 // only register wx classes (do remember also the IDs of their members)
623 if (cname
.StartsWith("wx"))
625 // NB: "file" attribute contains an ID value that we'll resolve later
626 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
628 const wxString
& ids
= child
->GetAttribute("members");
631 if (child
->GetAttribute("incomplete") != "1") {
632 LogError("Invalid member IDs for '%s' class node: %s",
633 cname
, child
->GetAttribute("id"));
636 //else: don't warn the user; it looks like "incomplete" classes
637 // never have any member...
641 // decode the non-empty list of IDs:
642 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
643 LogError("Invalid member IDs for '%s' class node: %s",
644 cname
, child
->GetAttribute("id"));
650 // register this class also as possible return/argument type:
653 else if (n
== "PointerType" || n
== "ReferenceType" ||
654 n
== "CvQualifiedType" || n
== "ArrayType")
656 unsigned long type
= 0;
657 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
658 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
662 unsigned long attr
= 0;
663 if (n
== "PointerType")
664 attr
= ATTRIB_POINTER
;
665 else if (n
== "ReferenceType")
666 attr
= ATTRIB_REFERENCE
;
667 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
669 else if (n
== "ArrayType")
672 // these nodes make reference to other types... we'll resolve them later
673 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
675 else if (n
== "FunctionType" || n
== "MethodType")
678 TODO: parsing FunctionType and MethodType nodes is not as easy
679 as for other "simple" types.
683 wxXmlNode
*arg
= child
->GetChildren();
686 if (arg
->GetName() == "Argument")
687 argstr
+= arg
->GetAttribute("type") + ", ";
688 arg
= arg
->GetNext();
691 if (argstr
.Len() > 0)
692 argstr
= argstr
.Left(argstr
.Len()-2);
694 // these nodes make reference to other types... we'll resolve them later
695 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
696 types
[id
] = child
->GetAttribute("returns") + "(" + argstr
+ ")";
698 else if (n
== "File")
700 if (!child
->GetAttribute("id").StartsWith("f")) {
701 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
705 // just ignore this node... all file IDs/names were already parsed
706 files
[id
] = child
->GetAttribute("name");
710 // we register everything else as a possible return/argument type:
711 const wxString
& name
= child
->GetAttribute("name");
716 //typeNames.Add(name);
721 // this may happen with unnamed structs/union, special ctors,
722 // or other exotic things which we are not interested to, since
723 // they're never used as return/argument types by wxWidgets methods
726 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
727 n
, child
->GetAttribute("id"));
733 child
= child
->GetNext();
735 // give feedback to the user about the progress...
736 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
739 // some nodes with IDs referenced by methods as return/argument types, do reference
740 // in turn o ther nodes (see PointerType, ReferenceType and CvQualifierType above);
741 // thus we need to resolve their name iteratively:
742 while (toResolveTypes
.size()>0)
745 LogMessage("%d types were collected; %d types need yet to be resolved...",
746 types
.size(), toResolveTypes
.size());
748 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
749 i
!= toResolveTypes
.end();)
751 unsigned long id
= i
->first
;
752 unsigned long referenced
= i
->second
.ref
;
754 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
755 if (primary
!= types
.end())
757 // this to-resolve-type references a "primary" type
759 wxString newtype
= primary
->second
;
760 int attribs
= i
->second
.attribs
;
762 // attribs may contain a combination of ATTRIB_* flags:
763 if (attribs
& ATTRIB_CONST
)
764 newtype
= "const " + newtype
;
765 if (attribs
& ATTRIB_REFERENCE
)
766 newtype
= newtype
+ "&";
767 if (attribs
& ATTRIB_POINTER
)
768 newtype
= newtype
+ "*";
769 if (attribs
& ATTRIB_ARRAY
)
770 newtype
= newtype
+ "[]";
772 // add the resolved type to the list of "primary" types
775 // this one has been resolved; erase it through its iterator!
776 toResolveTypes
.erase(i
);
778 // now iterator i is invalid; assign it again to the beginning
779 i
= toResolveTypes
.begin();
783 // then search in the referenced types themselves:
784 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
785 if (idx2
!= toResolveTypes
.end())
787 // merge this to-resolve-type with the idx2->second type
788 i
->second
.ref
= idx2
->second
.ref
;
789 i
->second
.attribs
|= idx2
->second
.attribs
;
791 // this type will eventually be solved in the next while() iteration
796 LogError("Cannot solve '%s' reference type!", referenced
);
803 // resolve header names
804 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
806 unsigned long fileID
= 0;
807 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
808 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
813 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
814 if (idx
== files
.end())
817 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
820 m_classes
[i
].SetHeader(idx
->second
);
823 // build the list of the wx methods
824 child
= doc
.GetRoot()->GetChildren();
827 wxString n
= child
->GetName();
829 // only register public methods
830 if (child
->GetAttribute("access") == "public" &&
831 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
833 unsigned long id
= 0;
834 if (!getID(&id
, child
->GetAttribute("id"))) {
835 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
839 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
840 if (it
!= members
.end())
842 wxClass
*p
= it
->second
;
844 // this <Method> node is a method of the i-th class!
846 if (!ParseMethod(child
, types
, newfunc
)) {
847 LogError("The method '%s' could not be added to class '%s'",
848 child
->GetAttribute("demangled"), p
->GetName());
852 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
853 LogError("The method '%s' does not seem to be a ctor for '%s'",
854 newfunc
.GetName(), p
->GetName());
857 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
858 LogError("The method '%s' does not seem to be a dtor for '%s'",
859 newfunc
.GetName(), p
->GetName());
863 p
->AddMethod(newfunc
);
867 child
= child
->GetNext();
869 // give feedback to the user about the progress...
870 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
873 if (!CheckParseResults())
879 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
880 const wxTypeIdHashMap
& types
,
884 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
885 if (p
->GetName() == "Destructor")
887 else if (p
->GetName() == "OperatorMethod")
888 name
= "operator" + name
;
890 // resolve return type
892 unsigned long retid
= 0;
893 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
895 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
896 LogError("Empty return ID for method '%s', with ID '%s'",
897 name
, p
->GetAttribute("id"));
903 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
904 if (retidx
== types
.end()) {
905 LogError("Could not find return type ID '%s'", retid
);
909 ret
= wxType(retidx
->second
);
911 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
912 retidx
->second
, name
, p
->GetAttribute("id"));
917 // resolve argument types
918 wxArgumentTypeArray argtypes
;
919 wxXmlNode
*arg
= p
->GetChildren();
922 if (arg
->GetName() == "Argument")
924 unsigned long id
= 0;
925 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
926 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
927 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
931 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
932 if (idx
== types
.end()) {
933 LogError("Could not find argument type ID '%s'", id
);
937 argtypes
.Add(wxArgumentType(idx
->second
, arg
->GetAttribute("default")));
940 arg
= arg
->GetNext();
943 m
.SetReturnType(ret
);
945 m
.SetArgumentTypes(argtypes
);
946 m
.SetConst(p
->GetAttribute("const") == "1");
947 m
.SetStatic(p
->GetAttribute("static") == "1");
949 // NOTE: gccxml is smart enough to mark as virtual those functions
950 // which are declared virtual in base classes but don't have
951 // the "virtual" keyword explicitely indicated in the derived
952 // classes... so we don't need any further logic for virtuals
954 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
955 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
956 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
959 LogError("The prototype '%s' is not valid!", m
.GetAsString());
968 // ----------------------------------------------------------------------------
969 // wxXmlDoxygenInterface global helpers
970 // ----------------------------------------------------------------------------
972 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
978 // <a><b>this</b> is a <b>string</b></a>
987 // unlike wxXmlNode::GetNodeContent() which would return " is a "
988 // this function returns "this is a string"
990 wxXmlNode
*ref
= n
->GetChildren();
992 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
993 text
+= ref
->GetNodeContent();
994 else if (ref
->GetType() == wxXML_TEXT_NODE
)
995 text
+= ref
->GetContent();
997 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
999 ref
= ref
->GetNext();
1005 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
1010 wxXmlNode
*p
= parent
->GetChildren();
1013 switch (p
->GetType())
1015 case wxXML_TEXT_NODE
:
1016 if (p
->GetContent() == name
)
1020 case wxXML_ELEMENT_NODE
:
1021 // recurse into this node...
1022 if (HasTextNodeContaining(p
, name
))
1037 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1042 const wxXmlNode
*p
= parent
->GetChildren();
1045 if (p
->GetName() == name
)
1048 // search recursively in the children of this node
1049 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1059 int GetAvailabilityFor(const wxXmlNode
*node
)
1061 // identify <onlyfor> custom XML tags
1062 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1064 return wxPORT_UNKNOWN
;
1066 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1067 int nAvail
= wxPORT_UNKNOWN
;
1068 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1070 if (!ports
[i
].StartsWith("wx")) {
1071 LogError("unexpected port ID '%s'", ports
[i
]);
1075 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1082 // ----------------------------------------------------------------------------
1083 // wxXmlDoxygenInterface
1084 // ----------------------------------------------------------------------------
1086 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1088 wxXmlDocument index
;
1089 wxXmlNode
*compound
;
1091 LogMessage("Parsing %s...", filename
);
1093 if (!index
.Load(filename
)) {
1094 LogError("can't load %s", filename
);
1098 // start processing the index:
1099 if (index
.GetRoot()->GetName() != "doxygenindex") {
1100 LogError("invalid root node for %s", filename
);
1105 NB: we may need in future to do a version-check here if the
1106 format of the XML generated by doxygen changes.
1107 For now (doxygen version 1.5.5), this check is not required
1108 since AFAIK the XML format never changed since it was introduced.
1111 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1113 // process files referenced by this index file
1114 compound
= index
.GetRoot()->GetChildren();
1117 if (compound
->GetName() == "compound" &&
1118 compound
->GetAttribute("kind") == "class")
1120 wxString refid
= compound
->GetAttribute("refid");
1122 wxFileName
fn(filename
);
1123 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1127 compound
= compound
->GetNext();
1131 if (!CheckParseResults())
1137 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1144 LogMessage("Parsing %s...", filename
);
1146 if (!doc
.Load(filename
)) {
1147 LogError("can't load %s", filename
);
1151 // start processing this compound definition XML
1152 if (doc
.GetRoot()->GetName() != "doxygen") {
1153 LogError("invalid root node for %s", filename
);
1157 // build a list of wx classes
1158 child
= doc
.GetRoot()->GetChildren();
1161 if (child
->GetName() == "compounddef" &&
1162 child
->GetAttribute("kind") == "class")
1166 wxString absoluteFile
, header
;
1168 wxXmlNode
*subchild
= child
->GetChildren();
1171 if (subchild
->GetName() == "sectiondef" &&
1172 subchild
->GetAttribute("kind") == "public-func")
1175 wxXmlNode
*membernode
= subchild
->GetChildren();
1178 if (membernode
->GetName() == "memberdef" &&
1179 membernode
->GetAttribute("kind") == "function")
1183 if (!ParseMethod(membernode
, m
, header
)) {
1184 LogError("The method '%s' could not be added to class '%s'",
1185 m
.GetName(), klass
.GetName());
1189 if (absoluteFile
.IsEmpty())
1190 absoluteFile
= header
;
1191 else if (header
!= absoluteFile
)
1193 LogError("The method '%s' is documented in a different "
1194 "file from others (which belong to '%s') ?",
1195 header
, absoluteFile
);
1202 membernode
= membernode
->GetNext();
1205 // all methods of this class were taken from the header "absoluteFile":
1206 klass
.SetHeader(absoluteFile
);
1208 else if (subchild
->GetName() == "compoundname")
1210 klass
.SetName(subchild
->GetNodeContent());
1212 /*else if (subchild->GetName() == "includes")
1214 // NOTE: we'll get the header from the <location> tags
1215 // scattered inside <memberdef> tags instead of
1216 // this <includes> tag since it does not contain
1217 // the absolute path of the header
1219 klass.SetHeader(subchild->GetNodeContent());
1221 else if (subchild
->GetName() == "detaileddescription")
1223 // identify <onlyfor> custom XML tags
1224 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1227 subchild
= subchild
->GetNext();
1232 m_classes
.Add(klass
);
1234 LogWarning("discarding class '%s' with %d methods...",
1235 klass
.GetName(), klass
.GetMethodCount());
1238 child
= child
->GetNext();
1240 // give feedback to the user about the progress...
1241 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1247 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1249 wxArgumentTypeArray args
;
1252 wxXmlNode
*child
= p
->GetChildren();
1255 if (child
->GetName() == "name")
1256 m
.SetName(child
->GetNodeContent());
1257 else if (child
->GetName() == "type")
1258 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1259 else if (child
->GetName() == "param")
1261 wxString typestr
, namestr
, defstr
, arrstr
;
1262 wxXmlNode
*n
= child
->GetChildren();
1265 if (n
->GetName() == "type")
1266 // if the <type> node has children, they should be all TEXT and <ref> nodes
1267 // and we need to take the text they contain, in the order they appear
1268 typestr
= GetTextFromChildren(n
);
1269 else if (n
->GetName() == "declname")
1270 namestr
= GetTextFromChildren(n
);
1271 else if (n
->GetName() == "defval")
1272 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1273 else if (n
->GetName() == "array")
1274 arrstr
= GetTextFromChildren(n
);
1279 if (typestr
.IsEmpty()) {
1280 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1284 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1286 // can we use preprocessor output to transform the default value
1287 // into the same form which gets processed by wxXmlGccInterface?
1288 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1289 if (it
!= m_preproc
.end())
1290 newarg
.SetDefaultValue(defstr
, it
->second
);
1294 else if (child
->GetName() == "location")
1297 if (child
->GetAttribute("line").ToLong(&line
))
1298 m
.SetLocation((int)line
);
1299 header
= child
->GetAttribute("file");
1301 else if (child
->GetName() == "detaileddescription")
1303 // when a method has a @deprecated tag inside its description,
1304 // Doxygen outputs somewhere nested inside <detaileddescription>
1305 // a <xreftitle>Deprecated</xreftitle> tag.
1306 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1308 // identify <onlyfor> custom XML tags
1309 m
.SetAvailability(GetAvailabilityFor(child
));
1312 child
= child
->GetNext();
1315 m
.SetArgumentTypes(args
);
1316 m
.SetConst(p
->GetAttribute("const")=="yes");
1317 m
.SetStatic(p
->GetAttribute("static")=="yes");
1319 // NOTE: Doxygen is smart enough to mark as virtual those functions
1320 // which are declared virtual in base classes but don't have
1321 // the "virtual" keyword explicitely indicated in the derived
1322 // classes... so we don't need any further logic for virtuals
1324 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1325 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1328 LogError("The prototype '%s' is not valid!", m
.GetAsString());