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"
27 #include "xmlparser.h"
29 #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
30 #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
31 #define ESTIMATED_NUM_TYPES 50000 // used only by wxGccXmlInterface to prealloc mem
32 #define ESTIMATED_NUM_FILES 800 // used only by wxGccXmlInterface to prealloc mem
34 WX_DEFINE_OBJARRAY(wxTypeArray
)
35 WX_DEFINE_OBJARRAY(wxMethodArray
)
36 WX_DEFINE_OBJARRAY(wxClassArray
)
39 // declared in ifacecheck.cpp
40 extern bool g_verbose
;
44 // ----------------------------------------------------------------------------
46 // ----------------------------------------------------------------------------
50 void wxType::SetFromString(const wxString
& t
)
52 m_strType
= t
.Strip(wxString::both
);
54 // [] is the same as * for gccxml
55 m_strType
.Replace("[]", "*");
58 bool wxType::IsOk() const
60 // NB: m_strType can contain the :: operator; think to e.g. the
61 // "reverse_iterator_impl<wxString::const_iterator>" type
62 // It can also contain commas, * and & operators etc
64 return !GetClean().IsEmpty();
67 wxString
wxType::GetClean() const
69 wxString
ret(m_strType
);
70 ret
.Replace("const", "");
71 ret
.Replace("static", "");
74 ret
.Replace("[]", "");
75 return ret
.Strip(wxString::both
);
78 bool wxType::operator==(const wxType
& m
) const
80 // brain-dead comparison:
82 if (GetClean() == m
.GetClean() &&
83 IsConst() == m
.IsConst() &&
84 IsStatic() == m
.IsStatic() &&
85 IsPointer() == m
.IsPointer() &&
86 IsReference() == m
.IsReference())
92 // ----------------------------------------------------------------------------
94 // ----------------------------------------------------------------------------
96 bool wxMethod::IsOk() const
98 // NOTE: m_retType can be a wxEmptyType, and means that this method
99 // is a ctor or a dtor.
100 if (!m_retType
.IsOk() && m_retType
!=wxEmptyType
) {
101 LogError("'%s' method has invalid return type: %s", m_retType
.GetAsString());
105 if (m_strName
.IsEmpty())
108 // a function can't be both const and static or virtual and static!
109 if ((m_bConst
&& m_bStatic
) || (m_bVirtual
&& m_bStatic
)) {
110 LogError("'%s' method can't be both const/static or virtual/static", m_strName
);
114 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
115 if (!m_args
[i
].IsOk()) {
116 LogError("'%s' method has invalid %d-th argument type: %s",
117 m_strName
, i
, m_args
[i
].GetAsString());
121 // NB: the default value of the arguments can contain pretty much everything
122 // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
123 // so we don't do any test on them.
128 void wxMethod::SetArgumentTypes(const wxTypeArray
& arr
, const wxArrayString
& defaults
)
130 wxASSERT(arr
.GetCount()==defaults
.GetCount());
133 m_argDefaults
=defaults
;
135 // in order to make valid&simple comparison on argument defaults,
136 // we reduce some of the multiple forms in which the same things may appear
138 for (unsigned int i
=0; i
<m_argDefaults
.GetCount(); i
++)
140 m_argDefaults
[i
].Replace("NULL", "0");
141 m_argDefaults
[i
].Replace("0u", "0");
147 void wxMethod::SetFromString(const wxString& proto)
149 m_strProto=proto.Strip(wxString::both);
151 // make sure there is a space separing each token
152 m_strProto.Replace("&", "& ");
153 m_strProto.Replace("*", "* ");
154 m_strProto.Replace(",", ", ");
156 wxASSERT(m_strProto.Contains("::"));
157 wxASSERT(m_strProto.Contains("(") && m_strProto.Contains(")"));
160 wxType wxMethod::GetReturnType() const
162 wxString leftpart = m_strProto.Left(m_strProto.Find("::")).Strip(wxString::both);
163 if (!leftpart.Contains(" "))
164 // this is a dtor or a ctor then...
167 // see SetFromString: all tokens are separed by a space!
168 wxType ret(leftpart.BeforeFirst(' ').Strip(wxString::both));
169 wxASSERT(ret.IsOk());
174 wxString wxMethod::GetName() const
176 int nstart = m_strProto.Find("::")+2,
177 nend = m_strProto.Find("(");
178 wxASSERT(nstart!=wxNOT_FOUND && nend!=wxNOT_FOUND);
180 return m_strProto.Mid(nstart, nend-nstart).Strip(wxString::both);
183 bool wxMethod::IsConst() const
185 return m_strProto.EndsWith("const");
188 bool wxMethod::IsStatic() const
190 return m_strProto.StartsWith("static");
193 wxTypeArray wxMethod::GetArgumentTypes() const
195 int nstart = m_strProto.Find('(', false * start from beginning *)+1,
196 nend = m_strProto.Find(')', true * start from end *);
197 wxASSERT(nstart!=wxNOT_FOUND && nend!=wxNOT_FOUND);
199 wxString argstr = m_strProto.Mid(nstart, nend-nstart).Strip(wxString::both);
200 wxArrayString args = wxSplit(argstr, ',');
203 for (unsigned int i=0; i<args.GetCount(); i++)
205 wxString arg = args[i].Strip(wxString::both);
207 // arg may contain both the type and the argument name;
208 // we need to get rid of the last one...
209 wxArrayString temp = wxSplit(arg, ' ');
211 if (temp.GetCount()>1 &&
212 !temp.Last().Contains("&") &&
213 !temp.Last().Contains("*") &&
215 arg.Replace(temp.Last(), ""); // looks like an argument name - remove it
217 ret.Add(wxType(arg));
223 bool wxMethod::operator==(const wxMethod
& m
) const
225 if (GetReturnType() != m
.GetReturnType() ||
226 GetName() != m
.GetName() ||
227 IsConst() != m
.IsConst() ||
228 IsStatic() != m
.IsStatic() ||
229 IsVirtual() != m
.IsVirtual())
232 if (m_args
.GetCount()!=m
.m_args
.GetCount())
235 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
236 if (m_args
[i
] != m
.m_args
[i
] || m_argDefaults
[i
] != m
.m_argDefaults
[i
])
242 wxString
wxMethod::GetAsString() const
246 if (m_retType
!=wxEmptyType
)
247 ret
+= m_retType
.GetAsString() + " ";
248 //else; this is a ctor or dtor
250 ret
+= m_strName
+ "(";
252 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
254 ret
+= m_args
[i
].GetAsString();
255 if (!m_argDefaults
[i
].IsEmpty())
256 ret
+= " = " + m_argDefaults
[i
];
260 if (m_args
.GetCount()>0)
268 ret
= "static " + ret
;
270 ret
= "virtual " + ret
;
275 void wxMethod::Dump(wxTextOutputStream
& stream
) const
277 stream
<< "[" + m_retType
.GetAsString() + "]";
278 stream
<< "[" + m_strName
+ "]";
280 for (unsigned int i
=0; i
<m_args
.GetCount(); i
++)
281 stream
<< "[" + m_args
[i
].GetAsString() + "=" + m_argDefaults
[i
] + "]";
288 stream
<< " VIRTUAL";
293 // ----------------------------------------------------------------------------
295 // ----------------------------------------------------------------------------
297 wxString
wxClass::GetNameWithoutTemplate() const
299 // NB: I'm not sure this is the right terminology for this function!
301 if (m_strName
.Contains("<"))
302 return m_strName
.Left(m_strName
.Find("<"));
306 bool wxClass::IsValidCtorForThisClass(const wxMethod
& m
) const
308 // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
309 // named wxWritableCharTypeBuffer, without the <...> part!
311 if (m
.IsCtor() && m
.GetName() == GetNameWithoutTemplate())
317 bool wxClass::IsValidDtorForThisClass(const wxMethod
& m
) const
319 if (m
.IsDtor() && m
.GetName() == "~" + GetNameWithoutTemplate())
325 void wxClass::Dump(wxTextOutputStream
& out
) const
327 out
<< m_strName
+ "\n";
329 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++) {
331 // dump all our methods
333 m_methods
[i
].Dump(out
);
340 bool wxClass::CheckConsistency() const
342 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
343 for (unsigned int j
=0; j
<m_methods
.GetCount(); j
++)
344 if (i
!=j
&& m_methods
[i
] == m_methods
[j
])
346 LogError("class %s has two methods with the same prototype: '%s'",
347 m_strName
, m_methods
[i
].GetAsString());
354 const wxMethod
* wxClass::FindMethod(const wxMethod
& m
) const
356 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
357 if (m_methods
[i
] == m
)
358 return &m_methods
[i
];
362 wxMethodPtrArray
wxClass::FindMethodNamed(const wxString
& name
) const
364 wxMethodPtrArray ret
;
366 for (unsigned int i
=0; i
<m_methods
.GetCount(); i
++)
367 if (m_methods
[i
].GetName() == name
)
368 ret
.Add(&m_methods
[i
]);
374 // ----------------------------------------------------------------------------
376 // ----------------------------------------------------------------------------
378 WX_DEFINE_SORTED_ARRAY(wxClass
*, wxSortedClassArray
);
380 int CompareWxClassObjects(wxClass
*item1
, wxClass
*item2
)
382 // sort alphabetically
383 return item1
->GetName().Cmp(item2
->GetName());
386 void wxXmlInterface::Dump(const wxString
& filename
)
388 wxFFileOutputStream
apioutput( filename
);
389 wxTextOutputStream
apiout( apioutput
);
391 // dump the classes in alphabetical order
392 wxSortedClassArray
sorted(CompareWxClassObjects
);
393 sorted
.Alloc(m_classes
.GetCount());
394 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
395 sorted
.Add(&m_classes
[i
]);
397 // now they have been sorted
398 for (unsigned int i
=0; i
<sorted
.GetCount(); i
++)
399 sorted
[i
]->Dump(apiout
);
402 bool wxXmlInterface::CheckParseResults() const
404 // this check can be quite slow, so do it only for debug releases:
406 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
407 if (!m_classes
[i
].CheckConsistency())
414 // ----------------------------------------------------------------------------
416 // ----------------------------------------------------------------------------
418 #define ATTRIB_CONST 1
419 #define ATTRIB_REFERENCE 2
420 #define ATTRIB_POINTER 4
421 #define ATTRIB_ARRAY 8
423 class toResolveTypeItem
426 toResolveTypeItem() { attribs
=0; }
427 toResolveTypeItem(const wxString
& namestr
, int attribint
)
428 : ref(namestr
), attribs(attribint
) {}
434 //WX_DECLARE_STRING_HASH_MAP( toResolveTypeItem, wxToResolveTypeHashMap );
436 typedef std::map
<wxString
, toResolveTypeItem
> wxToResolveTypeHashMap
;
438 bool wxXmlGccInterface::Parse(const wxString
& filename
)
444 LogMessage("Parsing %s...", filename
);
446 if (!doc
.Load(filename
)) {
447 LogError("can't load %s", filename
);
451 // start processing the XML file
452 if (doc
.GetRoot()->GetName() != "GCC_XML") {
453 LogError("invalid root node for %s", filename
);
457 wxToResolveTypeHashMap toResolveTypes
;
458 wxArrayString arrMemberIds
;
459 wxStringHashMap types
;
460 wxStringHashMap files
;
462 // prealloc quite a lot of memory!
463 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
464 arrMemberIds
.Alloc(ESTIMATED_NUM_TYPES
);
467 // do a quick parsing of the <File> nodes; we take advantage of the fact that
468 // gccxml puts all of them in a contiguos block at the end of the file it produces.
469 child
= doc
.GetRoot()->GetChildren();
470 while (child
&& child
->GetName() != "File")
471 child
= child
->GetNext(); // skip everything until first <File> node
473 // here starts the <File> node block
476 wxString id
= child
->GetAttribute("id", "");
477 if (!id
.StartsWith("f")) {
478 LogError("Unexpected file ID: %s", id
);
483 fileNames
.Add(child
->GetAttribute("name", ""));
485 child
= child
->GetNext();
490 wxString allWxClassesIds
, allWxMethodsIds
, allWxReferencesIds
;
491 allWxClassesIds
.Alloc(240000);
492 allWxMethodsIds
.Alloc(240000);
493 allWxReferencesIds
.Alloc(240000);
494 child
= doc
.GetRoot()->GetChildren();
497 if (child
->GetName() == "Class")
498 if (child
->GetAttribute("name", wxEmptyString
).StartsWith("wx"))
499 allWxClassesIds
+= " " + child
->GetAttribute("members", wxEmptyString
);
500 child
= child
->GetNext();
503 child
= doc
.GetRoot()->GetChildren();
506 wxString n
= child
->GetName();
507 if ((n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod") &&
508 child
->GetAttribute("access", wxEmptyString
) == "public")
510 if (allWxClassesIds
.Contains(child
->GetAttribute("id", wxEmptyString
)))
512 allWxMethodsIds
+= " " + child
->GetAttribute("returns", wxEmptyString
);
514 wxXmlNode
*arg
= child
->GetChildren();
517 if (arg
->GetName() == "Argument")
518 allWxMethodsIds
+= " " + arg
->GetAttribute("type", wxEmptyString
);
520 arg
= arg
->GetNext();
525 child
= child
->GetNext();
528 child
= doc
.GetRoot()->GetChildren();
531 if (allWxMethodsIds
.Contains(child
->GetAttribute("id", wxEmptyString
)))
533 const wxString
& type
= child
->GetAttribute("type", wxEmptyString
);
534 allWxReferencesIds
+= " " + type
;
537 child
= child
->GetNext();
540 child
= doc
.GetRoot()->GetChildren();
543 if (allWxReferencesIds
.Contains(child
->GetAttribute("id", wxEmptyString
)))
545 const wxString
& type
= child
->GetAttribute("type", wxEmptyString
);
546 allWxReferencesIds
+= " " + type
;
549 child
= child
->GetNext();
552 LogMessage("adfafdasfdas %d-%d-%d", allWxClassesIds
.Len(),
553 allWxMethodsIds
.Len(), allWxReferencesIds
.Len());
557 // build a list of wx classes and in general of all existent types
558 child
= doc
.GetRoot()->GetChildren();
561 const wxString
& n
= child
->GetName();
562 const wxString
& id
= child
->GetAttribute("id", wxEmptyString
);
565 if (!allWxClassesIds
.Contains(id
) && !allWxMethodsIds
.Contains(id
) && !allWxReferencesIds
.Contains(id
))
566 ; // ignore this node
570 wxString fid
= child
->GetAttribute("file", "");
571 int fileidx
= wxNOT_FOUND
;
573 if (!fid
.IsEmpty()) {
574 fileidx
= fileIds
.Index(fid
);
575 if (fileidx
== wxNOT_FOUND
) {
576 LogError("couldn't find the file ID '%s'", fid
);
581 if (fileidx
!=wxNOT_FOUND
&& !fileNames
[fileidx
].Contains("wx"))
583 // skip this node: not wx related!
587 /*if (child->GetAttribute("artificial", "") == "1")
589 // discard this immediately - we're not interested
594 wxString cname
= child
->GetAttribute("name", wxEmptyString
);
595 if (cname
.IsEmpty()) {
596 LogError("Invalid empty name for '%s' node", n
);
600 // only register wx classes (do remember also the IDs of their members)
601 if (cname
.StartsWith("wx")) {
602 arrMemberIds
.Add(child
->GetAttribute("members", wxEmptyString
));
604 // NB: "file" attribute contains an ID value that we'll resolve later
605 m_classes
.Add(wxClass(cname
, child
->GetAttribute("file", wxEmptyString
)));
608 // register this class also as possible return/argument type:
609 //typeIds.Add(child->GetAttribute("id", wxEmptyString));
610 //typeNames.Add(cname);
613 else if (n
== "PointerType" || n
== "ReferenceType" ||
614 n
== "CvQualifiedType" || n
== "ArrayType")
616 const wxString
& type
= child
->GetAttribute("type", wxEmptyString
);
617 if (id
.IsEmpty() || type
.IsEmpty()) {
618 LogError("Invalid empty type/id for '%s' node", n
);
623 if (n
== "PointerType")
624 attr
= ATTRIB_POINTER
;
625 else if (n
== "ReferenceType")
626 attr
= ATTRIB_REFERENCE
;
627 else if (n
== "CvQualifiedType" && child
->GetAttribute("const", "") == "1")
629 else if (n
== "ArrayType")
632 // these nodes make reference to other types... we'll resolve them later
633 //toResolveTypeIds.Add(id);
634 //toResolveRefType.Add(type);
635 //toResolveAttrib.Add(attr);
636 toResolveTypes
[id
] = toResolveTypeItem(type
, attr
);
638 else if (n
== "FunctionType" || n
== "MethodType")
640 /* TODO: incomplete */
642 const wxString
& ret
= child
->GetAttribute("returns", wxEmptyString
);
643 if (id
.IsEmpty() || ret
.IsEmpty()) {
644 LogError("Invalid empty ret/id for '%s' node", n
);
648 // these nodes make reference to other types... we'll resolve them later
649 toResolveTypes
[id
] = toResolveTypeItem(ret
, 0);
651 else if (n
== "File")
653 if (!id
.StartsWith("f")) {
654 LogError("Unexpected file ID: %s", id
);
658 // just ignore this node... all file IDs/names were already parsed
659 files
[id
] = child
->GetAttribute("name", "");
663 // we register everything else as a possible return/argument type:
664 const wxString
& name
= child
->GetAttribute("name", wxEmptyString
);
665 /*if (id.IsEmpty() || name.IsEmpty()) {
666 LogError("Invalid empty name/id for '%s' node", n);
673 //typeNames.Add(name);
678 // this may happen with unnamed structs/union, special ctors,
679 // or other exotic things which we are not interested to, since
680 // they're never used as return/argument types by wxWidgets methods
683 LogWarning("Type '%s' with ID '%s' does not have name attribute", n
, id
);
686 //typeNames.Add("TOFIX");
691 child
= child
->GetNext();
693 // give feedback to the user about the progress...
694 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
697 // some nodes with IDs referenced by methods as return/argument types, do reference
698 // in turn o ther nodes (see PointerType, ReferenceType and CvQualifierType above);
699 // thus we need to resolve their name iteratively:
700 while (toResolveTypes
.size()>0)
703 LogMessage("%d types were collected; %d types need yet to be resolved...",
704 types
.size(), toResolveTypes
.size());
706 for (wxToResolveTypeHashMap::iterator i
= toResolveTypes
.begin();
707 i
!= toResolveTypes
.end();)
709 const wxString
& id
= i
->first
;
710 const wxString
& referenced
= i
->second
.ref
;
712 wxStringHashMap::iterator primary
= types
.find(referenced
);
713 if (primary
!= types
.end())
715 // this to-resolve-type references a "primary" type
718 int attribs
= i
->second
.attribs
;
720 if (attribs
& ATTRIB_CONST
)
721 newtype
= "const " + primary
->second
;
722 if (attribs
& ATTRIB_REFERENCE
)
723 newtype
= primary
->second
+ "&";
724 if (attribs
& ATTRIB_POINTER
)
725 newtype
= primary
->second
+ "*";
726 if (attribs
& ATTRIB_ARRAY
)
727 newtype
= primary
->second
+ "[]";
729 // add the resolved type to the list of "primary" types
732 // this one has been resolved; erase it through its iterator!
733 toResolveTypes
.erase(i
);
735 // now iterator i is invalid; assign it again to the beginning
736 i
= toResolveTypes
.begin();
740 // then search in the referenced types themselves:
741 wxToResolveTypeHashMap::iterator idx2
= toResolveTypes
.find(referenced
);
742 if (idx2
!= toResolveTypes
.end())
744 // merge this to-resolve-type with the idx2->second type
745 i
->second
.ref
= idx2
->second
.ref
;
746 i
->second
.attribs
|= idx2
->second
.attribs
;
748 // this type will eventually be solved in the next while() iteration
754 LogError("Cannot solve '%s' reference type!", referenced
);
757 typeIds
.Add(toResolveTypeIds
[i
]);
758 typeNames
.Add("TOFIX");
760 // this one has been resolved!
761 toResolveTypeIds
.RemoveAt(i
);
762 toResolveRefType
.RemoveAt(i
);
763 toResolveAttrib
.RemoveAt(i
);
770 // now get the return types of all wx methods parsed above
771 child = doc.GetRoot()->GetChildren();
775 wxString n = child->GetName();
777 if (n == "PointerType" || n == "ReferenceType" || n == "CvQualifiedType") {
778 wxString id = child->GetAttribute("id", wxEmptyString);
779 wxString type = child->GetAttribute("type", wxEmptyString);
781 // are we interested to this id?
782 while ((idx = retTypeIds.Index(id)) != wxNOT_FOUND)
784 // substitute this ID with the ID referenced by this node
785 retTypeIds[idx] = type;
786 // leave "incompleteMethods" array untouched
789 wxString id = child->GetAttribute("id", wxEmptyString);
790 wxString name = child->GetAttribute("name", wxEmptyString);
793 // are we interested to this id?
794 while ((idx = retTypeIds.Index(id)) != wxNOT_FOUND)
796 wxMethod *p = (wxMethod*)incompleteMethods[idx];
797 p->SetFromString(name + " " + p->GetAsString());
799 //LogMessage("rettype node is named %s and is '%s'", n,
800 // child->GetAttribute("name", ""));
802 // prototype is now complete; remove it from the list of protos
803 // waiting for completion
804 retTypeIds.RemoveAt(idx);
805 incompleteMethods.RemoveAt(idx);
810 child = child->GetNext();
812 // give feedback to the user about the progress...
813 if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
817 // resolve header names
818 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
820 wxStringHashMap::const_iterator idx
= files
.find(m_classes
[i
].GetHeader());
821 if (idx
== files
.end())
824 LogError("couldn't find file ID '%s'", m_classes
[i
].GetHeader());
827 m_classes
[i
].SetHeader(idx
->second
);
830 // build the list of the wx methods
831 child
= doc
.GetRoot()->GetChildren();
834 wxString n
= child
->GetName();
836 if (n
== "Method" || n
== "Constructor" || n
== "Destructor" || n
== "OperatorMethod")
838 wxString id
= child
->GetAttribute("id", wxEmptyString
);
840 // only register public methods
841 if (child
->GetAttribute("access", wxEmptyString
) == "public")
843 wxASSERT(arrMemberIds
.GetCount()==m_classes
.GetCount());
845 for (unsigned int i
=0; i
<m_classes
.GetCount(); i
++)
847 if (arrMemberIds
[i
].Contains(id
))
849 // this <Method> node is a method of the i-th class!
851 if (!ParseMethod(child
, types
, newfunc
))
854 if (newfunc
.IsCtor() && !m_classes
[i
].IsValidCtorForThisClass(newfunc
)) {
855 LogError("The method '%s' does not seem to be a ctor for '%s'",
856 newfunc
.GetName(), m_classes
[i
].GetName());
859 if (newfunc
.IsDtor() && !m_classes
[i
].IsValidDtorForThisClass(newfunc
)) {
860 LogError("The method '%s' does not seem to be a dtor for '%s'",
861 newfunc
.GetName(), m_classes
[i
].GetName());
865 m_classes
[i
].AddMethod(newfunc
);
871 child
= child
->GetNext();
873 // give feedback to the user about the progress...
874 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
878 if (!CheckParseResults())
884 bool wxXmlGccInterface::ParseMethod(const wxXmlNode
*p
,
885 const wxStringHashMap
& types
,
889 wxString name
= p
->GetAttribute("name", wxEmptyString
).Strip(wxString::both
);
890 if (p
->GetName() == "Destructor")
892 else if (p
->GetName() == "OperatorMethod")
893 name
= "operator" + name
;
895 // resolve return type
897 wxString retid
= p
->GetAttribute("returns", wxEmptyString
);
900 if (p
->GetName() != "Destructor" && p
->GetName() != "Constructor") {
901 LogError("Empty return ID for method '%s', with ID '%s'",
902 name
, p
->GetAttribute("id", ""));
908 wxStringHashMap::const_iterator retidx
= types
.find(retid
);
909 if (retidx
== types
.end()) {
910 LogError("Could not find return type ID '%s'", retid
);
914 ret
= wxType(retidx
->second
);
916 LogError("Invalid return type '%s' for method '%s', with ID '%s'",
917 retidx
->second
, name
, p
->GetAttribute("id", ""));
922 // resolve argument types
923 wxTypeArray argtypes
;
924 wxArrayString argdefs
;
925 wxXmlNode
*arg
= p
->GetChildren();
928 if (arg
->GetName() == "Argument")
930 wxString id
= arg
->GetAttribute("type", wxEmptyString
);
931 wxStringHashMap::const_iterator idx
= types
.find(id
);
932 if (idx
== types
.end()) {
933 LogError("Could not find argument type ID '%s'", id
);
937 argtypes
.Add(wxType(idx
->second
));
939 wxString def
= arg
->GetAttribute("default", "");
940 if (def
.Contains("wxGetTranslation"))
941 argdefs
.Add(wxEmptyString
); // TODO: wxGetTranslation gives problems to gccxml
946 arg
= arg
->GetNext();
949 m
.SetReturnType(ret
);
951 m
.SetArgumentTypes(argtypes
, argdefs
);
952 m
.SetConst(p
->GetAttribute("const", "") == "1");
953 m
.SetStatic(p
->GetAttribute("static", "") == "1");
954 m
.SetVirtual(p
->GetAttribute("virtual", "") == "1");
957 LogError("The prototype '%s' is not valid!", m
.GetAsString());
965 // ----------------------------------------------------------------------------
966 // wxXmlDoxygenInterface
967 // ----------------------------------------------------------------------------
969 bool wxXmlDoxygenInterface::Parse(const wxString
& filename
)
974 LogMessage("Parsing %s...", filename
);
976 if (!index
.Load(filename
)) {
977 LogError("can't load %s", filename
);
981 // start processing the index:
982 if (index
.GetRoot()->GetName() != "doxygenindex") {
983 LogError("invalid root node for %s", filename
);
987 m_classes
.Alloc(ESTIMATED_NUM_CLASSES
);
989 // process files referenced by this index file
990 compound
= index
.GetRoot()->GetChildren();
993 if (compound
->GetName() == "compound" &&
994 compound
->GetAttribute("kind", "") == "class")
996 wxString refid
= compound
->GetAttribute("refid", "");
998 wxFileName
fn(filename
);
999 if (!ParseCompoundDefinition(fn
.GetPath(wxPATH_GET_SEPARATOR
) + refid
+ ".xml"))
1003 compound
= compound
->GetNext();
1007 if (!CheckParseResults())
1013 bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString
& filename
)
1020 LogMessage("Parsing %s...", filename
);
1022 if (!doc
.Load(filename
)) {
1023 LogError("can't load %s", filename
);
1027 // start processing this compound definition XML
1028 if (doc
.GetRoot()->GetName() != "doxygen") {
1029 LogError("invalid root node for %s", filename
);
1033 // build a list of wx classes
1034 child
= doc
.GetRoot()->GetChildren();
1037 if (child
->GetName() == "compounddef" &&
1038 child
->GetAttribute("kind", wxEmptyString
) == "class")
1042 wxString absoluteFile
, header
;
1044 wxXmlNode
*subchild
= child
->GetChildren();
1047 if (subchild
->GetName() == "sectiondef" &&
1048 subchild
->GetAttribute("kind", wxEmptyString
) == "public-func")
1051 wxXmlNode
*membernode
= subchild
->GetChildren();
1054 if (membernode
->GetName() == "memberdef" &&
1055 membernode
->GetAttribute("kind", wxEmptyString
) == "function")
1059 if (ParseMethod(membernode
, m
, header
))
1061 if (absoluteFile
.IsEmpty())
1062 absoluteFile
= header
;
1063 else if (header
!= absoluteFile
)
1065 LogError("The method '%s' is documented in a different "
1066 "file from others (which belong to '%s') ?",
1067 header
, absoluteFile
);
1075 membernode
= membernode
->GetNext();
1078 // all methods of this class were taken from the header "absoluteFile":
1079 klass
.SetHeader(absoluteFile
);
1081 else if (subchild
->GetName() == "compoundname")
1083 klass
.SetName(subchild
->GetNodeContent());
1085 /*else if (subchild->GetName() == "includes")
1087 // NOTE: we'll get the header from the <location> tags
1088 // scattered inside <memberdef> tags instead of
1089 // this <includes> tag since it does not contain
1090 // the absolute path of the header
1092 klass.SetHeader(subchild->GetNodeContent());
1095 subchild
= subchild
->GetNext();
1100 m_classes
.Add(klass
);
1102 LogWarning("discarding class '%s' with %d methods...",
1103 klass
.GetName(), klass
.GetMethodCount());
1106 child
= child
->GetNext();
1108 // give feedback to the user about the progress...
1109 if ((++nodes%PROGRESS_RATE
)==0) ShowProgress();
1115 static wxString
GetTextFromChildren(const wxXmlNode
*n
)
1119 // consider the tree
1121 // <a><b>this</b> is a <b>string</b></a>
1130 // unlike wxXmlNode::GetNodeContent() which would return " is a "
1131 // this function returns "this is a string"
1133 wxXmlNode
*ref
= n
->GetChildren();
1135 if (ref
->GetType() == wxXML_ELEMENT_NODE
)
1136 text
+= ref
->GetNodeContent();
1137 else if (ref
->GetType() == wxXML_TEXT_NODE
)
1138 text
+= ref
->GetContent();
1140 LogWarning("Unexpected node type while getting text from '%s' node", n
->GetName());
1142 ref
= ref
->GetNext();
1148 bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode
* p
, wxMethod
& m
, wxString
& header
)
1154 wxXmlNode
*child
= p
->GetChildren();
1157 if (child
->GetName() == "name")
1158 m
.SetName(child
->GetNodeContent());
1159 else if (child
->GetName() == "type")
1160 m
.SetReturnType(wxType(GetTextFromChildren(child
)));
1161 else if (child
->GetName() == "param")
1163 wxString typestr
, defstr
, arrstr
;
1164 wxXmlNode
*n
= child
->GetChildren();
1167 if (n
->GetName() == "type")
1168 // if the <type> node has children, they should be all TEXT and <ref> nodes
1169 // and we need to take the text they contain, in the order they appear
1170 typestr
= GetTextFromChildren(n
);
1171 else if (n
->GetName() == "defval")
1172 // same for the <defval> node
1173 defstr
= GetTextFromChildren(n
);
1174 else if (n
->GetName() == "array")
1175 arrstr
= GetTextFromChildren(n
);
1180 if (typestr
.IsEmpty()) {
1181 LogError("cannot find type node for a param in method '%s'", m
.GetName());
1185 args
.Add(wxType(typestr
+ arrstr
));
1188 else if (child
->GetName() == "location")
1190 if (child
->GetAttribute("line", "").ToLong(&line
))
1191 m
.SetLocation((int)line
);
1192 header
= child
->GetAttribute("file", "");
1195 child
= child
->GetNext();
1198 m
.SetArgumentTypes(args
, defs
);
1199 m
.SetConst(p
->GetAttribute("const", "")=="yes");
1200 m
.SetStatic(p
->GetAttribute("static", "")=="yes");
1201 m
.SetVirtual(p
->GetAttribute("virt", "")=="virtual");
1204 LogError("The prototype '%s' is not valid!", m
.GetAsString());