PageRenderTime 158ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 1ms

/ExtLibs/wxWidgets/utils/ifacecheck/src/xmlparser.cpp

https://bitbucket.org/sycoso/cafu
C++ | 1701 lines | 1131 code | 305 blank | 265 comment | 426 complexity | dc86a5d9b2b03052ca87a11ae55faa87 MD5 | raw file
Possible License(s): AGPL-3.0, GPL-3.0, LGPL-2.0, GPL-2.0, BSD-3-Clause, LGPL-3.0, LGPL-2.1
  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. // For compilers that support precompilation, includes "wx/wx.h".
  11. #include "wx/wxprec.h"
  12. #ifdef __BORLANDC__
  13. #pragma hdrstop
  14. #endif
  15. // for all others, include the necessary headers
  16. #ifndef WX_PRECOMP
  17. #include "wx/crt.h"
  18. #endif
  19. #include "wx/xml/xml.h"
  20. #include "wx/wfstream.h"
  21. #include "wx/hashmap.h"
  22. #include "wx/filename.h"
  23. #include "xmlparser.h"
  24. #include <errno.h>
  25. #include <wx/arrimpl.cpp>
  26. WX_DEFINE_OBJARRAY(wxTypeArray)
  27. WX_DEFINE_OBJARRAY(wxArgumentTypeArray)
  28. WX_DEFINE_OBJARRAY(wxMethodArray)
  29. WX_DEFINE_OBJARRAY(wxClassArray)
  30. #define PROGRESS_RATE 1000 // each PROGRESS_RATE nodes processed print a dot
  31. #define ESTIMATED_NUM_CLASSES 600 // used by both wxXmlInterface-derived classes to prealloc mem
  32. // defined in ifacecheck.cpp
  33. extern bool g_verbose;
  34. // global variable:
  35. bool g_bLogEnabled = true;
  36. // ----------------------------------------------------------------------------
  37. // wxType
  38. // ----------------------------------------------------------------------------
  39. wxType wxEmptyType;
  40. void wxType::SetTypeFromString(const wxString& t)
  41. {
  42. /*
  43. TODO: optimize the following code writing a single function
  44. which works at char-level and does everything in a single pass
  45. */
  46. // clean the type string
  47. // ---------------------
  48. m_strType = t;
  49. // [] is the same as * for gccxml
  50. m_strType.Replace("[]", "*");
  51. m_strType.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
  52. m_strType.Replace("long unsigned int", "unsigned long");
  53. m_strType.Replace("short unsigned int", "unsigned short");
  54. // make sure the * and & operator always use the same spacing rules
  55. // (to make sure GetAsString() output is always consistent)
  56. m_strType.Replace("*", "* ");
  57. m_strType.Replace("&", "& ");
  58. m_strType.Replace(" *", "*");
  59. m_strType.Replace(" &", "&");
  60. while (m_strType.Contains(" "))
  61. m_strType.Replace(" ", " "); // do it once again
  62. m_strType.Replace(" ,", ",");
  63. // ADHOC-FIX
  64. m_strType.Replace("_wxArraywxArrayStringBase", "wxString");
  65. m_strType.Replace("ExitCode", "void*"); // used in wxThread stuff
  66. m_strType = m_strType.Strip(wxString::both);
  67. // clean the type string (this time for the comparison)
  68. // ----------------------------------------------------
  69. m_strTypeClean = m_strType; // begin with the already-cleaned string
  70. m_strTypeClean.Replace("const", "");
  71. m_strTypeClean.Replace("static", "");
  72. m_strTypeClean.Replace("*", "");
  73. m_strTypeClean.Replace("&", "");
  74. m_strTypeClean.Replace("[]", "");
  75. m_strTypeClean = m_strTypeClean.Strip(wxString::both);
  76. // to avoid false errors types like wxStandardPaths and wxStandardPathsBase
  77. // need to be considered as the same type
  78. if (m_strTypeClean.EndsWith("Base"))
  79. m_strTypeClean = m_strTypeClean.Left(m_strTypeClean.Len()-4);
  80. // remove the namespace from the types; there's no problem of conflicts
  81. // (except for templates) and this avoids tons of false warnings
  82. if (m_strTypeClean.Contains("::") && !m_strTypeClean.Contains("<"))
  83. m_strTypeClean = m_strTypeClean.Mid(m_strTypeClean.Find("::")+2);
  84. // ADHOC-FIX:
  85. m_strTypeClean.Replace("wxWindowID", "int");
  86. }
  87. bool wxType::IsOk() const
  88. {
  89. // NB: m_strType can contain the :: operator; think to e.g. the
  90. // "reverse_iterator_impl<wxString::const_iterator>" type
  91. // It can also contain commas, * and & operators etc
  92. return !m_strTypeClean.IsEmpty();
  93. }
  94. bool wxType::operator==(const wxType& m) const
  95. {
  96. // brain-dead comparison:
  97. if (m_strTypeClean == m.m_strTypeClean &&
  98. IsConst() == m.IsConst() &&
  99. IsStatic() == m.IsStatic() &&
  100. IsPointer() == m.IsPointer() &&
  101. IsReference() == m.IsReference())
  102. return true;
  103. if (g_verbose)
  104. {
  105. wxLogMessage("Type '%s' does not match type '%s'", m_strType, m.m_strType);
  106. wxLogMessage(" => TypeClean %s / %s; IsConst %d / %d; IsStatic %d / %d; IsPointer %d / %d; IsReference %d / %d",
  107. m_strTypeClean, m.m_strTypeClean, IsConst(), m.IsConst(),
  108. IsStatic(), m.IsStatic(), IsPointer(), m.IsPointer(),
  109. IsReference(), m.IsReference());
  110. }
  111. return false;
  112. }
  113. // ----------------------------------------------------------------------------
  114. // wxArgumentType
  115. // ----------------------------------------------------------------------------
  116. void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& defvalForCmp)
  117. {
  118. m_strDefaultValue = defval.Strip(wxString::both);
  119. m_strDefaultValueForCmp = defvalForCmp.IsEmpty() ?
  120. m_strDefaultValue : defvalForCmp.Strip(wxString::both);
  121. // clean the default argument strings
  122. // ----------------------------------
  123. // Note: we adjust the aesthetic form of the m_strDefaultValue string for the "modify mode"
  124. // of ifacecheck: we may need to write it out in an interface header
  125. wxString *p = NULL;
  126. for (int i=0; i<2; i++) // to avoid copying&pasting the code!
  127. {
  128. if (i == 0) p = &m_strDefaultValue;
  129. if (i == 1) p = &m_strDefaultValueForCmp;
  130. if (*p == "0u" || *p == "0l") *p = "0";
  131. p->Replace("0x000000001", "1");
  132. p->Replace("\\000\\000\\000", ""); // fix for unicode strings:
  133. p->Replace("\\011", "\\t");
  134. p->Replace("e+0", "");
  135. p->Replace("2147483647", "__INT_MAX__");
  136. // ADHOC-FIX: for wxConv* default values
  137. p->Replace("wxConvAuto(wxFONTENCODING_DEFAULT)", "wxConvAuto()");
  138. p->Replace("wxGet_wxConvUTF8()", "wxConvUTF8");
  139. p->Replace("wxGet_wxConvLocal()", "wxConvLocal");
  140. }
  141. // clean ONLY the default argument string specific for comparison
  142. // --------------------------------------------------------------
  143. if (m_strDefaultValueForCmp.StartsWith("wxT(") &&
  144. m_strDefaultValueForCmp.EndsWith(")"))
  145. {
  146. // get rid of the wxT() part
  147. unsigned int len = m_strDefaultValueForCmp.Len();
  148. m_strDefaultValueForCmp = m_strDefaultValueForCmp.Mid(4,len-5);
  149. }
  150. // ADHOC-FIX:
  151. // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
  152. // fix this to avoid false positives
  153. m_strDefaultValueForCmp.Replace("wxDateTime::", "");
  154. m_strDefaultValueForCmp.Replace("wxStockGDI::", ""); // same story for some other classes
  155. m_strDefaultValueForCmp.Replace("wxHelpEvent::", ""); // same story for some other classes
  156. m_strDefaultValueForCmp.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
  157. // ADHOC-FIX:
  158. if (m_strDefaultValueForCmp.Contains("wxGetTranslation"))
  159. m_strDefaultValueForCmp = "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
  160. }
  161. bool wxArgumentType::operator==(const wxArgumentType& m) const
  162. {
  163. if ((const wxType&)(*this) != (const wxType&)m)
  164. return false;
  165. // check if the default values match
  166. // ---------------------------------
  167. // ADHOC-FIX:
  168. // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
  169. // numbers; avoid false positives in this case!
  170. if (m_strArgName == m.m_strArgName && m_strArgName == "style" &&
  171. (m_strDefaultValueForCmp.IsNumber() || m.m_strDefaultValueForCmp.IsNumber()))
  172. return true;
  173. // fix for default values which were replaced by gcc-xml with their numeric values
  174. // (at this point we know that m_strTypeClean == m.m_strTypeClean):
  175. if (m_strTypeClean == "long" || m_strTypeClean == "int")
  176. {
  177. if ((m_strDefaultValueForCmp.IsNumber() && m.m_strDefaultValueForCmp.StartsWith("wx")) ||
  178. (m.m_strDefaultValueForCmp.IsNumber() && m_strDefaultValueForCmp.StartsWith("wx")))
  179. {
  180. if (g_verbose)
  181. {
  182. wxLogMessage("Supposing '%s' default value to be the same of '%s'...",
  183. m_strDefaultValueForCmp, m.m_strDefaultValueForCmp);
  184. }
  185. return true;
  186. }
  187. }
  188. else if (m_strTypeClean == "float" || m_strTypeClean == "double")
  189. // gccXML translates the default floating values in a hardly usable
  190. // format; e.g. 25.2 => 2.51999999999999992894572642398998141288757324219e+1
  191. // we avoid check on these...
  192. return true;
  193. if (m_strDefaultValueForCmp != m.m_strDefaultValueForCmp)
  194. {
  195. // maybe the default values are numbers.
  196. // in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
  197. // To handle these cases, we try to convert the default value strings to numbers:
  198. long def1val, def2val;
  199. if (m_strDefaultValueForCmp.ToLong(&def1val, 0 /* auto-detect */) &&
  200. m.m_strDefaultValueForCmp.ToLong(&def2val, 0 /* auto-detect */))
  201. {
  202. if (def1val == def2val)
  203. return true; // the default values match
  204. }
  205. if (g_verbose)
  206. {
  207. wxLogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
  208. m_strType, m_strDefaultValueForCmp, m.m_strType, m.m_strDefaultValueForCmp);
  209. }
  210. return false;
  211. }
  212. // we deliberately avoid checks on the argument name
  213. return true;
  214. }
  215. // ----------------------------------------------------------------------------
  216. // wxMethod
  217. // ----------------------------------------------------------------------------
  218. bool wxMethod::IsOk() const
  219. {
  220. // NOTE: m_retType can be a wxEmptyType, and means that this method
  221. // is a ctor or a dtor.
  222. if (!m_retType.IsOk() && m_retType!=wxEmptyType) {
  223. wxLogError("'%s' method has invalid return type: %s", m_retType.GetAsString());
  224. return false;
  225. }
  226. if (m_strName.IsEmpty())
  227. return false;
  228. // a function can't be both const and static or virtual and static!
  229. if ((m_bConst && m_bStatic) || ((m_bVirtual || m_bPureVirtual) && m_bStatic)) {
  230. wxLogError("'%s' method can't be both const/static or virtual/static", m_strName);
  231. return false;
  232. }
  233. wxASSERT(!m_bPureVirtual || (m_bPureVirtual && m_bVirtual));
  234. for (unsigned int i=0; i<m_args.GetCount(); i++)
  235. if (!m_args[i].IsOk()) {
  236. wxLogError("'%s' method has invalid %d-th argument type: %s",
  237. m_strName, i+1, m_args[i].GetAsString());
  238. return false;
  239. }
  240. // NB: the default value of the arguments can contain pretty much everything
  241. // (think to e.g. wxPoint(3+4/2,0) or *wxBLACK or someClass<type>)
  242. // so we don't do any test on their contents
  243. if (m_args.GetCount()>0)
  244. {
  245. bool previousArgHasDefault = m_args[0].HasDefaultValue();
  246. for (unsigned int i=1; i<m_args.GetCount(); i++)
  247. {
  248. if (previousArgHasDefault && !m_args[i].HasDefaultValue()) {
  249. wxLogError("'%s' method has %d-th argument which has no default value "
  250. "(while the previous one had one!)",
  251. m_strName, i+1);
  252. return false;
  253. }
  254. previousArgHasDefault = m_args[i].HasDefaultValue();
  255. }
  256. }
  257. return true;
  258. }
  259. bool wxMethod::MatchesExceptForAttributes(const wxMethod& m) const
  260. {
  261. if (GetReturnType() != m.GetReturnType() ||
  262. GetName() != m.GetName())
  263. {
  264. if (g_verbose)
  265. {
  266. wxLogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m.GetName());
  267. }
  268. return false;
  269. }
  270. if (m_args.GetCount()!=m.m_args.GetCount()) {
  271. if (g_verbose)
  272. {
  273. wxLogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
  274. m_strName, m_args.GetCount(), m_strName, m.m_args.GetCount());
  275. }
  276. return false;
  277. }
  278. // compare argument types
  279. for (unsigned int i=0; i<m_args.GetCount(); i++)
  280. if (m_args[i] != m.m_args[i])
  281. return false;
  282. return true;
  283. }
  284. bool wxMethod::ActsAsDefaultCtor() const
  285. {
  286. if (!IsCtor())
  287. return false;
  288. for (unsigned int i=0; i<m_args.GetCount(); i++)
  289. if (!m_args[i].HasDefaultValue())
  290. return false;
  291. return true;
  292. }
  293. bool wxMethod::operator==(const wxMethod& m) const
  294. {
  295. // check attributes
  296. if (IsConst() != m.IsConst() ||
  297. IsStatic() != m.IsStatic() ||
  298. IsVirtual() != m.IsVirtual() ||
  299. IsPureVirtual() != m.IsPureVirtual() ||
  300. IsDeprecated() != m.IsDeprecated() ||
  301. GetAccessSpecifier() != m.GetAccessSpecifier())
  302. {
  303. if (g_verbose)
  304. {
  305. wxLogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m.GetName());
  306. }
  307. return false;
  308. }
  309. // check everything else
  310. return MatchesExceptForAttributes(m);
  311. }
  312. wxString wxMethod::GetAsString(bool bWithArgumentNames, bool bCleanDefaultValues,
  313. bool bDeprecated, bool bAccessSpec) const
  314. {
  315. wxString ret;
  316. // NOTE: for return and argument types, never use wxType::GetAsCleanString
  317. // since in that way we'd miss important decorators like &,*,const etc
  318. if (m_retType!=wxEmptyType)
  319. ret += m_retType.GetAsString() + " ";
  320. //else; this is a ctor or dtor
  321. ret += m_strName + "(";
  322. for (unsigned int i=0; i<m_args.GetCount(); i++)
  323. {
  324. ret += m_args[i].GetAsString();
  325. const wxString& name = m_args[i].GetArgumentName();
  326. if (bWithArgumentNames && !name.IsEmpty())
  327. ret += " " + name;
  328. const wxString& def = bCleanDefaultValues ?
  329. m_args[i].GetDefaultCleanValue() : m_args[i].GetDefaultValue();
  330. if (!def.IsEmpty())
  331. ret += " = " + def;
  332. ret += ", ";
  333. }
  334. if (m_args.GetCount()>0)
  335. ret = ret.Left(ret.Len()-2);
  336. ret += ")";
  337. if (m_bConst)
  338. ret += " const";
  339. if (m_bStatic)
  340. ret = "static " + ret;
  341. if (m_bVirtual || m_bPureVirtual)
  342. ret = "virtual " + ret;
  343. if (m_bPureVirtual)
  344. ret += " = 0";
  345. if (m_bDeprecated && bDeprecated)
  346. ret += " [deprecated]";
  347. if (bAccessSpec)
  348. {
  349. switch (m_access)
  350. {
  351. case wxMAS_PUBLIC:
  352. ret += " [public]";
  353. break;
  354. case wxMAS_PROTECTED:
  355. ret += " [protected]";
  356. break;
  357. case wxMAS_PRIVATE:
  358. ret += " [private]";
  359. break;
  360. }
  361. }
  362. return ret;
  363. }
  364. void wxMethod::Dump(wxTextOutputStream& stream) const
  365. {
  366. stream << "[" + m_retType.GetAsString() + "]";
  367. stream << "[" + m_strName + "]";
  368. for (unsigned int i=0; i<m_args.GetCount(); i++)
  369. stream << "[" + m_args[i].GetAsString() + " " + m_args[i].GetArgumentName() +
  370. "=" + m_args[i].GetDefaultValue() + "]";
  371. if (IsConst())
  372. stream << " CONST";
  373. if (IsStatic())
  374. stream << " STATIC";
  375. if (IsVirtual())
  376. stream << " VIRTUAL";
  377. if (IsPureVirtual())
  378. stream << " PURE-VIRTUAL";
  379. if (IsDeprecated())
  380. stream << " DEPRECATED";
  381. // no final newline
  382. }
  383. // ----------------------------------------------------------------------------
  384. // wxClass
  385. // ----------------------------------------------------------------------------
  386. wxString wxClass::GetNameWithoutTemplate() const
  387. {
  388. // NB: I'm not sure this is the right terminology for this function!
  389. if (m_strName.Contains("<"))
  390. return m_strName.Left(m_strName.Find("<"));
  391. return m_strName;
  392. }
  393. bool wxClass::IsValidCtorForThisClass(const wxMethod& m) const
  394. {
  395. // remember that e.g. the ctor for wxWritableCharTypeBuffer<wchar_t> is
  396. // named wxWritableCharTypeBuffer, without the <...> part!
  397. if (m.IsCtor() && m.GetName() == GetNameWithoutTemplate())
  398. return true;
  399. return false;
  400. }
  401. bool wxClass::IsValidDtorForThisClass(const wxMethod& m) const
  402. {
  403. if (m.IsDtor() && m.GetName() == "~" + GetNameWithoutTemplate())
  404. return true;
  405. return false;
  406. }
  407. void wxClass::Dump(wxTextOutputStream& out) const
  408. {
  409. out << m_strName + "\n";
  410. for (unsigned int i=0; i<m_methods.GetCount(); i++) {
  411. // dump all our methods
  412. out << "|- ";
  413. m_methods[i].Dump(out);
  414. out << "\n";
  415. }
  416. out << "\n";
  417. }
  418. bool wxClass::CheckConsistency() const
  419. {
  420. for (unsigned int i=0; i<m_methods.GetCount(); i++)
  421. for (unsigned int j=0; j<m_methods.GetCount(); j++)
  422. if (i!=j && m_methods[i] == m_methods[j])
  423. {
  424. wxLogError("class %s has two methods with the same prototype: '%s'",
  425. m_strName, m_methods[i].GetAsString());
  426. return false;
  427. // fix the problem?
  428. //((wxClass*)this)->m_methods.RemoveAt(j);
  429. //j--;
  430. }
  431. return true;
  432. }
  433. const wxMethod* wxClass::FindMethod(const wxMethod& m) const
  434. {
  435. for (unsigned int i=0; i<m_methods.GetCount(); i++)
  436. if (m_methods[i] == m)
  437. return &m_methods[i];
  438. return NULL;
  439. }
  440. const wxMethod* wxClass::RecursiveUpwardFindMethod(const wxMethod& m,
  441. const wxXmlInterface* allclasses) const
  442. {
  443. // first, search into *this
  444. const wxMethod* ret = FindMethod(m);
  445. if (ret)
  446. return ret;
  447. // then, search into its parents
  448. for (unsigned int i=0; i<m_parents.GetCount(); i++)
  449. {
  450. // ignore non-wx-classes parents
  451. // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
  452. if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
  453. {
  454. const wxClass *parent = allclasses->FindClass(m_parents[i]);
  455. if (!parent) {
  456. wxLogError("Could not find parent '%s' of class '%s'...",
  457. m_parents[i], GetName());
  458. return false;
  459. }
  460. const wxMethod *parentMethod = parent->RecursiveUpwardFindMethod(m, allclasses);
  461. if (parentMethod)
  462. return parentMethod;
  463. }
  464. }
  465. // could not find anything even in parent classes...
  466. return NULL;
  467. }
  468. wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const
  469. {
  470. wxMethodPtrArray ret;
  471. for (unsigned int i=0; i<m_methods.GetCount(); i++)
  472. if (m_methods[i].GetName() == name)
  473. ret.Add(&m_methods[i]);
  474. return ret;
  475. }
  476. wxMethodPtrArray wxClass::RecursiveUpwardFindMethodsNamed(const wxString& name,
  477. const wxXmlInterface* allclasses) const
  478. {
  479. // first, search into *this
  480. wxMethodPtrArray ret = FindMethodsNamed(name);
  481. if (ret.GetCount()>0)
  482. return ret; // stop here, don't look upward in the parents
  483. // then, search into parents of this class
  484. for (unsigned int i=0; i<m_parents.GetCount(); i++)
  485. {
  486. // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
  487. if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
  488. {
  489. const wxClass *parent = allclasses->FindClass(m_parents[i]);
  490. if (!parent) {
  491. wxLogError("Could not find parent '%s' of class '%s'...",
  492. m_parents[i], GetName());
  493. return false;
  494. }
  495. wxMethodPtrArray temp = parent->RecursiveUpwardFindMethodsNamed(name, allclasses);
  496. WX_APPEND_ARRAY(ret, temp);
  497. }
  498. }
  499. return ret;
  500. }
  501. // ----------------------------------------------------------------------------
  502. // wxXmlInterface
  503. // ----------------------------------------------------------------------------
  504. WX_DEFINE_SORTED_ARRAY(wxClass*, wxSortedClassArray);
  505. int CompareWxClassObjects(wxClass *item1, wxClass *item2)
  506. {
  507. // sort alphabetically
  508. return item1->GetName().Cmp(item2->GetName());
  509. }
  510. void wxXmlInterface::Dump(const wxString& filename)
  511. {
  512. wxFFileOutputStream apioutput( filename );
  513. wxTextOutputStream apiout( apioutput );
  514. // dump the classes in alphabetical order
  515. wxSortedClassArray sorted(CompareWxClassObjects);
  516. sorted.Alloc(m_classes.GetCount());
  517. unsigned i;
  518. for (i=0; i<m_classes.GetCount(); i++)
  519. sorted.Add(&m_classes[i]);
  520. // now they have been sorted
  521. for (i=0; i<sorted.GetCount(); i++)
  522. sorted[i]->Dump(apiout);
  523. }
  524. bool wxXmlInterface::CheckConsistency() const
  525. {
  526. // this check can be quite slow, so do it only for debug releases:
  527. //#ifdef __WXDEBUG__
  528. for (unsigned int i=0; i<m_classes.GetCount(); i++)
  529. {
  530. if (!m_classes[i].CheckConsistency())
  531. return false;
  532. for (unsigned int j=0; j<m_classes.GetCount(); j++)
  533. if (i!=j && m_classes[i].GetName() == m_classes[j].GetName())
  534. {
  535. wxLogError("two classes have the same name: %s",
  536. m_classes[i].GetName());
  537. return false;
  538. }
  539. }
  540. //#endif
  541. return true;
  542. }
  543. wxClassPtrArray wxXmlInterface::FindClassesDefinedIn(const wxString& headerfile) const
  544. {
  545. wxClassPtrArray ret;
  546. for (unsigned int i=0; i<m_classes.GetCount(); i++)
  547. if (m_classes[i].GetHeader() == headerfile)
  548. ret.Add(&m_classes[i]);
  549. return ret;
  550. }
  551. // ----------------------------------------------------------------------------
  552. // wxXmlGccInterface helper declarations
  553. // ----------------------------------------------------------------------------
  554. // or-able flags for a toResolveTypeItem->attrib:
  555. #define ATTRIB_CONST 1
  556. #define ATTRIB_REFERENCE 2
  557. #define ATTRIB_POINTER 4
  558. #define ATTRIB_ARRAY 8
  559. // it may sound strange but gccxml, in order to produce shorter ID names
  560. // uses (after the underscore) characters in range 0-9 and a-z in the ID names;
  561. // in order to be able to translate such strings into numbers using strtoul()
  562. // we use as base 10 (possible digits) + 25 (possible characters) = 35
  563. #define GCCXML_BASE 35
  564. class toResolveTypeItem
  565. {
  566. public:
  567. toResolveTypeItem() { attribs=0; }
  568. toResolveTypeItem(unsigned int refID, unsigned int attribint)
  569. : ref(refID), attribs(attribint) {}
  570. unsigned long ref, // the referenced type's ID
  571. attribs; // the attributes of this reference
  572. };
  573. #if 1
  574. // for wxToResolveTypeHashMap, keys == gccXML IDs and values == toResolveTypeItem
  575. WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem,
  576. wxIntegerHash, wxIntegerEqual,
  577. wxToResolveTypeHashMap );
  578. // for wxClassMemberIdHashMap, keys == gccXML IDs and values == wxClass which owns that member ID
  579. WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
  580. wxIntegerHash, wxIntegerEqual,
  581. wxClassMemberIdHashMap );
  582. #else
  583. #include <map>
  584. typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap;
  585. #endif
  586. // utility to parse gccXML ID values;
  587. // this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
  588. // but is a little bit faster
  589. bool getID(unsigned long *id, const wxString& str)
  590. {
  591. const wxStringCharType * const start = str.wx_str()+1;
  592. wxStringCharType *end;
  593. #if wxUSE_UNICODE_WCHAR
  594. unsigned long val = wcstoul(start, &end, GCCXML_BASE);
  595. #else
  596. unsigned long val = strtoul(start, &end, GCCXML_BASE);
  597. #endif
  598. // return true only if scan was stopped by the terminating NUL and
  599. // if the string was not empty to start with and no under/overflow
  600. // occurred:
  601. if ( *end != '\0' || end == start || errno == ERANGE || errno == EINVAL )
  602. return false;
  603. *id = val;
  604. return true;
  605. }
  606. // utility specialized to parse efficiently the gccXML list of IDs which occur
  607. // in nodes like <Class> ones... i.e. numeric values separated by " _" token
  608. bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxString& str)
  609. {
  610. const wxStringCharType * const start = str.wx_str();
  611. #if wxUSE_UNICODE_WCHAR
  612. size_t len = wcslen(start);
  613. #else
  614. size_t len = strlen(start);
  615. #endif
  616. if (len == 0 || start[0] != '_')
  617. return false;
  618. const wxStringCharType *curpos = start,
  619. *end = start + len;
  620. wxStringCharType *nexttoken;
  621. while (curpos < end)
  622. {
  623. // curpos always points to the underscore of the next token to parse:
  624. #if wxUSE_UNICODE_WCHAR
  625. unsigned long id = wcstoul(curpos+1, &nexttoken, GCCXML_BASE);
  626. #else
  627. unsigned long id = strtoul(curpos+1, &nexttoken, GCCXML_BASE);
  628. #endif
  629. if ( *nexttoken != ' ' || errno == ERANGE || errno == EINVAL )
  630. return false;
  631. // advance current position
  632. curpos = nexttoken + 1;
  633. // add this ID to the hashmap
  634. wxClassMemberIdHashMap::value_type v(id, p);
  635. map->insert(v);
  636. }
  637. return true;
  638. }
  639. // ----------------------------------------------------------------------------
  640. // wxXmlGccInterface
  641. // ----------------------------------------------------------------------------
  642. bool wxXmlGccInterface::Parse(const wxString& filename)
  643. {
  644. wxXmlDocument doc;
  645. wxXmlNode *child;
  646. int nodes = 0;
  647. wxLogMessage("Parsing %s...", filename);
  648. if (!doc.Load(filename)) {
  649. wxLogError("can't load %s", filename);
  650. return false;
  651. }
  652. // start processing the XML file
  653. if (doc.GetRoot()->GetName() != "GCC_XML") {
  654. wxLogError("invalid root node for %s", filename);
  655. return false;
  656. }
  657. wxString version = doc.GetRoot()->GetAttribute("cvs_revision");
  658. bool old = false;
  659. #define MIN_REVISION 120
  660. if (!version.StartsWith("1."))
  661. old = true;
  662. if (!old)
  663. {
  664. unsigned long rev = 0;
  665. if (!version.Mid(2).ToULong(&rev))
  666. old = true;
  667. else
  668. if (rev < MIN_REVISION)
  669. old = true;
  670. }
  671. if (old)
  672. {
  673. wxLogError("The version of GCC-XML used for the creation of %s is too old; "
  674. "the cvs_revision attribute of the root node reports '%s', "
  675. "minimal required is 1.%d.", filename, version, MIN_REVISION);
  676. return false;
  677. }
  678. wxToResolveTypeHashMap toResolveTypes;
  679. wxClassMemberIdHashMap members;
  680. wxTypeIdHashMap types;
  681. wxTypeIdHashMap files;
  682. wxTypeIdHashMap typedefs;
  683. // prealloc quite a lot of memory!
  684. m_classes.Alloc(ESTIMATED_NUM_CLASSES);
  685. // build a list of wx classes and in general of all existent types
  686. child = doc.GetRoot()->GetChildren();
  687. while (child)
  688. {
  689. const wxString& n = child->GetName();
  690. unsigned long id = 0;
  691. if (!getID(&id, child->GetAttribute("id")) || (id == 0 && n != "File")) {
  692. // NOTE: <File> nodes can have an id == "f0"...
  693. wxLogError("Invalid id for node %s: %s", n, child->GetAttribute("id"));
  694. return false;
  695. }
  696. if (n == "Class")
  697. {
  698. wxString cname = child->GetAttribute("name");
  699. if (cname.IsEmpty()) {
  700. wxLogError("Invalid empty name for '%s' node", n);
  701. return false;
  702. }
  703. // only register wx classes (do remember also the IDs of their members)
  704. if (cname.StartsWith("wx"))
  705. {
  706. // NB: "file" attribute contains an ID value that we'll resolve later
  707. m_classes.Add(wxClass(cname, child->GetAttribute("file")));
  708. // the just-inserted class:
  709. wxClass *newClass = &m_classes.Last();
  710. // now get a list of the base classes:
  711. wxXmlNode *baseNode = child->GetChildren();
  712. while (baseNode)
  713. {
  714. // for now we store as "parents" only the parent IDs...
  715. // later we will resolve them into full class names
  716. if (baseNode->GetName() == "Base")
  717. newClass->AddParent(baseNode->GetAttribute("type"));
  718. baseNode = baseNode->GetNext();
  719. }
  720. const wxString& ids = child->GetAttribute("members");
  721. if (ids.IsEmpty())
  722. {
  723. if (child->GetAttribute("incomplete") != "1") {
  724. wxLogError("Invalid member IDs for '%s' class node: %s",
  725. cname, child->GetAttribute("id"));
  726. return false;
  727. }
  728. //else: don't warn the user; it looks like "incomplete" classes
  729. // never have any member...
  730. }
  731. else
  732. {
  733. // decode the non-empty list of IDs:
  734. if (!getMemberIDs(&members, newClass, ids)) {
  735. wxLogError("Invalid member IDs for '%s' class node: %s",
  736. cname, child->GetAttribute("id"));
  737. return false;
  738. }
  739. }
  740. }
  741. // register this class also as possible return/argument type:
  742. types[id] = cname;
  743. }
  744. else if (n == "Typedef")
  745. {
  746. unsigned long typeId = 0;
  747. if (!getID(&typeId, child->GetAttribute("type"))) {
  748. wxLogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
  749. return false;
  750. }
  751. // this typedef node tell us that every type referenced with the
  752. // "typeId" ID should be called with another name:
  753. wxString name = child->GetAttribute("name");
  754. // save this typedef in a separate hashmap...
  755. typedefs[typeId] = name;
  756. types[id] = name;
  757. }
  758. else if (n == "PointerType" || n == "ReferenceType" ||
  759. n == "CvQualifiedType" || n == "ArrayType")
  760. {
  761. unsigned long type = 0;
  762. if (!getID(&type, child->GetAttribute("type")) || type == 0) {
  763. wxLogError("Invalid type for node %s: %s", n, child->GetAttribute("type"));
  764. return false;
  765. }
  766. unsigned long attr = 0;
  767. if (n == "PointerType")
  768. attr = ATTRIB_POINTER;
  769. else if (n == "ReferenceType")
  770. attr = ATTRIB_REFERENCE;
  771. else if (n == "CvQualifiedType" && child->GetAttribute("const") == "1")
  772. attr = ATTRIB_CONST;
  773. else if (n == "ArrayType")
  774. attr = ATTRIB_ARRAY;
  775. // these nodes make reference to other types... we'll resolve them later
  776. toResolveTypes[id] = toResolveTypeItem(type, attr);
  777. }
  778. else if (n == "FunctionType" || n == "MethodType")
  779. {
  780. /*
  781. TODO: parsing FunctionType and MethodType nodes is not as easy
  782. as for other "simple" types.
  783. */
  784. wxString argstr;
  785. wxXmlNode *arg = child->GetChildren();
  786. while (arg)
  787. {
  788. if (arg->GetName() == "Argument")
  789. argstr += arg->GetAttribute("type") + ", ";
  790. arg = arg->GetNext();
  791. }
  792. if (argstr.Len() > 0)
  793. argstr = argstr.Left(argstr.Len()-2); // remove final comma
  794. // these nodes make reference to other types... we'll resolve them later
  795. //toResolveTypes[id] = toResolveTypeItem(ret, 0);
  796. //types[id] = child->GetAttribute("returns") + "(" + argstr + ")";
  797. types[id] = "TOFIX"; // typically this type will be "fixed" thanks
  798. // to a typedef later...
  799. }
  800. else if (n == "File")
  801. {
  802. if (!child->GetAttribute("id").StartsWith("f")) {
  803. wxLogError("Unexpected file ID: %s", child->GetAttribute("id"));
  804. return false;
  805. }
  806. // just ignore this node... all file IDs/names were already parsed
  807. files[id] = child->GetAttribute("name");
  808. }
  809. else
  810. {
  811. // we register everything else as a possible return/argument type:
  812. const wxString& name = child->GetAttribute("name");
  813. if (!name.IsEmpty())
  814. {
  815. //typeIds.Add(id);
  816. //typeNames.Add(name);
  817. types[id] = name;
  818. }
  819. else
  820. {
  821. // this may happen with unnamed structs/union, special ctors,
  822. // or other exotic things which we are not interested to, since
  823. // they're never used as return/argument types by wxWidgets methods
  824. if (g_verbose)
  825. {
  826. wxLogWarning("Type node '%s' with ID '%s' does not have name attribute",
  827. n, child->GetAttribute("id"));
  828. }
  829. types[id] = "TOFIX";
  830. }
  831. }
  832. child = child->GetNext();
  833. // give feedback to the user about the progress...
  834. if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
  835. }
  836. // some nodes with IDs referenced by methods as return/argument types, do reference
  837. // in turn other nodes (see PointerType, ReferenceType and CvQualifierType above);
  838. // thus we need to resolve their name iteratively:
  839. while (toResolveTypes.size()>0)
  840. {
  841. if (g_verbose)
  842. {
  843. wxLogMessage("%d types were collected; %d types need yet to be resolved...",
  844. types.size(), toResolveTypes.size());
  845. }
  846. for (wxToResolveTypeHashMap::iterator i = toResolveTypes.begin();
  847. i != toResolveTypes.end();)
  848. {
  849. unsigned long id = i->first;
  850. unsigned long referenced = i->second.ref;
  851. wxTypeIdHashMap::iterator primary = types.find(referenced);
  852. if (primary != types.end())
  853. {
  854. // this to-resolve-type references a "primary" type
  855. wxString newtype = primary->second;
  856. int attribs = i->second.attribs;
  857. // attribs may contain a combination of ATTRIB_* flags:
  858. if (attribs & ATTRIB_CONST)
  859. newtype = "const " + newtype;
  860. if (attribs & ATTRIB_REFERENCE)
  861. newtype = newtype + "&";
  862. if (attribs & ATTRIB_POINTER)
  863. newtype = newtype + "*";
  864. if (attribs & ATTRIB_ARRAY)
  865. newtype = newtype + "[]";
  866. // add the resolved type to the list of "primary" types
  867. if (newtype.Contains("TOFIX") && typedefs[id] != "")
  868. types[id] = typedefs[id]; // better use a typedef for this type!
  869. else
  870. types[id] = newtype;
  871. // this one has been resolved; erase it through its iterator!
  872. toResolveTypes.erase(i);
  873. // now iterator i is invalid; assign it again to the beginning
  874. i = toResolveTypes.begin();
  875. }
  876. else
  877. {
  878. // then search in the referenced types themselves:
  879. wxToResolveTypeHashMap::iterator idx2 = toResolveTypes.find(referenced);
  880. if (idx2 != toResolveTypes.end())
  881. {
  882. // merge this to-resolve-type with the idx2->second type
  883. i->second.ref = idx2->second.ref;
  884. i->second.attribs |= idx2->second.attribs;
  885. // this type will eventually be solved in the next while() iteration
  886. i++;
  887. }
  888. else
  889. {
  890. wxLogError("Cannot solve '%d' reference type!", referenced);
  891. return false;
  892. }
  893. }
  894. }
  895. }
  896. // resolve header names
  897. unsigned i;
  898. for (i=0; i<m_classes.GetCount(); i++)
  899. {
  900. unsigned long fileID = 0;
  901. if (!getID(&fileID, m_classes[i].GetHeader()) || fileID == 0) {
  902. wxLogError("invalid header id: %s", m_classes[i].GetHeader());
  903. return false;
  904. }
  905. // search this file
  906. wxTypeIdHashMap::const_iterator idx = files.find(fileID);
  907. if (idx == files.end())
  908. {
  909. // this is an error!
  910. wxLogError("couldn't find file ID '%s'", m_classes[i].GetHeader());
  911. }
  912. else
  913. m_classes[i].SetHeader(idx->second);
  914. }
  915. // resolve parent names
  916. for (i=0; i<m_classes.GetCount(); i++)
  917. {
  918. for (unsigned int k=0; k<m_classes[i].GetParentCount(); k++)
  919. {
  920. unsigned long id;
  921. if (!getID(&id, m_classes[i].GetParent(k))) {
  922. wxLogError("invalid parent class ID for '%s'", m_classes[i].GetName());
  923. return false;
  924. }
  925. wxTypeIdHashMap::const_iterator idx = types.find(id);
  926. if (idx == types.end())
  927. {
  928. // this is an error!
  929. wxLogError("couldn't find parent class ID '%d'", id);
  930. }
  931. else
  932. // replace k-th parent with its true name:
  933. m_classes[i].SetParent(k, idx->second);
  934. }
  935. }
  936. // build the list of the wx methods
  937. child = doc.GetRoot()->GetChildren();
  938. while (child)
  939. {
  940. wxString n = child->GetName(), acc = child->GetAttribute("access");
  941. // only register public&protected methods
  942. if ((acc == "public" || acc == "protected") &&
  943. (n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod"))
  944. {
  945. unsigned long id = 0;
  946. if (!getID(&id, child->GetAttribute("id"))) {
  947. wxLogError("invalid ID for node '%s' with ID '%s'", n, child->GetAttribute("id"));
  948. return false;
  949. }
  950. wxClassMemberIdHashMap::const_iterator it = members.find(id);
  951. if (it != members.end())
  952. {
  953. wxClass *p = it->second;
  954. // this <Method> node is a method of the i-th class!
  955. wxMethod newfunc;
  956. if (!ParseMethod(child, types, newfunc)) {
  957. wxLogError("The method '%s' could not be added to class '%s'",
  958. child->GetAttribute("demangled"), p->GetName());
  959. return false;
  960. }
  961. // do some additional check that we can do only here:
  962. if (newfunc.IsCtor() && !p->IsValidCtorForThisClass(newfunc)) {
  963. wxLogError("The method '%s' does not seem to be a ctor for '%s'",
  964. newfunc.GetName(), p->GetName());
  965. return false;
  966. }
  967. if (newfunc.IsDtor() && !p->IsValidDtorForThisClass(newfunc)) {
  968. wxLogError("The method '%s' does not seem to be a dtor for '%s'",
  969. newfunc.GetName(), p->GetName());
  970. return false;
  971. }
  972. p->AddMethod(newfunc);
  973. }
  974. }
  975. child = child->GetNext();
  976. // give feedback to the user about the progress...
  977. if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
  978. }
  979. if (!CheckConsistency())
  980. return false; // the check failed
  981. return true;
  982. }
  983. bool wxXmlGccInterface::ParseMethod(const wxXmlNode *p,
  984. const wxTypeIdHashMap& types,
  985. wxMethod& m)
  986. {
  987. // get the real name
  988. wxString name = p->GetAttribute("name").Strip(wxString::both);
  989. if (p->GetName() == "Destructor")
  990. name = "~" + name;
  991. else if (p->GetName() == "OperatorMethod")
  992. name = "operator" + name;
  993. // resolve return type
  994. wxType ret;
  995. unsigned long retid = 0;
  996. if (!getID(&retid, p->GetAttribute("returns")) || retid == 0)
  997. {
  998. if (p->GetName() != "Destructor" && p->GetName() != "Constructor") {
  999. wxLogError("Empty return ID for method '%s', with ID '%s'",
  1000. name, p->GetAttribute("id"));
  1001. return false;
  1002. }
  1003. }
  1004. else
  1005. {
  1006. wxTypeIdHashMap::const_iterator retidx = types.find(retid);
  1007. if (retidx == types.end()) {
  1008. wxLogError("Could not find return type ID '%s'", retid);
  1009. return false;
  1010. }
  1011. ret = wxType(retidx->second);
  1012. if (!ret.IsOk()) {
  1013. wxLogError("Invalid return type '%s' for method '%s', with ID '%s'",
  1014. retidx->second, name, p->GetAttribute("id"));
  1015. return false;
  1016. }
  1017. }
  1018. // resolve argument types
  1019. wxArgumentTypeArray argtypes;
  1020. wxXmlNode *arg = p->GetChildren();
  1021. while (arg)
  1022. {
  1023. if (arg->GetName() == "Argument")
  1024. {
  1025. unsigned long id = 0;
  1026. if (!getID(&id, arg->GetAttribute("type")) || id == 0) {
  1027. wxLogError("Invalid argument type ID '%s' for method '%s' with ID %s",
  1028. arg->GetAttribute("type"), name, p->GetAttribute("id"));
  1029. return false;
  1030. }
  1031. wxTypeIdHashMap::const_iterator idx = types.find(id);
  1032. if (idx == types.end()) {
  1033. wxLogError("Could not find argument type ID '%s'", id);
  1034. return false;
  1035. }
  1036. argtypes.Add(wxArgumentType(idx->second,
  1037. arg->GetAttribute("default"),
  1038. arg->GetAttribute("name")));
  1039. }
  1040. arg = arg->GetNext();
  1041. }
  1042. m.SetReturnType(ret);
  1043. m.SetName(name);
  1044. m.SetArgumentTypes(argtypes);
  1045. m.SetConst(p->GetAttribute("const") == "1");
  1046. m.SetStatic(p->GetAttribute("static") == "1");
  1047. // NOTE: gccxml is smart enough to mark as virtual those functions
  1048. // which are declared virtual in base classes but don't have
  1049. // the "virtual" keyword explicitly indicated in the derived
  1050. // classes... so we don't need any further logic for virtuals
  1051. m.SetVirtual(p->GetAttribute("virtual") == "1");
  1052. m.SetPureVirtual(p->GetAttribute("pure_virtual") == "1");
  1053. m.SetDeprecated(p->GetAttribute("attributes") == "deprecated");
  1054. // decode access specifier
  1055. if (p->GetAttribute("access") == "public")
  1056. m.SetAccessSpecifier(wxMAS_PUBLIC);
  1057. else if (p->GetAttribute("access") == "protected")
  1058. m.SetAccessSpecifier(wxMAS_PROTECTED);
  1059. else if (p->GetAttribute("access") == "private")
  1060. m.SetAccessSpecifier(wxMAS_PRIVATE);
  1061. if (!m.IsOk()) {
  1062. wxLogError("The prototype '%s' is not valid!", m.GetAsString());
  1063. return false;
  1064. }
  1065. return true;
  1066. }
  1067. // ----------------------------------------------------------------------------
  1068. // wxXmlDoxygenInterface global helpers
  1069. // ----------------------------------------------------------------------------
  1070. static wxString GetTextFromChildren(const wxXmlNode *n)
  1071. {
  1072. wxString text;
  1073. // consider the tree
  1074. //
  1075. // <a><b>this</b> is a <b>string</b></a>
  1076. //
  1077. // <a>
  1078. // |- <b>
  1079. // | |- this
  1080. // |- is a
  1081. // |- <b>
  1082. // |- string
  1083. //
  1084. // unlike wxXmlNode::GetNodeContent() which would return " is a "
  1085. // this function returns "this is a string"
  1086. wxXmlNode *ref = n->GetChildren();
  1087. while (ref) {
  1088. if (ref->GetType() == wxXML_ELEMENT_NODE)
  1089. text += ref->GetNodeContent();
  1090. else if (ref->GetType() == wxXML_TEXT_NODE)
  1091. text += ref->GetContent();
  1092. else
  1093. wxLogWarning("Unexpected node type while getting text from '%s' node", n->GetName());
  1094. ref = ref->GetNext();
  1095. }
  1096. return text;
  1097. }
  1098. static bool HasTextNodeContaining(const wxXmlNode *parent, const wxString& name)
  1099. {
  1100. if (!parent)
  1101. return false;
  1102. wxXmlNode *p = parent->GetChildren();
  1103. while (p)
  1104. {
  1105. switch (p->GetType())
  1106. {
  1107. case wxXML_TEXT_NODE:
  1108. if (p->GetContent() == name)
  1109. return true;
  1110. break;
  1111. case wxXML_ELEMENT_NODE:
  1112. // recurse into this node...
  1113. if (HasTextNodeContaining(p, name))
  1114. return true;
  1115. break;
  1116. default:
  1117. // skip it
  1118. break;
  1119. }
  1120. p = p->GetNext();
  1121. }
  1122. return false;
  1123. }
  1124. static const wxXmlNode* FindNodeNamed(const wxXmlNode* parent, const wxString& name)
  1125. {
  1126. if (!parent)
  1127. return NULL;
  1128. const wxXmlNode *p = parent->GetChildren();
  1129. while (p)
  1130. {
  1131. if (p->GetName() == name)
  1132. return p; // found!
  1133. // search recursively in the children of this node
  1134. const wxXmlNode *ret = FindNodeNamed(p, name);
  1135. if (ret)
  1136. return ret;
  1137. p = p->GetNext();
  1138. }
  1139. return NULL;
  1140. }
  1141. int GetAvailabilityFor(const wxXmlNode *node)
  1142. {
  1143. // identify <onlyfor> custom XML tags
  1144. const wxXmlNode* onlyfor = FindNodeNamed(node, "onlyfor");
  1145. if (!onlyfor)
  1146. return wxPORT_UNKNOWN;
  1147. wxArrayString ports = wxSplit(onlyfor->GetNodeContent(), ',');
  1148. int nAvail = wxPORT_UNKNOWN;
  1149. for (unsigned int i=0; i < ports.GetCount(); i++)
  1150. {
  1151. if (!ports[i].StartsWith("wx")) {
  1152. wxLogError("unexpected port ID '%s'", ports[i]);
  1153. return false;
  1154. }
  1155. nAvail |= wxPlatformInfo::GetPortId(ports[i].Mid(2));
  1156. }
  1157. return nAvail;
  1158. }
  1159. // ----------------------------------------------------------------------------
  1160. // wxXmlDoxygenInterface
  1161. // ----------------------------------------------------------------------------
  1162. bool wxXmlDoxygenInterface::Parse(const wxString& filename)
  1163. {
  1164. wxXmlDocument index;
  1165. wxXmlNode *compound;
  1166. wxLogMessage("Parsing %s...", filename);
  1167. if (!index.Load(filename)) {
  1168. wxLogError("can't load %s", filename);
  1169. return false;
  1170. }
  1171. // start processing the index:
  1172. if (index.GetRoot()->GetName() != "doxygenindex") {
  1173. wxLogError("invalid root node for %s", filename);
  1174. return false;
  1175. }
  1176. /*
  1177. NB: we may need in future to do a version-check here if the
  1178. format of the XML generated by doxygen changes.
  1179. For now (doxygen version 1.5.5), this check is not required
  1180. since AFAIK the XML format never changed since it was introduced.
  1181. */
  1182. m_classes.Alloc(ESTIMATED_NUM_CLASSES);
  1183. // process files referenced by this index file
  1184. compound = index.GetRoot()->GetChildren();
  1185. while (compound)
  1186. {
  1187. if (compound->GetName() == "compound" &&
  1188. compound->GetAttribute("kind") == "class")
  1189. {
  1190. wxString refid = compound->GetAttribute("refid");
  1191. wxFileName fn(filename);
  1192. if (!ParseCompoundDefinition(fn.GetPath(wxPATH_GET_SEPARATOR) + refid + ".xml"))
  1193. return false;
  1194. }
  1195. compound = compound->GetNext();
  1196. }
  1197. //wxPrint("\n");
  1198. if (!CheckConsistency())
  1199. return false; // the check failed
  1200. return true;
  1201. }
  1202. bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
  1203. {
  1204. wxClassMemberIdHashMap parents;
  1205. wxXmlDocument doc;
  1206. wxXmlNode *child;
  1207. int nodes = 0;
  1208. if (g_verbose)
  1209. {
  1210. wxLogMessage("Parsing %s...", filename);
  1211. }
  1212. if (!doc.Load(filename)) {
  1213. wxLogError("can't load %s", filename);
  1214. return false;
  1215. }
  1216. // start processing this compound definition XML
  1217. if (doc.GetRoot()->GetName() != "doxygen") {
  1218. wxLogError("invalid root node for %s", filename);
  1219. return false;
  1220. }
  1221. // build a list of wx classes
  1222. child = doc.GetRoot()->GetChildren();
  1223. while (child)
  1224. {
  1225. if (child->GetName() == "compounddef" &&
  1226. child->GetAttribute("kind") == "class")
  1227. {
  1228. // parse this class
  1229. wxClass klass;
  1230. wxString absoluteFile, header;
  1231. wxXmlNode *subchild = child->GetChildren();
  1232. while (subchild)
  1233. {
  1234. // NOTE: when documenting functions using the //@{ and //@}
  1235. // tags to create function groups, doxygen puts the
  1236. // contained methods into a "user-defined" section
  1237. // so we _must_ use the "prot" attribute to distinguish
  1238. // public/protected methods from private ones and cannot
  1239. // rely on the kind="public" attribute of <sectiondef>
  1240. if (subchild->GetName() == "sectiondef")
  1241. {
  1242. wxXmlNode *membernode = subchild->GetChildren();
  1243. while (membernode)
  1244. {
  1245. const wxString& accessSpec = membernode->GetAttribute("prot");
  1246. // parse only public&protected functions:
  1247. if (membernode->GetName() == "memberdef" &&
  1248. membernode->GetAttribute("kind") == "function" &&
  1249. (accessSpec == "public" || accessSpec == "protected"))
  1250. {
  1251. wxMethod m;
  1252. if (!ParseMethod(membernode, m, header)) {
  1253. wxLogError("The method '%s' could not be added to class '%s'",
  1254. m.GetName(), klass.GetName());
  1255. return false;
  1256. }
  1257. if (accessSpec == "public")
  1258. m.SetAccessSpecifier(wxMAS_PUBLIC);
  1259. else if (accessSpec == "protected")
  1260. m.SetAccessSpecifier(wxMAS_PROTECTED);
  1261. else if (accessSpec == "private")
  1262. m.SetAccessSpecifier(wxMAS_PRIVATE);
  1263. if (absoluteFile.IsEmpty())
  1264. absoluteFile = header;
  1265. else if (header != absoluteFile)
  1266. {
  1267. wxLogError("Found inconsistency in the XML file '%s': "
  1268. "the method '%s' is documented in the "
  1269. "file '%s' but the other methods of the same "
  1270. "class are documented in the file '%s'",
  1271. filename, m.GetName(), header, absoluteFile);
  1272. return false;
  1273. }
  1274. klass.AddMethod(m);
  1275. }
  1276. membernode = membernode->GetNext();
  1277. }
  1278. // all methods of this class were taken from the header "absoluteFile":
  1279. klass.SetHeader(absoluteFile);
  1280. }
  1281. else if (subchild->GetName() == "compoundname")
  1282. {
  1283. klass.SetName(subchild->GetNodeContent());
  1284. }
  1285. /*else if (subchild->GetName() == "includes")
  1286. {
  1287. // NOTE: we'll get the header from the <location> tags
  1288. // scattered inside <memberdef> tags instead of
  1289. // this <includes> tag since it does not contain
  1290. // the absolute path of the header
  1291. klass.SetHeader(subchild->GetNodeContent());
  1292. }*/
  1293. else if (subchild->GetName() == "detaileddescription")
  1294. {
  1295. // identify <onlyfor> custom XML tags
  1296. klass.SetAvailability(GetAvailabilityFor(subchild));
  1297. }
  1298. else if (subchild->GetName() == "basecompoundref")
  1299. {
  1300. // add the name of this parent to the list of klass' parents
  1301. klass.AddParent(subchild->GetNodeContent());
  1302. }
  1303. subchild = subchild->GetNext();
  1304. }
  1305. // add a new class
  1306. if (klass.IsOk())
  1307. {
  1308. m_classes.Add(klass);
  1309. }
  1310. else if (g_verbose)
  1311. {
  1312. wxLogWarning("discarding class '%s' with %d methods...",
  1313. klass.GetName(), klass.GetMethodCount());
  1314. }
  1315. }
  1316. child = child->GetNext();
  1317. // give feedback to the user about the progress...
  1318. if ((++nodes%PROGRESS_RATE)==0) ShowProgress();
  1319. }
  1320. return true;
  1321. }
  1322. bool wxXmlDoxygenInterface::ParseMethod(const wxXmlNode* p, wxMethod& m, wxString& header)
  1323. {
  1324. wxArgumentTypeArray args;
  1325. long line;
  1326. wxXmlNode *child = p->GetChildren();
  1327. while (child)
  1328. {
  1329. if (child->GetName() == "name")
  1330. m.SetName(child->GetNodeContent());
  1331. else if (child->GetName() == "type")
  1332. m.SetReturnType(wxType(GetTextFromChildren(child)));
  1333. else if (child->GetName() == "param")
  1334. {
  1335. wxString typestr, namestr, defstr, arrstr;
  1336. wxXmlNode *n = child->GetChildren();
  1337. while (n)
  1338. {
  1339. if (n->GetName() == "type")
  1340. // if the <type> node has children, they should be all TEXT and <ref> nodes
  1341. // and we need to take the text they contain, in the order they appear
  1342. typestr = GetTextFromChildren(n);
  1343. else if (n->GetName() == "declname")
  1344. namestr = GetTextFromChildren(n);
  1345. else if (n->GetName() == "defval")
  1346. defstr = GetTextFromChildren(n).Strip(wxString::both);
  1347. else if (n->GetName() == "array")
  1348. arrstr = GetTextFromChildren(n);
  1349. n = n->GetNext();
  1350. }
  1351. if (typestr.IsEmpty()) {
  1352. wxLogError("cannot find type node for a param in method '%s'", m.GetName());
  1353. return false;
  1354. }
  1355. wxArgumentType newarg(typestr + arrstr, defstr, namestr);
  1356. // can we use preprocessor output to transform the default value
  1357. // into the same form which gets processed by wxXmlGccInterface?
  1358. wxStringHashMap::const_iterator it = m_preproc.find(defstr);
  1359. if (it != m_preproc.end())
  1360. newarg.SetDefaultValue(defstr, it->second);
  1361. args.Add(newarg);
  1362. }
  1363. else if (child->GetName() == "location")
  1364. {
  1365. line = -1;
  1366. if (child->GetAttribute("line").ToLong(&line))
  1367. m.SetLocation((int)line);
  1368. header = child->GetAttribute("file");
  1369. }
  1370. else if (child->GetName() == "detaileddescription")
  1371. {
  1372. // when a method has a @deprecated tag inside its description,
  1373. // Doxygen outputs somewhere nested inside <detaileddescription>
  1374. // a <xreftitle>Deprecated</xreftitle> tag.
  1375. m.SetDeprecated(HasTextNodeContaining(child, "Deprecated"));
  1376. // identify <onlyfor> custom XML tags
  1377. m.SetAvailability(GetAvailabilityFor(child));
  1378. }
  1379. child = child->GetNext();
  1380. }
  1381. m.SetArgumentTypes(args);
  1382. m.SetConst(p->GetAttribute("const")=="yes");
  1383. m.SetStatic(p->GetAttribute("static")=="yes");
  1384. // NOTE: Doxygen is smart enough to mark as virtual those functions
  1385. // which are declared virtual in base classes but don't have
  1386. // the "virtual" keyword explicitly indicated in the derived
  1387. // classes... so we don't need any further logic for virtuals
  1388. m.SetVirtual(p->GetAttribute("virt")=="virtual");
  1389. m.SetPureVirtual(p->GetAttribute("virt")=="pure-virtual");
  1390. if (!m.IsOk()) {
  1391. wxLogError("The prototype '%s' is not valid!", m.GetAsString());
  1392. return false;
  1393. }
  1394. return true;
  1395. }