]>
Commit | Line | Data |
---|---|---|
5934cda1 FM |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: xmlparser.cpp | |
3 | // Purpose: Parser of the API/interface XML files | |
4 | // Author: Francesco Montorsi | |
5 | // Created: 2008/03/17 | |
6 | // RCS-ID: $Id$ | |
7 | // Copyright: (c) 2008 Francesco Montorsi | |
8 | // Licence: wxWindows licence | |
9 | ///////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | // For compilers that support precompilation, includes "wx/wx.h". | |
12 | #include "wx/wxprec.h" | |
13 | ||
14 | #ifdef __BORLANDC__ | |
15 | #pragma hdrstop | |
16 | #endif | |
17 | ||
18 | // for all others, include the necessary headers | |
19 | #ifndef WX_PRECOMP | |
20 | #endif | |
21 | ||
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" | |
fdd4a897 | 27 | |
74bda203 FM |
28 | #include <errno.h> |
29 | ||
5934cda1 FM |
30 | #include "xmlparser.h" |
31 | ||
32 | #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot | |
33 | #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem | |
34 | #define ESTIMATED_NUM_TYPES 50000 // used only by wxGccXmlInterface to prealloc mem | |
35 | #define ESTIMATED_NUM_FILES 800 // used only by wxGccXmlInterface to prealloc mem | |
36 | ||
37 | WX_DEFINE_OBJARRAY(wxTypeArray) | |
38 | WX_DEFINE_OBJARRAY(wxMethodArray) | |
39 | WX_DEFINE_OBJARRAY(wxClassArray) | |
40 | ||
41 | ||
42 | // declared in ifacecheck.cpp | |
43 | extern bool g_verbose; | |
44 | ||
45 | ||
46 | ||
47 | // ---------------------------------------------------------------------------- | |
48 | // wxType | |
49 | // ---------------------------------------------------------------------------- | |
50 | ||
51 | wxType wxEmptyType; | |
52 | ||
53 | void wxType::SetFromString(const wxString& t) | |
54 | { | |
55 | m_strType = t.Strip(wxString::both); | |
56 | ||
57 | // [] is the same as * for gccxml | |
58 | m_strType.Replace("[]", "*"); | |
59 | } | |
60 | ||
61 | bool wxType::IsOk() const | |
62 | { | |
63 | // NB: m_strType can contain the :: operator; think to e.g. the | |
64 | // "reverse_iterator_impl<wxString::const_iterator>" type | |
65 | // It can also contain commas, * and & operators etc | |
66 | ||
67 | return !GetClean().IsEmpty(); | |
68 | } | |
69 | ||
70 | wxString wxType::GetClean() const | |
71 | { | |
72 | wxString ret(m_strType); | |
73 | ret.Replace("const", ""); | |
74 | ret.Replace("static", ""); | |
75 | ret.Replace("*", ""); | |
76 | ret.Replace("&", ""); | |
77 | ret.Replace("[]", ""); | |
78 | return ret.Strip(wxString::both); | |
79 | } | |
80 | ||
81 | bool wxType::operator==(const wxType& m) const | |
82 | { | |
83 | // brain-dead comparison: | |
84 | ||
85 | if (GetClean() == m.GetClean() && | |
86 | IsConst() == m.IsConst() && | |
87 | IsStatic() == m.IsStatic() && | |
88 | IsPointer() == m.IsPointer() && | |
89 | IsReference() == m.IsReference()) | |
90 | return true; | |
91 | ||
92 | return false; | |
93 | } | |
94 | ||
95 | // ---------------------------------------------------------------------------- | |
96 | // wxMethod | |
97 | // ---------------------------------------------------------------------------- | |
98 | ||
99 | bool wxMethod::IsOk() const | |
100 | { | |
101 | // NOTE: m_retType can be a wxEmptyType, and means that this method | |
102 | // is a ctor or a dtor. | |
103 | if (!m_retType.IsOk() && m_retType!=wxEmptyType) { | |
104 | LogError("'%s' method has invalid return type: %s", m_retType.GetAsString()); | |
105 | return false; | |
106 | } | |
107 | ||
108 | if (m_strName.IsEmpty()) | |
109 | return false; | |
110 | ||
111 | // a function can't be both const and static or virtual and static! | |
112 | if ((m_bConst && m_bStatic) || (m_bVirtual && m_bStatic)) { | |
113 | LogError("'%s' method can't be both const/static or virtual/static", m_strName); | |
114 | return false; | |
115 | } | |
116 | ||
117 | for (unsigned int i=0; i<m_args.GetCount(); i++) | |
118 | if (!m_args[i].IsOk()) { | |
119 | LogError("'%s' method has invalid %d-th argument type: %s", | |
120 | m_strName, i, m_args[i].GetAsString()); | |
121 | return false; | |
122 | } | |
123 | ||
124 | // NB: the default value of the arguments can contain pretty much everything | |
125 | // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>) | |
126 | // so we don't do any test on them. | |
127 | ||
128 | return true; | |
129 | } | |
130 | ||
131 | void wxMethod::SetArgumentTypes(const wxTypeArray& arr, const wxArrayString& defaults) | |
132 | { | |
133 | wxASSERT(arr.GetCount()==defaults.GetCount()); | |
134 | ||
135 | m_args=arr; | |
136 | m_argDefaults=defaults; | |
137 | ||
138 | // in order to make valid&simple comparison on argument defaults, | |
139 | // we reduce some of the multiple forms in which the same things may appear | |
140 | // to a single form | |
141 | for (unsigned int i=0; i<m_argDefaults.GetCount(); i++) | |
142 | { | |
143 | m_argDefaults[i].Replace("NULL", "0"); | |
144 | m_argDefaults[i].Replace("0u", "0"); | |
145 | } | |
146 | } | |
147 | ||
5934cda1 FM |
148 | bool wxMethod::operator==(const wxMethod& m) const |
149 | { | |
150 | if (GetReturnType() != m.GetReturnType() || | |
151 | GetName() != m.GetName() || | |
152 | IsConst() != m.IsConst() || | |
153 | IsStatic() != m.IsStatic() || | |
154 | IsVirtual() != m.IsVirtual()) | |
155 | return false; | |
156 | ||
157 | if (m_args.GetCount()!=m.m_args.GetCount()) | |
158 | return false; | |
159 | ||
160 | for (unsigned int i=0; i<m_args.GetCount(); i++) | |
161 | if (m_args[i] != m.m_args[i] || m_argDefaults[i] != m.m_argDefaults[i]) | |
162 | return false; | |
163 | ||
164 | return true; | |
165 | } | |
166 | ||
167 | wxString wxMethod::GetAsString() const | |
168 | { | |
169 | wxString ret; | |
170 | ||
171 | if (m_retType!=wxEmptyType) | |
172 | ret += m_retType.GetAsString() + " "; | |
173 | //else; this is a ctor or dtor | |
174 | ||
175 | ret += m_strName + "("; | |
176 | ||
177 | for (unsigned int i=0; i<m_args.GetCount(); i++) | |
178 | { | |
179 | ret += m_args[i].GetAsString(); | |
180 | if (!m_argDefaults[i].IsEmpty()) | |
181 | ret += " = " + m_argDefaults[i]; | |
182 | ret += ","; | |
183 | } | |
184 | ||
185 | if (m_args.GetCount()>0) | |
186 | ret.RemoveLast(); | |
187 | ||
188 | ret += ")"; | |
189 | ||
190 | if (m_bConst) | |
191 | ret += " const"; | |
192 | if (m_bStatic) | |
193 | ret = "static " + ret; | |
194 | if (m_bVirtual) | |
195 | ret = "virtual " + ret; | |
196 | ||
197 | return ret; | |
198 | } | |
199 | ||
200 | void wxMethod::Dump(wxTextOutputStream& stream) const | |
201 | { | |
202 | stream << "[" + m_retType.GetAsString() + "]"; | |
203 | stream << "[" + m_strName + "]"; | |
204 | ||
205 | for (unsigned int i=0; i<m_args.GetCount(); i++) | |
206 | stream << "[" + m_args[i].GetAsString() + "=" + m_argDefaults[i] + "]"; | |
207 | ||
208 | if (IsConst()) | |
209 | stream << " CONST"; | |
210 | if (IsStatic()) | |
211 | stream << " STATIC"; | |
212 | if (IsVirtual()) | |
213 | stream << " VIRTUAL"; | |
214 | ||
215 | // no final newline | |
216 | } | |
217 | ||
218 | // ---------------------------------------------------------------------------- | |
219 | // wxClass | |
220 | // ---------------------------------------------------------------------------- | |
221 | ||
222 | wxString wxClass::GetNameWithoutTemplate() const | |
223 | { | |
224 | // NB: I'm not sure this is the right terminology for this function! | |
225 | ||
226 | if (m_strName.Contains("<")) | |
227 | return m_strName.Left(m_strName.Find("<")); | |
228 | return m_strName; | |
229 | } | |
230 | ||
231 | bool wxClass::IsValidCtorForThisClass(const wxMethod& m) const | |
232 | { | |
233 | // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is | |
234 | // named wxWritableCharTypeBuffer, without the <...> part! | |
235 | ||
236 | if (m.IsCtor() && m.GetName() == GetNameWithoutTemplate()) | |
237 | return true; | |
238 | ||
239 | return false; | |
240 | } | |
241 | ||
242 | bool wxClass::IsValidDtorForThisClass(const wxMethod& m) const | |
243 | { | |
244 | if (m.IsDtor() && m.GetName() == "~" + GetNameWithoutTemplate()) | |
245 | return true; | |
246 | ||
247 | return false; | |
248 | } | |
249 | ||
250 | void wxClass::Dump(wxTextOutputStream& out) const | |
251 | { | |
252 | out << m_strName + "\n"; | |
253 | ||
254 | for (unsigned int i=0; i<m_methods.GetCount(); i++) { | |
255 | ||
256 | // dump all our methods | |
257 | out << "|- "; | |
258 | m_methods[i].Dump(out); | |
259 | out << "\n"; | |
260 | } | |
261 | ||
262 | out << "\n"; | |
263 | } | |
264 | ||
265 | bool wxClass::CheckConsistency() const | |
266 | { | |
267 | for (unsigned int i=0; i<m_methods.GetCount(); i++) | |
268 | for (unsigned int j=0; j<m_methods.GetCount(); j++) | |
269 | if (i!=j && m_methods[i] == m_methods[j]) | |
270 | { | |
271 | LogError("class %s has two methods with the same prototype: '%s'", | |
272 | m_strName, m_methods[i].GetAsString()); | |
273 | return false; | |
274 | } | |
275 | ||
276 | return true; | |
277 | } | |
278 | ||
279 | const wxMethod* wxClass::FindMethod(const wxMethod& m) const | |
280 | { | |
281 | for (unsigned int i=0; i<m_methods.GetCount(); i++) | |
282 | if (m_methods[i] == m) | |
283 | return &m_methods[i]; | |
284 | return NULL; | |
285 | } | |
286 | ||
287 | wxMethodPtrArray wxClass::FindMethodNamed(const wxString& name) const | |
288 | { | |
289 | wxMethodPtrArray ret; | |
290 | ||
291 | for (unsigned int i=0; i<m_methods.GetCount(); i++) | |
292 | if (m_methods[i].GetName() == name) | |
293 | ret.Add(&m_methods[i]); | |
294 | ||
295 | return ret; | |
296 | } | |
297 | ||
298 | ||
299 | // ---------------------------------------------------------------------------- | |
300 | // wxXmlInterface | |
301 | // ---------------------------------------------------------------------------- | |
302 | ||
303 | WX_DEFINE_SORTED_ARRAY(wxClass*, wxSortedClassArray); | |
304 | ||
305 | int CompareWxClassObjects(wxClass *item1, wxClass *item2) | |
306 | { | |
307 | // sort alphabetically | |
308 | return item1->GetName().Cmp(item2->GetName()); | |
309 | } | |
310 | ||
311 | void wxXmlInterface::Dump(const wxString& filename) | |
312 | { | |
313 | wxFFileOutputStream apioutput( filename ); | |
314 | wxTextOutputStream apiout( apioutput ); | |
315 | ||
316 | // dump the classes in alphabetical order | |
317 | wxSortedClassArray sorted(CompareWxClassObjects); | |
318 | sorted.Alloc(m_classes.GetCount()); | |
319 | for (unsigned int i=0; i<m_classes.GetCount(); i++) | |
320 | sorted.Add(&m_classes[i]); | |
321 | ||
322 | // now they have been sorted | |
323 | for (unsigned int i=0; i<sorted.GetCount(); i++) | |
324 | sorted[i]->Dump(apiout); | |
325 | } | |
326 | ||
327 | bool wxXmlInterface::CheckParseResults() const | |
328 | { | |
329 | // this check can be quite slow, so do it only for debug releases: | |
330 | #ifdef __WXDEBUG__ | |
331 | for (unsigned int i=0; i<m_classes.GetCount(); i++) | |
332 | if (!m_classes[i].CheckConsistency()) | |
333 | return false; | |
334 | #endif | |
335 | ||
336 | return true; | |
337 | } | |
338 | ||
339 | // ---------------------------------------------------------------------------- | |
74bda203 | 340 | // wxXmlGccInterface helper declarations |
5934cda1 FM |
341 | // ---------------------------------------------------------------------------- |
342 | ||
343 | #define ATTRIB_CONST 1 | |
344 | #define ATTRIB_REFERENCE 2 | |
345 | #define ATTRIB_POINTER 4 | |
346 | #define ATTRIB_ARRAY 8 | |
347 | ||
fdd4a897 FM |
348 | #define GCCXML_BASE 35 |
349 | ||
5934cda1 FM |
350 | class toResolveTypeItem |
351 | { | |
352 | public: | |
353 | toResolveTypeItem() { attribs=0; } | |
fdd4a897 FM |
354 | toResolveTypeItem(unsigned int refID, unsigned int attribint) |
355 | : ref(refID), attribs(attribint) {} | |
5934cda1 | 356 | |
fdd4a897 | 357 | unsigned long ref, attribs; |
5934cda1 FM |
358 | }; |
359 | ||
5ceba28d | 360 | #if 1 |
74bda203 FM |
361 | |
362 | // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem | |
fdd4a897 FM |
363 | WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem, |
364 | wxIntegerHash, wxIntegerEqual, | |
365 | wxToResolveTypeHashMap ); | |
74bda203 FM |
366 | |
367 | // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID | |
368 | WX_DECLARE_HASH_MAP( unsigned long, wxClass*, | |
369 | wxIntegerHash, wxIntegerEqual, | |
370 | wxClassMemberIdHashMap ); | |
5ceba28d | 371 | #else |
5934cda1 | 372 | #include <map> |
fdd4a897 | 373 | typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap; |
5ceba28d | 374 | #endif |
5934cda1 | 375 | |
74bda203 FM |
376 | |
377 | // utility to parse gccXML ID values; | |
378 | // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE) | |
379 | // but is a little bit faster | |
380 | bool getID(unsigned long *id, const wxStringCharType* str) | |
381 | { | |
382 | wxStringCharType *end; | |
383 | #if wxUSE_UNICODE_UTF8 | |
384 | unsigned long val = strtoul(str+1, &end, GCCXML_BASE); | |
385 | #else | |
386 | unsigned long val = wcstoul(str+1, &end, GCCXML_BASE); | |
387 | #endif | |
388 | ||
389 | // return true only if scan was stopped by the terminating NUL and | |
390 | // if the string was not empty to start with and no under/overflow | |
391 | // occurred: | |
392 | if ( *end != '\0' || end == str+1 || errno == ERANGE || errno == EINVAL ) | |
393 | return false; | |
394 | ||
395 | *id = val; | |
396 | return true; | |
397 | } | |
398 | ||
399 | // utility specialized to parse efficiently the gccXML list of IDs which occur | |
400 | // in nodes like <Class> ones... i.e. numeric values separed by " _" token | |
401 | bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxStringCharType* str) | |
402 | { | |
403 | #if wxUSE_UNICODE_UTF8 | |
404 | size_t len = strlen(str); | |
405 | #else | |
406 | size_t len = wcslen(str); | |
407 | #endif | |
408 | ||
409 | if (len == 0 || str[0] != '_') | |
410 | return false; | |
411 | ||
412 | const wxStringCharType *curpos = str, | |
413 | *end = str + len; | |
414 | wxStringCharType *nexttoken; | |
415 | ||
416 | while (curpos < end) | |
417 | { | |
418 | // curpos always points to the underscore of the next token to parse: | |
419 | #if wxUSE_UNICODE_UTF8 | |
420 | unsigned long id = strtoul(curpos+1, &nexttoken, GCCXML_BASE); | |
421 | #else | |
422 | unsigned long id = wcstoul(curpos+1, &nexttoken, GCCXML_BASE); | |
423 | #endif | |
424 | if ( *nexttoken != ' ' || errno == ERANGE || errno == EINVAL ) | |
425 | return false; | |
426 | ||
427 | // advance current position | |
428 | curpos = nexttoken + 1; | |
429 | ||
430 | // add this ID to the hashmap | |
431 | wxClassMemberIdHashMap::value_type v(id, p); | |
432 | map->insert(v); | |
433 | } | |
434 | ||
435 | return true; | |
436 | } | |
437 | ||
438 | ||
439 | // ---------------------------------------------------------------------------- | |
440 | // wxXmlGccInterface | |
441 | // ---------------------------------------------------------------------------- | |
442 | ||
5934cda1 FM |
443 | bool wxXmlGccInterface::Parse(const wxString& filename) |
444 | { | |
445 | wxXmlDocument doc; | |
446 | wxXmlNode *child; | |
447 | int nodes = 0; | |
448 | ||
449 | LogMessage("Parsing %s...", filename); | |
450 | ||
451 | if (!doc.Load(filename)) { | |
452 | LogError("can't load %s", filename); | |
453 | return false; | |
454 | } | |
455 | ||
456 | // start processing the XML file | |
457 | if (doc.GetRoot()->GetName() != "GCC_XML") { | |
458 | LogError("invalid root node for %s", filename); | |
459 | return false; | |
460 | } | |
461 | ||
462 | wxToResolveTypeHashMap toResolveTypes; | |
74bda203 FM |
463 | //wxArrayString arrMemberIds; |
464 | wxClassMemberIdHashMap members; | |
fdd4a897 FM |
465 | wxTypeIdHashMap types; |
466 | wxTypeIdHashMap files; | |
5934cda1 FM |
467 | |
468 | // prealloc quite a lot of memory! | |
469 | m_classes.Alloc(ESTIMATED_NUM_CLASSES); | |
74bda203 | 470 | //arrMemberIds.Alloc(ESTIMATED_NUM_TYPES); |
5934cda1 | 471 | |
5934cda1 FM |
472 | // build a list of wx classes and in general of all existent types |
473 | child = doc.GetRoot()->GetChildren(); | |
474 | while (child) | |
475 | { | |
476 | const wxString& n = child->GetName(); | |
74bda203 | 477 | |
fdd4a897 | 478 | unsigned long id = 0; |
74bda203 | 479 | if (!getID(&id, child->GetAttribute("id")) || (id == 0 && n != "File")) { |
fdd4a897 FM |
480 | |
481 | // NOTE: <File> nodes can have an id == "f0"... | |
482 | ||
483 | LogError("Invalid id for node %s: %s", n, child->GetAttribute("id")); | |
484 | return false; | |
485 | } | |
5934cda1 | 486 | |
5934cda1 FM |
487 | if (n == "Class") |
488 | { | |
fdd4a897 | 489 | wxString cname = child->GetAttribute("name"); |
5934cda1 FM |
490 | if (cname.IsEmpty()) { |
491 | LogError("Invalid empty name for '%s' node", n); | |
492 | return false; | |
493 | } | |
494 | ||
495 | // only register wx classes (do remember also the IDs of their members) | |
74bda203 FM |
496 | if (cname.StartsWith("wx")) |
497 | { | |
5934cda1 | 498 | // NB: "file" attribute contains an ID value that we'll resolve later |
fdd4a897 | 499 | m_classes.Add(wxClass(cname, child->GetAttribute("file"))); |
74bda203 FM |
500 | |
501 | const wxString& ids = child->GetAttribute("members"); | |
502 | if (ids.IsEmpty()) | |
503 | { | |
504 | if (child->GetAttribute("incomplete") != "1") { | |
505 | LogError("Invalid member IDs for '%s' class node (ID %s)", | |
506 | cname, child->GetAttribute("id")); | |
507 | return false; | |
508 | } | |
509 | //else: don't warn the user; it looks like "incomplete" classes | |
510 | // never have any member... | |
511 | } | |
512 | else | |
513 | { | |
514 | // decode the non-empty list of IDs: | |
515 | if (!getMemberIDs(&members, &m_classes.Last(), ids)) { | |
516 | LogError("Invalid member IDs for '%s' class node (ID %s)", | |
517 | cname, child->GetAttribute("id")); | |
518 | return false; | |
519 | } | |
520 | } | |
5934cda1 FM |
521 | } |
522 | ||
523 | // register this class also as possible return/argument type: | |
5934cda1 FM |
524 | types[id] = cname; |
525 | } | |
526 | else if (n == "PointerType" || n == "ReferenceType" || | |
527 | n == "CvQualifiedType" || n == "ArrayType") | |
528 | { | |
fdd4a897 | 529 | unsigned long type = 0; |
74bda203 | 530 | if (!getID(&type, child->GetAttribute("type")) || type == 0) { |
fdd4a897 | 531 | LogError("Invalid type for node %s: %s", n, child->GetAttribute("type")); |
5934cda1 FM |
532 | return false; |
533 | } | |
534 | ||
fdd4a897 | 535 | unsigned long attr = 0; |
5934cda1 FM |
536 | if (n == "PointerType") |
537 | attr = ATTRIB_POINTER; | |
538 | else if (n == "ReferenceType") | |
539 | attr = ATTRIB_REFERENCE; | |
fdd4a897 | 540 | else if (n == "CvQualifiedType" && child->GetAttribute("const") == "1") |
5934cda1 FM |
541 | attr = ATTRIB_CONST; |
542 | else if (n == "ArrayType") | |
543 | attr = ATTRIB_ARRAY; | |
544 | ||
545 | // these nodes make reference to other types... we'll resolve them later | |
5934cda1 FM |
546 | toResolveTypes[id] = toResolveTypeItem(type, attr); |
547 | } | |
548 | else if (n == "FunctionType" || n == "MethodType") | |
549 | { | |
550 | /* TODO: incomplete */ | |
551 | ||
fdd4a897 | 552 | unsigned long ret = 0; |
74bda203 | 553 | if (!getID(&ret, child->GetAttribute("returns")) || ret == 0) { |
fdd4a897 | 554 | LogError("Invalid empty returns value for '%s' node", n); |
5934cda1 FM |
555 | return false; |
556 | } | |
557 | ||
558 | // these nodes make reference to other types... we'll resolve them later | |
559 | toResolveTypes[id] = toResolveTypeItem(ret, 0); | |
560 | } | |
561 | else if (n == "File") | |
562 | { | |
fdd4a897 | 563 | if (!child->GetAttribute("id").StartsWith("f")) { |
5934cda1 FM |
564 | LogError("Unexpected file ID: %s", id); |
565 | return false; | |
566 | } | |
567 | ||
568 | // just ignore this node... all file IDs/names were already parsed | |
fdd4a897 | 569 | files[id] = child->GetAttribute("name"); |
5934cda1 FM |
570 | } |
571 | else | |
572 | { | |
573 | // we register everything else as a possible return/argument type: | |
fdd4a897 | 574 | const wxString& name = child->GetAttribute("name"); |
5934cda1 FM |
575 | |
576 | if (!name.IsEmpty()) | |
577 | { | |
578 | //typeIds.Add(id); | |
579 | //typeNames.Add(name); | |
580 | types[id] = name; | |
581 | } | |
582 | else | |
583 | { | |
584 | // this may happen with unnamed structs/union, special ctors, | |
585 | // or other exotic things which we are not interested to, since | |
586 | // they're never used as return/argument types by wxWidgets methods | |
587 | ||
588 | if (g_verbose) | |
589 | LogWarning("Type '%s' with ID '%s' does not have name attribute", n, id); | |
590 | ||
5934cda1 FM |
591 | types[id] = "TOFIX"; |
592 | } | |
593 | } | |
594 | ||
595 | child = child->GetNext(); | |
596 | ||
597 | // give feedback to the user about the progress... | |
598 | if ((++nodes%PROGRESS_RATE)==0) ShowProgress(); | |
599 | } | |
600 | ||
601 | // some nodes with IDs referenced by methods as return/argument types, do reference | |
602 | // in turn o ther nodes (see PointerType, ReferenceType and CvQualifierType above); | |
603 | // thus we need to resolve their name iteratively: | |
604 | while (toResolveTypes.size()>0) | |
605 | { | |
606 | if (g_verbose) | |
607 | LogMessage("%d types were collected; %d types need yet to be resolved...", | |
608 | types.size(), toResolveTypes.size()); | |
609 | ||
610 | for (wxToResolveTypeHashMap::iterator i = toResolveTypes.begin(); | |
611 | i != toResolveTypes.end();) | |
612 | { | |
fdd4a897 FM |
613 | unsigned long id = i->first; |
614 | unsigned long referenced = i->second.ref; | |
5934cda1 | 615 | |
fdd4a897 | 616 | wxTypeIdHashMap::iterator primary = types.find(referenced); |
5934cda1 FM |
617 | if (primary != types.end()) |
618 | { | |
619 | // this to-resolve-type references a "primary" type | |
620 | ||
621 | wxString newtype; | |
622 | int attribs = i->second.attribs; | |
623 | ||
624 | if (attribs & ATTRIB_CONST) | |
625 | newtype = "const " + primary->second; | |
626 | if (attribs & ATTRIB_REFERENCE) | |
627 | newtype = primary->second + "&"; | |
628 | if (attribs & ATTRIB_POINTER) | |
629 | newtype = primary->second + "*"; | |
630 | if (attribs & ATTRIB_ARRAY) | |
631 | newtype = primary->second + "[]"; | |
632 | ||
633 | // add the resolved type to the list of "primary" types | |
634 | types[id] = newtype; | |
635 | ||
636 | // this one has been resolved; erase it through its iterator! | |
637 | toResolveTypes.erase(i); | |
638 | ||
639 | // now iterator i is invalid; assign it again to the beginning | |
640 | i = toResolveTypes.begin(); | |
641 | } | |
642 | else | |
643 | { | |
644 | // then search in the referenced types themselves: | |
645 | wxToResolveTypeHashMap::iterator idx2 = toResolveTypes.find(referenced); | |
646 | if (idx2 != toResolveTypes.end()) | |
647 | { | |
648 | // merge this to-resolve-type with the idx2->second type | |
649 | i->second.ref = idx2->second.ref; | |
650 | i->second.attribs |= idx2->second.attribs; | |
651 | ||
652 | // this type will eventually be solved in the next while() iteration | |
653 | i++; | |
654 | } | |
655 | else | |
656 | { | |
657 | #if 1 | |
658 | LogError("Cannot solve '%s' reference type!", referenced); | |
659 | return false; | |
660 | #else | |
661 | typeIds.Add(toResolveTypeIds[i]); | |
662 | typeNames.Add("TOFIX"); | |
663 | ||
664 | // this one has been resolved! | |
665 | toResolveTypeIds.RemoveAt(i); | |
666 | toResolveRefType.RemoveAt(i); | |
667 | toResolveAttrib.RemoveAt(i); | |
668 | n--; | |
669 | #endif | |
670 | } | |
671 | } | |
672 | } | |
5934cda1 FM |
673 | } |
674 | ||
675 | // resolve header names | |
676 | for (unsigned int i=0; i<m_classes.GetCount(); i++) | |
677 | { | |
fdd4a897 | 678 | unsigned long fileID = 0; |
74bda203 | 679 | if (!getID(&fileID, m_classes[i].GetHeader()) || fileID == 0) { |
fdd4a897 FM |
680 | LogError("invalid header id: %s", m_classes[i].GetHeader()); |
681 | return false; | |
682 | } | |
683 | ||
684 | // search this file | |
685 | wxTypeIdHashMap::const_iterator idx = files.find(fileID); | |
5934cda1 FM |
686 | if (idx == files.end()) |
687 | { | |
688 | // this is an error! | |
689 | LogError("couldn't find file ID '%s'", m_classes[i].GetHeader()); | |
690 | } | |
691 | else | |
692 | m_classes[i].SetHeader(idx->second); | |
693 | } | |
694 | ||
695 | // build the list of the wx methods | |
696 | child = doc.GetRoot()->GetChildren(); | |
697 | while (child) | |
698 | { | |
699 | wxString n = child->GetName(); | |
700 | ||
74bda203 FM |
701 | // only register public methods |
702 | if (child->GetAttribute("access") == "public" && | |
703 | (n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod")) | |
5934cda1 | 704 | { |
74bda203 FM |
705 | unsigned long id = 0; |
706 | if (!getID(&id, child->GetAttribute("id"))) { | |
707 | LogError("invalid ID for node '%s' with ID '%s'", n, child->GetAttribute("id")); | |
708 | return false; | |
709 | } | |
5934cda1 | 710 | |
74bda203 FM |
711 | wxClassMemberIdHashMap::const_iterator it = members.find(id); |
712 | if (it != members.end()) | |
5934cda1 | 713 | { |
74bda203 | 714 | wxClass *p = it->second; |
5934cda1 | 715 | |
74bda203 FM |
716 | // this <Method> node is a method of the i-th class! |
717 | wxMethod newfunc; | |
718 | if (!ParseMethod(child, types, newfunc)) | |
719 | return false; | |
720 | ||
721 | if (newfunc.IsCtor() && !p->IsValidCtorForThisClass(newfunc)) { | |
722 | LogError("The method '%s' does not seem to be a ctor for '%s'", | |
723 | newfunc.GetName(), p->GetName()); | |
724 | return false; | |
5934cda1 | 725 | } |
74bda203 FM |
726 | if (newfunc.IsDtor() && !p->IsValidDtorForThisClass(newfunc)) { |
727 | LogError("The method '%s' does not seem to be a dtor for '%s'", | |
728 | newfunc.GetName(), p->GetName()); | |
729 | return false; | |
730 | } | |
731 | ||
732 | p->AddMethod(newfunc); | |
5934cda1 FM |
733 | } |
734 | } | |
735 | ||
736 | child = child->GetNext(); | |
737 | ||
738 | // give feedback to the user about the progress... | |
739 | if ((++nodes%PROGRESS_RATE)==0) ShowProgress(); | |
740 | } | |
741 | ||
742 | //wxPrint("\n"); | |
743 | if (!CheckParseResults()) | |
744 | return false; | |
745 | ||
746 | return true; | |
747 | } | |
748 | ||
749 | bool wxXmlGccInterface::ParseMethod(const wxXmlNode *p, | |
fdd4a897 | 750 | const wxTypeIdHashMap& types, |
5934cda1 FM |
751 | wxMethod& m) |
752 | { | |
753 | // get the real name | |
fdd4a897 | 754 | wxString name = p->GetAttribute("name").Strip(wxString::both); |
5934cda1 FM |
755 | if (p->GetName() == "Destructor") |
756 | name = "~" + name; | |
757 | else if (p->GetName() == "OperatorMethod") | |
758 | name = "operator" + name; | |
759 | ||
760 | // resolve return type | |
761 | wxType ret; | |
fdd4a897 | 762 | unsigned long retid = 0; |
74bda203 | 763 | if (!getID(&retid, p->GetAttribute("returns")) || retid == 0) |
5934cda1 FM |
764 | { |
765 | if (p->GetName() != "Destructor" && p->GetName() != "Constructor") { | |
766 | LogError("Empty return ID for method '%s', with ID '%s'", | |
fdd4a897 | 767 | name, p->GetAttribute("id")); |
5934cda1 FM |
768 | return false; |
769 | } | |
770 | } | |
771 | else | |
772 | { | |
fdd4a897 | 773 | wxTypeIdHashMap::const_iterator retidx = types.find(retid); |
5934cda1 FM |
774 | if (retidx == types.end()) { |
775 | LogError("Could not find return type ID '%s'", retid); | |
776 | return false; | |
777 | } | |
778 | ||
779 | ret = wxType(retidx->second); | |
780 | if (!ret.IsOk()) { | |
781 | LogError("Invalid return type '%s' for method '%s', with ID '%s'", | |
fdd4a897 | 782 | retidx->second, name, p->GetAttribute("id")); |
5934cda1 FM |
783 | return false; |
784 | } | |
785 | } | |
786 | ||
787 | // resolve argument types | |
788 | wxTypeArray argtypes; | |
789 | wxArrayString argdefs; | |
790 | wxXmlNode *arg = p->GetChildren(); | |
791 | while (arg) | |
792 | { | |
793 | if (arg->GetName() == "Argument") | |
794 | { | |
fdd4a897 | 795 | unsigned long id = 0; |
74bda203 | 796 | if (!getID(&id, arg->GetAttribute("type")) || id == 0) { |
fdd4a897 FM |
797 | LogError("Invalid argument type ID '%s' for method '%s' with ID %s", |
798 | arg->GetAttribute("type"), name, p->GetAttribute("id")); | |
799 | return false; | |
800 | } | |
801 | ||
802 | wxTypeIdHashMap::const_iterator idx = types.find(id); | |
5934cda1 FM |
803 | if (idx == types.end()) { |
804 | LogError("Could not find argument type ID '%s'", id); | |
805 | return false; | |
806 | } | |
807 | ||
808 | argtypes.Add(wxType(idx->second)); | |
809 | ||
fdd4a897 | 810 | wxString def = arg->GetAttribute("default"); |
5934cda1 FM |
811 | if (def.Contains("wxGetTranslation")) |
812 | argdefs.Add(wxEmptyString); // TODO: wxGetTranslation gives problems to gccxml | |
813 | else | |
814 | argdefs.Add(def); | |
815 | } | |
816 | ||
817 | arg = arg->GetNext(); | |
818 | } | |
819 | ||
820 | m.SetReturnType(ret); | |
821 | m.SetName(name); | |
822 | m.SetArgumentTypes(argtypes, argdefs); | |
fdd4a897 FM |
823 | m.SetConst(p->GetAttribute("const") == "1"); |
824 | m.SetStatic(p->GetAttribute("static") == "1"); | |
825 | m.SetVirtual(p->GetAttribute("virtual") == "1"); | |
5934cda1 FM |
826 | |
827 | if (!m.IsOk()) { | |
828 | LogError("The prototype '%s' is not valid!", m.GetAsString()); | |
829 | return false; | |
830 | } | |
831 | ||
832 | return true; | |
833 | } | |
834 | ||
835 | ||
836 | // ---------------------------------------------------------------------------- | |
837 | // wxXmlDoxygenInterface | |
838 | // ---------------------------------------------------------------------------- | |
839 | ||
840 | bool wxXmlDoxygenInterface::Parse(const wxString& filename) | |
841 | { | |
842 | wxXmlDocument index; | |
843 | wxXmlNode *compound; | |
844 | ||
845 | LogMessage("Parsing %s...", filename); | |
846 | ||
847 | if (!index.Load(filename)) { | |
848 | LogError("can't load %s", filename); | |
849 | return false; | |
850 | } | |
851 | ||
852 | // start processing the index: | |
853 | if (index.GetRoot()->GetName() != "doxygenindex") { | |
854 | LogError("invalid root node for %s", filename); | |
855 | return false; | |
856 | } | |
857 | ||
858 | m_classes.Alloc(ESTIMATED_NUM_CLASSES); | |
859 | ||
860 | // process files referenced by this index file | |
861 | compound = index.GetRoot()->GetChildren(); | |
862 | while (compound) | |
863 | { | |
864 | if (compound->GetName() == "compound" && | |
fdd4a897 | 865 | compound->GetAttribute("kind") == "class") |
5934cda1 | 866 | { |
fdd4a897 | 867 | wxString refid = compound->GetAttribute("refid"); |
5934cda1 FM |
868 | |
869 | wxFileName fn(filename); | |
870 | if (!ParseCompoundDefinition(fn.GetPath(wxPATH_GET_SEPARATOR) + refid + ".xml")) | |
871 | return false; | |
872 | } | |
873 | ||
874 | compound = compound->GetNext(); | |
875 | } | |
876 | //wxPrint("\n"); | |
877 | ||
878 | if (!CheckParseResults()) | |
879 | return false; | |
880 | ||
881 | return true; | |
882 | } | |
883 | ||
884 | bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename) | |
885 | { | |
886 | wxXmlDocument doc; | |
887 | wxXmlNode *child; | |
888 | int nodes = 0; | |
889 | ||
890 | if (g_verbose) | |
891 | LogMessage("Parsing %s...", filename); | |
892 | ||
893 | if (!doc.Load(filename)) { | |
894 | LogError("can't load %s", filename); | |
895 | return false; | |
896 | } | |
897 | ||
898 | // start processing this compound definition XML | |
899 | if (doc.GetRoot()->GetName() != "doxygen") { | |
900 | LogError("invalid root node for %s", filename); | |
901 | return false; | |
902 | } | |
903 | ||
904 | // build a list of wx classes | |
905 | child = doc.GetRoot()->GetChildren(); | |
906 | while (child) | |
907 | { | |
908 | if (child->GetName() == "compounddef" && | |
fdd4a897 | 909 | child->GetAttribute("kind") == "class") |
5934cda1 FM |
910 | { |
911 | // parse this class | |
912 | wxClass klass; | |
913 | wxString absoluteFile, header; | |
914 | ||
915 | wxXmlNode *subchild = child->GetChildren(); | |
916 | while (subchild) | |
917 | { | |
918 | if (subchild->GetName() == "sectiondef" && | |
fdd4a897 | 919 | subchild->GetAttribute("kind") == "public-func") |
5934cda1 FM |
920 | { |
921 | ||
922 | wxXmlNode *membernode = subchild->GetChildren(); | |
923 | while (membernode) | |
924 | { | |
925 | if (membernode->GetName() == "memberdef" && | |
fdd4a897 | 926 | membernode->GetAttribute("kind") == "function") |
5934cda1 FM |
927 | { |
928 | ||
929 | wxMethod m; | |
930 | if (ParseMethod(membernode, m, header)) | |
931 | { | |
932 | if (absoluteFile.IsEmpty()) | |
933 | absoluteFile = header; | |
934 | else if (header != absoluteFile) | |
935 | { | |
936 | LogError("The method '%s' is documented in a different " | |
937 | "file from others (which belong to '%s') ?", | |
938 | header, absoluteFile); | |
939 | return false; | |
940 | } | |
941 | ||
942 | klass.AddMethod(m); | |
943 | } | |
944 | } | |
945 | ||
946 | membernode = membernode->GetNext(); | |
947 | } | |
948 | ||
949 | // all methods of this class were taken from the header "absoluteFile": | |
950 | klass.SetHeader(absoluteFile); | |
951 | } | |
952 | else if (subchild->GetName() == "compoundname") | |
953 | { | |
954 | klass.SetName(subchild->GetNodeContent()); | |
955 | } | |
956 | /*else if (subchild->GetName() == "includes") | |
957 | { | |
958 | // NOTE: we'll get the header from the <location> tags | |
959 | // scattered inside <memberdef> tags instead of | |
960 | // this <includes> tag since it does not contain | |
961 | // the absolute path of the header | |
962 | ||
963 | klass.SetHeader(subchild->GetNodeContent()); | |
964 | }*/ | |
965 | ||
966 | subchild = subchild->GetNext(); | |
967 | } | |
968 | ||
969 | // add a new class | |
970 | if (klass.IsOk()) | |
971 | m_classes.Add(klass); | |
972 | else if (g_verbose) | |
973 | LogWarning("discarding class '%s' with %d methods...", | |
974 | klass.GetName(), klass.GetMethodCount()); | |
975 | } | |
976 | ||
977 | child = child->GetNext(); | |
978 | ||
979 | // give feedback to the user about the progress... | |
980 | if ((++nodes%PROGRESS_RATE)==0) ShowProgress(); | |
981 | } | |
982 | ||
983 | return true; | |
984 | } | |
985 | ||
986 | static wxString GetTextFromChildren(const wxXmlNode *n) | |
987 | { | |
988 | wxString text; | |
989 | ||
990 | // consider the tree | |
991 | // | |
992 | // <a><b>this</b> is a <b>string</b></a> | |
993 | // | |
994 | // <a> | |
995 | // |- <b> | |
996 | // | |- this | |
997 | // |- is a | |
998 | // |- <b> | |
999 | // |- string | |
1000 | // | |
1001 | // unlike wxXmlNode::GetNodeContent() which would return " is a " | |
1002 | // this function returns "this is a string" | |
1003 | ||
1004 | wxXmlNode *ref = n->GetChildren(); | |
1005 | while (ref) { | |
1006 | if (ref->GetType() == wxXML_ELEMENT_NODE) | |
1007 | text += ref->GetNodeContent(); | |
1008 | else if (ref->GetType() == wxXML_TEXT_NODE) | |
1009 | text += ref->GetContent(); | |
1010 | else | |
1011 | LogWarning("Unexpected node type while getting text from '%s' node", n->GetName()); | |
1012 | ||
1013 | ref = ref->GetNext(); | |
1014 | } | |
1015 | ||
1016 | return text; | |
1017 | } | |
1018 | ||
1019 | bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode* p, wxMethod& m, wxString& header) | |
1020 | { | |
1021 | wxTypeArray args; | |
1022 | wxArrayString defs; | |
1023 | long line; | |
1024 | ||
1025 | wxXmlNode *child = p->GetChildren(); | |
1026 | while (child) | |
1027 | { | |
1028 | if (child->GetName() == "name") | |
1029 | m.SetName(child->GetNodeContent()); | |
1030 | else if (child->GetName() == "type") | |
1031 | m.SetReturnType(wxType(GetTextFromChildren(child))); | |
1032 | else if (child->GetName() == "param") | |
1033 | { | |
1034 | wxString typestr, defstr, arrstr; | |
1035 | wxXmlNode *n = child->GetChildren(); | |
1036 | while (n) | |
1037 | { | |
1038 | if (n->GetName() == "type") | |
1039 | // if the <type> node has children, they should be all TEXT and <ref> nodes | |
1040 | // and we need to take the text they contain, in the order they appear | |
1041 | typestr = GetTextFromChildren(n); | |
1042 | else if (n->GetName() == "defval") | |
1043 | // same for the <defval> node | |
1044 | defstr = GetTextFromChildren(n); | |
1045 | else if (n->GetName() == "array") | |
1046 | arrstr = GetTextFromChildren(n); | |
1047 | ||
1048 | n = n->GetNext(); | |
1049 | } | |
1050 | ||
1051 | if (typestr.IsEmpty()) { | |
1052 | LogError("cannot find type node for a param in method '%s'", m.GetName()); | |
1053 | return false; | |
1054 | } | |
1055 | ||
1056 | args.Add(wxType(typestr + arrstr)); | |
1057 | defs.Add(defstr); | |
1058 | } | |
1059 | else if (child->GetName() == "location") | |
1060 | { | |
fdd4a897 | 1061 | if (child->GetAttribute("line").ToLong(&line)) |
5934cda1 | 1062 | m.SetLocation((int)line); |
fdd4a897 | 1063 | header = child->GetAttribute("file"); |
5934cda1 FM |
1064 | } |
1065 | ||
1066 | child = child->GetNext(); | |
1067 | } | |
1068 | ||
1069 | m.SetArgumentTypes(args, defs); | |
fdd4a897 FM |
1070 | m.SetConst(p->GetAttribute("const")=="yes"); |
1071 | m.SetStatic(p->GetAttribute("static")=="yes"); | |
1072 | m.SetVirtual(p->GetAttribute("virt")=="virtual"); | |
5934cda1 FM |
1073 | |
1074 | if (!m.IsOk()) { | |
1075 | LogError("The prototype '%s' is not valid!", m.GetAsString()); | |
1076 | return false; | |
1077 | } | |
1078 | ||
1079 | return true; | |
1080 | } |