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_UTF8
488 unsigned long val
= strtoul(str
+1, &end
, GCCXML_BASE
);
490 unsigned long val
= wcstoul(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_UTF8
508 size_t len
= strlen(str
);
510 size_t len
= wcslen(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_UTF8
524 unsigned long id
= strtoul(curpos
+1, &nexttoken
, GCCXML_BASE
);
526 unsigned long id
= wcstoul(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 wxToResolveTypeHashMap toResolveTypes
;
567 wxClassMemberIdHashMap members
;
568 wxTypeIdHashMap types
;
569 wxTypeIdHashMap files
;
571 // prealloc quite a lot of memory!
572 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
574 // build a list of wx classes and in general of all existent types
575 child
= doc
.GetRoot()->GetChildren();
578 const wxString
& n
= child
->GetName();
580 unsigned long id
= 0;
581 if (!getID(&id
, child
->GetAttribute("id")) || (id
== 0 && n
!= "File")) {
583 // NOTE: <File> nodes can have an id == "f0"...
585 LogError("Invalid id for node %s: %s", n
, child
->GetAttribute("id"));
591 wxString cname
= child
->GetAttribute("name");
592 if (cname
.IsEmpty()) {
593 LogError("Invalid empty name for '%s' node", n
);
597 // only register wx classes (do remember also the IDs of their members)
598 if (cname
.StartsWith("wx"))
600 // NB: "file" attribute contains an ID value that we'll resolve later
601 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file")));
603 const wxString
& ids
= child
->GetAttribute("members");
606 if (child
->GetAttribute("incomplete") != "1") {
607 LogError("Invalid member IDs for '%s' class node: %s",
608 cname
, child
->GetAttribute("id"));
611 //else: don't warn the user; it looks like "incomplete" classes
612 // never have any member...
616 // decode the non-empty list of IDs:
617 if (!getMemberIDs(&members
, &m_classes
.Last(), ids
)) {
618 LogError("Invalid member IDs for '%s' class node: %s",
619 cname
, child
->GetAttribute("id"));
625 // register this class also as possible return/argument type:
628 else if (n
== "PointerType" || n
== "ReferenceType" ||
629 n
== "CvQualifiedType" || n
== "ArrayType")
631 unsigned long type
= 0;
632 if (!getID(&type
, child
->GetAttribute("type")) || type
== 0) {
633 LogError("Invalid type for node %s: %s", n
, child
->GetAttribute("type"));
637 unsigned long attr
= 0;
638 if (n
== "PointerType")
639 attr
= ATTRIB_POINTER
;
640 else if (n
== "ReferenceType")
641 attr
= ATTRIB_REFERENCE
;
642 else if (n
== "CvQualifiedType" && child
->GetAttribute("const") == "1")
644 else if (n
== "ArrayType")
647 // these nodes make reference to other types... we'll resolve them later
648 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
650 else if (n
== "FunctionType" || n
== "MethodType")
653 TODO: parsing FunctionType and MethodType nodes is not as easy
654 as for other "simple" types.
658 wxXmlNode
*arg
= child
->GetChildren();
661 if (arg
->GetName() == "Argument")
662 argstr
+= arg
->GetAttribute("type") + ", ";
663 arg
= arg
->GetNext();
666 if (argstr
.Len() > 0)
667 argstr
= argstr
.Left(argstr
.Len()-2);
669 // these nodes make reference to other types... we'll resolve them later
670 //toResolveTypes[id] = toResolveTypeItem(ret, 0);
671 types
[id
] = child
->GetAttribute("returns") + "(" + argstr
+ ")";
673 else if (n
== "File")
675 if (!child
->GetAttribute("id").StartsWith("f")) {
676 LogError("Unexpected file ID: %s", child
->GetAttribute("id"));
680 // just ignore this node... all file IDs/names were already parsed
681 files
[id
] = child
->GetAttribute("name");
685 // we register everything else as a possible return/argument type:
686 const wxString
& name
= child
->GetAttribute("name");
691 //typeNames.Add(name);
696 // this may happen with unnamed structs/union, special ctors,
697 // or other exotic things which we are not interested to, since
698 // they're never used as return/argument types by wxWidgets methods
701 LogWarning("Type node '%s' with ID '%s' does not have name attribute",
702 n
, child
->GetAttribute("id"));
708 child
= child
->GetNext();
710 // give feedback to the user about the progress...
711 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
714 // some nodes with IDs referenced by methods as return/argument types, do reference
715 // in turn o ther nodes (see PointerType, ReferenceType and CvQualifierType above);
716 // thus we need to resolve their name iteratively:
717 while (toResolveTypes
.size()>0)
720 LogMessage("%d types were collected; %d types need yet to be resolved...",
721 types
.size(), toResolveTypes
.size());
723 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
724 i
!= toResolveTypes
.end();)
726 unsigned long id
= i
->first
;
727 unsigned long referenced
= i
->second
.ref
;
729 wxTypeIdHashMap::iterator primary
= types
.find(referenced
);
730 if (primary
!= types
.end())
732 // this to-resolve-type references a "primary" type
734 wxString newtype
= primary
->second
;
735 int attribs
= i
->second
.attribs
;
737 // attribs may contain a combination of ATTRIB_* flags:
738 if (attribs
& ATTRIB_CONST
)
739 newtype
= "const " + newtype
;
740 if (attribs
& ATTRIB_REFERENCE
)
741 newtype
= newtype
+ "&";
742 if (attribs
& ATTRIB_POINTER
)
743 newtype
= newtype
+ "*";
744 if (attribs
& ATTRIB_ARRAY
)
745 newtype
= newtype
+ "[]";
747 // add the resolved type to the list of "primary" types
750 // this one has been resolved; erase it through its iterator!
751 toResolveTypes
.erase(i
);
753 // now iterator i is invalid; assign it again to the beginning
754 i
= toResolveTypes
.begin();
758 // then search in the referenced types themselves:
759 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
760 if (idx2
!= toResolveTypes
.end())
762 // merge this to-resolve-type with the idx2->second type
763 i
->second
.ref
= idx2
->second
.ref
;
764 i
->second
.attribs
|= idx2
->second
.attribs
;
766 // this type will eventually be solved in the next while() iteration
771 LogError("Cannot solve '%s' reference type!", referenced
);
778 // resolve header names
779 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
781 unsigned long fileID
= 0;
782 if (!getID(&fileID
, m_classes
[i
].GetHeader()) || fileID
== 0) {
783 LogError("invalid header id: %s", m_classes
[i
].GetHeader());
788 wxTypeIdHashMap::const_iterator idx
= files
.find(fileID
);
789 if (idx
== files
.end())
792 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
795 m_classes
[i
].SetHeader(idx
->second
);
798 // build the list of the wx methods
799 child
= doc
.GetRoot()->GetChildren();
802 wxString n
= child
->GetName();
804 // only register public methods
805 if (child
->GetAttribute("access") == "public" &&
806 (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod"))
808 unsigned long id
= 0;
809 if (!getID(&id
, child
->GetAttribute("id"))) {
810 LogError("invalid ID for node '%s' with ID '%s'", n
, child
->GetAttribute("id"));
814 wxClassMemberIdHashMap::const_iterator it
= members
.find(id
);
815 if (it
!= members
.end())
817 wxClass
*p
= it
->second
;
819 // this <Method> node is a method of the i-th class!
821 if (!ParseMethod(child
, types
, newfunc
)) {
822 LogError("The method '%s' could not be added to class '%s'",
823 child
->GetAttribute("demangled"), p
->GetName());
827 if (newfunc
.IsCtor() && !p
->IsValidCtorForThisClass(newfunc
)) {
828 LogError("The method '%s' does not seem to be a ctor for '%s'",
829 newfunc
.GetName(), p
->GetName());
832 if (newfunc
.IsDtor() && !p
->IsValidDtorForThisClass(newfunc
)) {
833 LogError("The method '%s' does not seem to be a dtor for '%s'",
834 newfunc
.GetName(), p
->GetName());
838 p
->AddMethod(newfunc
);
842 child
= child
->GetNext();
844 // give feedback to the user about the progress...
845 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
848 if (!CheckParseResults())
854 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
855 const wxTypeIdHashMap
& types
,
859 wxString name
= p
->GetAttribute("name").Strip(wxString::both
);
860 if (p
->GetName() == "Destructor")
862 else if (p
->GetName() == "OperatorMethod")
863 name
= "operator" + name
;
865 // resolve return type
867 unsigned long retid
= 0;
868 if (!getID(&retid
, p
->GetAttribute("returns")) || retid
== 0)
870 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
871 LogError("Empty return ID for method '%s', with ID '%s'",
872 name
, p
->GetAttribute("id"));
878 wxTypeIdHashMap::const_iterator retidx
= types
.find(retid
);
879 if (retidx
== types
.end()) {
880 LogError("Could not find return type ID '%s'", retid
);
884 ret
= wxType(retidx
->second
);
886 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
887 retidx
->second
, name
, p
->GetAttribute("id"));
892 // resolve argument types
893 wxArgumentTypeArray argtypes
;
894 wxXmlNode
*arg
= p
->GetChildren();
897 if (arg
->GetName() == "Argument")
899 unsigned long id
= 0;
900 if (!getID(&id
, arg
->GetAttribute("type")) || id
== 0) {
901 LogError("Invalid argument type ID '%s' for method '%s' with ID %s",
902 arg
->GetAttribute("type"), name
, p
->GetAttribute("id"));
906 wxTypeIdHashMap::const_iterator idx
= types
.find(id
);
907 if (idx
== types
.end()) {
908 LogError("Could not find argument type ID '%s'", id
);
912 argtypes
.Add(wxArgumentType(idx
->second
, arg
->GetAttribute("default")));
915 arg
= arg
->GetNext();
918 m
.SetReturnType(ret
);
920 m
.SetArgumentTypes(argtypes
);
921 m
.SetConst(p
->GetAttribute("const") == "1");
922 m
.SetStatic(p
->GetAttribute("static") == "1");
924 // NOTE: gccxml is smart enough to mark as virtual those functions
925 // which are declared virtual in base classes but don't have
926 // the "virtual" keyword explicitely indicated in the derived
927 // classes... so we don't need any further logic for virtuals
929 m
.SetVirtual(p
->GetAttribute("virtual") == "1");
930 m
.SetPureVirtual(p
->GetAttribute("pure_virtual") == "1");
931 m
.SetDeprecated(p
->GetAttribute("attributes") == "deprecated");
934 LogError("The prototype '%s' is not valid!", m
.GetAsString());
943 // ----------------------------------------------------------------------------
944 // wxXmlDoxygenInterface global helpers
945 // ----------------------------------------------------------------------------
947 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
953 // <a><b>this</b> is a <b>string</b></a>
962 // unlike wxXmlNode::GetNodeContent() which would return " is a "
963 // this function returns "this is a string"
965 wxXmlNode
*ref
= n
->GetChildren();
967 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
968 text
+= ref
->GetNodeContent();
969 else if (ref
->GetType() == wxXML_TEXT_NODE
)
970 text
+= ref
->GetContent();
972 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
974 ref
= ref
->GetNext();
980 static bool HasTextNodeContaining(const wxXmlNode
*parent
, const wxString
& name
)
985 wxXmlNode
*p
= parent
->GetChildren();
988 switch (p
->GetType())
990 case wxXML_TEXT_NODE
:
991 if (p
->GetContent() == name
)
995 case wxXML_ELEMENT_NODE
:
996 // recurse into this node...
997 if (HasTextNodeContaining(p
, name
))
1012 static const wxXmlNode
* FindNodeNamed(const wxXmlNode
* parent
, const wxString
& name
)
1017 const wxXmlNode
*p
= parent
->GetChildren();
1020 if (p
->GetName() == name
)
1023 // search recursively in the children of this node
1024 const wxXmlNode
*ret
= FindNodeNamed(p
, name
);
1034 int GetAvailabilityFor(const wxXmlNode
*node
)
1036 // identify <onlyfor> custom XML tags
1037 const wxXmlNode
* onlyfor
= FindNodeNamed(node
, "onlyfor");
1039 return wxPORT_UNKNOWN
;
1041 wxArrayString ports
= wxSplit(onlyfor
->GetNodeContent(), ',');
1042 int nAvail
= wxPORT_UNKNOWN
;
1043 for (unsigned int i
=0; i
< ports
.GetCount(); i
++)
1045 if (!ports
[i
].StartsWith("wx")) {
1046 LogError("unexpected port ID '%s'", ports
[i
]);
1050 nAvail
|= wxPlatformInfo::GetPortId(ports
[i
].Mid(2));
1057 // ----------------------------------------------------------------------------
1058 // wxXmlDoxygenInterface
1059 // ----------------------------------------------------------------------------
1061 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
1063 wxXmlDocument index
;
1064 wxXmlNode
*compound
;
1066 LogMessage("Parsing %s...", filename
);
1068 if (!index
.Load(filename
)) {
1069 LogError("can't load %s", filename
);
1073 // start processing the index:
1074 if (index
.GetRoot()->GetName() != "doxygenindex") {
1075 LogError("invalid root node for %s", filename
);
1079 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
1081 // process files referenced by this index file
1082 compound
= index
.GetRoot()->GetChildren();
1085 if (compound
->GetName() == "compound" &&
1086 compound
->GetAttribute("kind") == "class")
1088 wxString refid
= compound
->GetAttribute("refid");
1090 wxFileName
fn(filename
);
1091 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1095 compound
= compound
->GetNext();
1099 if (!CheckParseResults())
1105 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1112 LogMessage("Parsing %s...", filename
);
1114 if (!doc
.Load(filename
)) {
1115 LogError("can't load %s", filename
);
1119 // start processing this compound definition XML
1120 if (doc
.GetRoot()->GetName() != "doxygen") {
1121 LogError("invalid root node for %s", filename
);
1125 // build a list of wx classes
1126 child
= doc
.GetRoot()->GetChildren();
1129 if (child
->GetName() == "compounddef" &&
1130 child
->GetAttribute("kind") == "class")
1134 wxString absoluteFile
, header
;
1136 wxXmlNode
*subchild
= child
->GetChildren();
1139 if (subchild
->GetName() == "sectiondef" &&
1140 subchild
->GetAttribute("kind") == "public-func")
1143 wxXmlNode
*membernode
= subchild
->GetChildren();
1146 if (membernode
->GetName() == "memberdef" &&
1147 membernode
->GetAttribute("kind") == "function")
1151 if (!ParseMethod(membernode
, m
, header
)) {
1152 LogError("The method '%s' could not be added to class '%s'",
1153 m
.GetName(), klass
.GetName());
1157 if (absoluteFile
.IsEmpty())
1158 absoluteFile
= header
;
1159 else if (header
!= absoluteFile
)
1161 LogError("The method '%s' is documented in a different "
1162 "file from others (which belong to '%s') ?",
1163 header
, absoluteFile
);
1170 membernode
= membernode
->GetNext();
1173 // all methods of this class were taken from the header "absoluteFile":
1174 klass
.SetHeader(absoluteFile
);
1176 else if (subchild
->GetName() == "compoundname")
1178 klass
.SetName(subchild
->GetNodeContent());
1180 /*else if (subchild->GetName() == "includes")
1182 // NOTE: we'll get the header from the <location> tags
1183 // scattered inside <memberdef> tags instead of
1184 // this <includes> tag since it does not contain
1185 // the absolute path of the header
1187 klass.SetHeader(subchild->GetNodeContent());
1189 else if (subchild
->GetName() == "detaileddescription")
1191 // identify <onlyfor> custom XML tags
1192 klass
.SetAvailability(GetAvailabilityFor(subchild
));
1195 subchild
= subchild
->GetNext();
1200 m_classes
.Add(klass
);
1202 LogWarning("discarding class '%s' with %d methods...",
1203 klass
.GetName(), klass
.GetMethodCount());
1206 child
= child
->GetNext();
1208 // give feedback to the user about the progress...
1209 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1215 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1217 wxArgumentTypeArray args
;
1220 wxXmlNode
*child
= p
->GetChildren();
1223 if (child
->GetName() == "name")
1224 m
.SetName(child
->GetNodeContent());
1225 else if (child
->GetName() == "type")
1226 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1227 else if (child
->GetName() == "param")
1229 wxString typestr
, namestr
, defstr
, arrstr
;
1230 wxXmlNode
*n
= child
->GetChildren();
1233 if (n
->GetName() == "type")
1234 // if the <type> node has children, they should be all TEXT and <ref> nodes
1235 // and we need to take the text they contain, in the order they appear
1236 typestr
= GetTextFromChildren(n
);
1237 else if (n
->GetName() == "declname")
1238 namestr
= GetTextFromChildren(n
);
1239 else if (n
->GetName() == "defval")
1240 defstr
= GetTextFromChildren(n
).Strip(wxString::both
);
1241 else if (n
->GetName() == "array")
1242 arrstr
= GetTextFromChildren(n
);
1247 if (typestr
.IsEmpty()) {
1248 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1252 wxArgumentType
newarg(typestr
+ arrstr
, defstr
, namestr
);
1254 // can we use preprocessor output to transform the default value
1255 // into the same form which gets processed by wxXmlGccInterface?
1256 wxStringHashMap::const_iterator it
= m_preproc
.find(defstr
);
1257 if (it
!= m_preproc
.end())
1258 newarg
.SetDefaultValue(defstr
, it
->second
);
1262 else if (child
->GetName() == "location")
1265 if (child
->GetAttribute("line").ToLong(&line
))
1266 m
.SetLocation((int)line
);
1267 header
= child
->GetAttribute("file");
1269 else if (child
->GetName() == "detaileddescription")
1271 // when a method has a @deprecated tag inside its description,
1272 // Doxygen outputs somewhere nested inside <detaileddescription>
1273 // a <xreftitle>Deprecated</xreftitle> tag.
1274 m
.SetDeprecated(HasTextNodeContaining(child
, "Deprecated"));
1276 // identify <onlyfor> custom XML tags
1277 m
.SetAvailability(GetAvailabilityFor(child
));
1280 child
= child
->GetNext();
1283 m
.SetArgumentTypes(args
);
1284 m
.SetConst(p
->GetAttribute("const")=="yes");
1285 m
.SetStatic(p
->GetAttribute("static")=="yes");
1287 // NOTE: Doxygen is smart enough to mark as virtual those functions
1288 // which are declared virtual in base classes but don't have
1289 // the "virtual" keyword explicitely indicated in the derived
1290 // classes... so we don't need any further logic for virtuals
1292 m
.SetVirtual(p
->GetAttribute("virt")=="virtual");
1293 m
.SetPureVirtual(p
->GetAttribute("virt")=="pure-virtual");
1296 LogError("The prototype '%s' is not valid!", m
.GetAsString());