-/////////////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////////////
// Name: xmlparser.cpp
// Purpose: Parser of the API/interface XML files
// Author: Francesco Montorsi
// for all others, include the necessary headers
#ifndef WX_PRECOMP
+ #include "wx/crt.h"
#endif
#include "wx/xml/xml.h"
// defined in ifacecheck.cpp
extern bool g_verbose;
+// global variable:
+bool g_bLogEnabled = true;
+
// ----------------------------------------------------------------------------
which works at char-level and does everything in a single pass
*/
+ // clean the type string
+ // ---------------------
+
m_strType = t;
// [] is the same as * for gccxml
m_strType.Replace("[]", "*");
m_strType.Replace("long int", "long"); // in wx typically we never write "long int", just "long"
+ m_strType.Replace("long unsigned int", "unsigned long");
// make sure the * and & operator always use the same spacing rules
// (to make sure GetAsString() output is always consistent)
m_strType = m_strType.Strip(wxString::both);
- // now set the clean version
- m_strTypeClean = m_strType;
+
+
+ // clean the type string (this time for the comparison)
+ // ----------------------------------------------------
+
+ m_strTypeClean = m_strType; // begin with the already-cleaned string
m_strTypeClean.Replace("const", "");
m_strTypeClean.Replace("static", "");
m_strTypeClean.Replace("*", "");
// need to be considered as the same type
if (m_strTypeClean.EndsWith("Base"))
m_strTypeClean = m_strTypeClean.Left(m_strTypeClean.Len()-4);
+
+ // ADHOC-FIX:
+ // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
+ // fix this to avoid false positives
+ m_strTypeClean.Replace("wxDateTime::", "");
+ m_strTypeClean.Replace("wxStockGDI::", ""); // same story for some other classes
+ m_strTypeClean.Replace("wxHelpEvent::", "");
}
bool wxType::IsOk() const
IsReference() == m.IsReference())
return true;
+ if (g_verbose)
+ {
+ LogMessage("Type '%s' does not match type '%s'", m_strType, m.m_strType);
+ LogMessage(" => TypeClean %s / %s; IsConst %d / %d; IsStatic %d / %d; IsPointer %d / %d; IsReference %d / %d",
+ m_strTypeClean, m.m_strTypeClean, IsConst(), m.IsConst(),
+ IsStatic(), m.IsStatic(), IsPointer(), m.IsPointer(),
+ IsReference(), m.IsReference());
+ }
+
return false;
}
void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& defvalForCmp)
{
- m_strDefaultValue=defval.Strip(wxString::both);
- m_strDefaultValueForCmp=defvalForCmp.Strip(wxString::both);
-
- // in order to make valid&simple comparison on argument defaults,
- // we reduce some of the multiple forms in which the same things may appear
- // to a single form:
- if (m_strDefaultValue == "0u")
- m_strDefaultValue = "0";
-/*
- if (IsPointer())
- m_strDefaultValue.Replace("0", "NULL");
- else
- m_strDefaultValue.Replace("NULL", "0");
-*/
+ m_strDefaultValue = defval.Strip(wxString::both);
+ m_strDefaultValueForCmp = defvalForCmp.IsEmpty() ?
+ m_strDefaultValue : defvalForCmp.Strip(wxString::both);
+
+
+ // clean the default argument strings
+ // ----------------------------------
+
+ // Note: we adjust the aesthetic form of the m_strDefaultValue string for the "modify mode"
+ // of ifacecheck: we may need to write it out in an interface header
+
+ wxString *p;
+ for (int i=0; i<2; i++) // to avoid copying&pasting the code!
+ {
+ if (i == 0) p = &m_strDefaultValue;
+ if (i == 1) p = &m_strDefaultValueForCmp;
+
+ if (*p == "0u") *p = "0";
+
+ p->Replace("0x000000001", "1");
+ p->Replace("\\000\\000\\000", ""); // fix for unicode strings:
+
+ // ADHOC-FIX: for wxConv* default values
+ p->Replace("wxConvAuto(wxFONTENCODING_DEFAULT)", "wxConvAuto()");
+ p->Replace("wxGet_wxConvUTF8()", "wxConvUTF8");
+ p->Replace("wxGet_wxConvLocal()", "wxConvLocal");
+ }
+
+
+ // clean ONLY the default argument string specific for comparison
+ // --------------------------------------------------------------
+
+ if (m_strDefaultValueForCmp.StartsWith("wxT(") &&
+ m_strDefaultValueForCmp.EndsWith(")"))
+ {
+ // get rid of the wxT() part
+ unsigned int len = m_strDefaultValueForCmp.Len();
+ m_strDefaultValueForCmp = m_strDefaultValueForCmp.Mid(4,len-5);
+ }
- if (m_strDefaultValue.Contains("wxGetTranslation"))
- m_strDefaultValue = "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
+ // ADHOC-FIX:
+ // doxygen likes to put wxDateTime:: in front of all wxDateTime enums;
+ // fix this to avoid false positives
+ m_strDefaultValueForCmp.Replace("wxDateTime::", "");
+ m_strDefaultValueForCmp.Replace("wxStockGDI::", ""); // same story for some other classes
+ m_strDefaultValueForCmp.Replace("wxHelpEvent::", ""); // same story for some other classes
+ m_strDefaultValueForCmp.Replace("* GetColour(COLOUR_BLACK)", "*wxBLACK");
+
+ // ADHOC-FIX:
+ if (m_strDefaultValueForCmp.Contains("wxGetTranslation"))
+ m_strDefaultValueForCmp = "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
}
bool wxArgumentType::operator==(const wxArgumentType& m) const
if ((const wxType&)(*this) != (const wxType&)m)
return false;
- const wxString& def1 = m_strDefaultValueForCmp.IsEmpty() ? m_strDefaultValue : m_strDefaultValueForCmp;
- const wxString& def2 = m.m_strDefaultValueForCmp.IsEmpty() ? m.m_strDefaultValue : m.m_strDefaultValueForCmp;
+ // ADHOC-FIX:
+ // default values for style attributes of wxWindow-derived classes in gccxml appear as raw
+ // numbers; avoid false positives in this case!
+ if (m_strArgName == m.m_strArgName && m_strArgName == "style" &&
+ (m_strDefaultValueForCmp.IsNumber() || m.m_strDefaultValueForCmp.IsNumber()))
+ return true;
- if (def1 != def2)
+ // fix for default values which were replaced by gcc-xml with their numeric values
+ // (at this point we know that m_strTypeClean == m.m_strTypeClean):
+ if (m_strTypeClean == "long" || m_strTypeClean == "int")
+ {
+ if ((m_strDefaultValueForCmp.IsNumber() && m.m_strDefaultValueForCmp.StartsWith("wx")) ||
+ (m.m_strDefaultValueForCmp.IsNumber() && m_strDefaultValueForCmp.StartsWith("wx")))
+ {
+ if (g_verbose)
+ LogMessage("Supposing '%s' default value to be the same of '%s'...",
+ m_strDefaultValueForCmp, m.m_strDefaultValueForCmp);
+
+ return true;
+ }
+ }
+
+ if (m_strDefaultValueForCmp != m.m_strDefaultValueForCmp)
{
// maybe the default values are numbers.
// in this case gccXML gives as default values things like '-0x0000001' instead of just '-1'.
// To handle these cases, we try to convert the default value strings to numbers:
long def1val, def2val;
- if (def1.ToLong(&def1val, 0 /* auto-detect */) &&
- def2.ToLong(&def2val, 0 /* auto-detect */))
+ if (m_strDefaultValueForCmp.ToLong(&def1val, 0 /* auto-detect */) &&
+ m.m_strDefaultValueForCmp.ToLong(&def2val, 0 /* auto-detect */))
{
if (def1val == def2val)
return true; // the default values match
}
+ if (g_verbose)
+ LogMessage("Argument type '%s = %s' has different default value from '%s = %s'",
+ m_strType, m_strDefaultValueForCmp, m.m_strType, m.m_strDefaultValueForCmp);
return false;
}
return true;
}
-bool wxMethod::operator==(const wxMethod& m) const
+bool wxMethod::MatchesExceptForAttributes(const wxMethod& m) const
{
if (GetReturnType() != m.GetReturnType() ||
- GetName() != m.GetName() ||
- IsConst() != m.IsConst() ||
- IsStatic() != m.IsStatic() ||
- IsVirtual() != m.IsVirtual() ||
- IsPureVirtual() != m.IsPureVirtual() ||
- IsDeprecated() != m.IsDeprecated())
+ GetName() != m.GetName())
+ {
+ if (g_verbose)
+ LogMessage("The method '%s' does not match method '%s'; different names/rettype", GetName(), m.GetName());
return false;
+ }
- if (m_args.GetCount()!=m.m_args.GetCount())
+ if (m_args.GetCount()!=m.m_args.GetCount()) {
+ if (g_verbose)
+ LogMessage("Method '%s' has %d arguments while '%s' has %d arguments",
+ m_strName, m_args.GetCount(), m_strName, m.m_args.GetCount());
return false;
+ }
+ // compare argument types
for (unsigned int i=0; i<m_args.GetCount(); i++)
if (m_args[i] != m.m_args[i])
return false;
return true;
}
-wxString wxMethod::GetAsString(bool bWithArgumentNames) const
+bool wxMethod::ActsAsDefaultCtor() const
+{
+ if (!IsCtor())
+ return false;
+
+ for (unsigned int i=0; i<m_args.GetCount(); i++)
+ if (!m_args[i].HasDefaultValue())
+ return false;
+
+ return true;
+}
+
+bool wxMethod::operator==(const wxMethod& m) const
+{
+ // check attributes
+ if (IsConst() != m.IsConst() ||
+ IsStatic() != m.IsStatic() ||
+ IsVirtual() != m.IsVirtual() ||
+ IsPureVirtual() != m.IsPureVirtual() ||
+ IsDeprecated() != m.IsDeprecated() ||
+ GetAccessSpecifier() != m.GetAccessSpecifier())
+ {
+ if (g_verbose)
+ LogMessage("The method '%s' does not match method '%s'; different attributes", GetName(), m.GetName());
+
+ return false;
+ }
+
+ // check everything else
+ return MatchesExceptForAttributes(m);
+}
+
+wxString wxMethod::GetAsString(bool bWithArgumentNames, bool bCleanDefaultValues,
+ bool bDeprecated, bool bAccessSpec) const
{
wxString ret;
+ // NOTE: for return and argument types, never use wxType::GetAsCleanString
+ // since in that way we'd miss important decorators like &,*,const etc
+
if (m_retType!=wxEmptyType)
ret += m_retType.GetAsString() + " ";
//else; this is a ctor or dtor
if (bWithArgumentNames && !name.IsEmpty())
ret += " " + name;
- const wxString& def = m_args[i].GetDefaultValue();
+ const wxString& def = bCleanDefaultValues ?
+ m_args[i].GetDefaultCleanValue() : m_args[i].GetDefaultValue();
if (!def.IsEmpty())
ret += " = " + def;
if (m_bVirtual || m_bPureVirtual)
ret = "virtual " + ret;
if (m_bPureVirtual)
- ret = ret + " = 0";
+ ret += " = 0";
+ if (m_bDeprecated && bDeprecated)
+ ret += " [deprecated]";
- // in doxygen headers we don't need wxDEPRECATED:
- //if (m_bDeprecated)
- // ret = "wxDEPRECATED( " + ret + " )";
+ if (bAccessSpec)
+ {
+ switch (m_access)
+ {
+ case wxMAS_PUBLIC:
+ ret += " [public]";
+ break;
+ case wxMAS_PROTECTED:
+ ret += " [protected]";
+ break;
+ case wxMAS_PRIVATE:
+ ret += " [private]";
+ break;
+ }
+ }
return ret;
}
LogError("class %s has two methods with the same prototype: '%s'",
m_strName, m_methods[i].GetAsString());
return false;
- ((wxClass*)this)->m_methods.RemoveAt(j);
- j--;
+
+ // fix the problem?
+ //((wxClass*)this)->m_methods.RemoveAt(j);
+ //j--;
}
return true;
return NULL;
}
+const wxMethod* wxClass::RecursiveUpwardFindMethod(const wxMethod& m,
+ const wxXmlInterface* allclasses) const
+{
+ // first, search into *this
+ const wxMethod* ret = FindMethod(m);
+ if (ret)
+ return ret;
+
+ // then, search into its parents
+ for (unsigned int i=0; i<m_parents.GetCount(); i++)
+ {
+ // ignore non-wx-classes parents
+ // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
+ if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
+ {
+ const wxClass *parent = allclasses->FindClass(m_parents[i]);
+ if (!parent) {
+ LogError("Could not find parent '%s' of class '%s'...",
+ m_parents[i], GetName());
+ return false;
+ }
+
+ const wxMethod *parentMethod = parent->RecursiveUpwardFindMethod(m, allclasses);
+ if (parentMethod)
+ return parentMethod;
+ }
+ }
+
+ // could not find anything even in parent classes...
+ return NULL;
+}
+
wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const
{
wxMethodPtrArray ret;
}
+wxMethodPtrArray wxClass::RecursiveUpwardFindMethodsNamed(const wxString& name,
+ const wxXmlInterface* allclasses) const
+{
+ // first, search into *this
+ wxMethodPtrArray ret = FindMethodsNamed(name);
+ if (ret.GetCount()>0)
+ return ret; // stop here, don't look upward in the parents
+
+ // then, search into parents of this class
+ for (unsigned int i=0; i<m_parents.GetCount(); i++)
+ {
+ // AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
+ if (m_parents[i].StartsWith("wx") && m_parents[i] != "wxScrolledT_Helper")
+ {
+ const wxClass *parent = allclasses->FindClass(m_parents[i]);
+ if (!parent) {
+ LogError("Could not find parent '%s' of class '%s'...",
+ m_parents[i], GetName());
+ return false;
+ }
+
+ wxMethodPtrArray temp = parent->RecursiveUpwardFindMethodsNamed(name, allclasses);
+ WX_APPEND_ARRAY(ret, temp);
+ }
+ }
+
+ return ret;
+}
+
+
+
// ----------------------------------------------------------------------------
// wxXmlInterface
// ----------------------------------------------------------------------------
#define ATTRIB_POINTER 4
#define ATTRIB_ARRAY 8
+// it may sound strange but gccxml, in order to produce shorter ID names
+// uses (after the underscore) characters in range 0-9 and a-z in the ID names;
+// in order to be able to translate such strings into numbers using strtoul()
+// we use as base 10 (possible digits) + 25 (possible characters) = 35
#define GCCXML_BASE 35
class toResolveTypeItem
WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
wxIntegerHash, wxIntegerEqual,
wxClassMemberIdHashMap );
+
#else
#include <map>
typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap;
// utility to parse gccXML ID values;
// this function is equivalent to wxString(str).Mid(1).ToULong(&id, GCCXML_BASE)
// but is a little bit faster
-bool getID(unsigned long *id, const wxStringCharType* str)
+bool getID(unsigned long *id, const wxString& str)
{
+ const wxStringCharType * const start = str.wx_str()+1;
wxStringCharType *end;
#if wxUSE_UNICODE_WCHAR
- unsigned long val = wcstoul(str+1, &end, GCCXML_BASE);
+ unsigned long val = wcstoul(start, &end, GCCXML_BASE);
#else
- unsigned long val = strtoul(str+1, &end, GCCXML_BASE);
+ unsigned long val = strtoul(start, &end, GCCXML_BASE);
#endif
// return true only if scan was stopped by the terminating NUL and
// if the string was not empty to start with and no under/overflow
// occurred:
- if ( *end != '\0' || end == str+1 || errno == ERANGE || errno == EINVAL )
+ if ( *end != '\0' || end == start || errno == ERANGE || errno == EINVAL )
return false;
*id = val;
// utility specialized to parse efficiently the gccXML list of IDs which occur
// in nodes like <Class> ones... i.e. numeric values separed by " _" token
-bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxStringCharType* str)
+bool getMemberIDs(wxClassMemberIdHashMap* map, wxClass* p, const wxString& str)
{
+ const wxStringCharType * const start = str.wx_str();
#if wxUSE_UNICODE_WCHAR
- size_t len = wcslen(str);
+ size_t len = wcslen(start);
#else
- size_t len = strlen(str);
+ size_t len = strlen(start);
#endif
- if (len == 0 || str[0] != '_')
+ if (len == 0 || start[0] != '_')
return false;
- const wxStringCharType *curpos = str,
- *end = str + len;
+ const wxStringCharType *curpos = start,
+ *end = start + len;
wxStringCharType *nexttoken;
while (curpos < end)
// NB: "file" attribute contains an ID value that we'll resolve later
m_classes.Add(wxClass(cname, child->GetAttribute("file")));
+ // the just-inserted class:
+ wxClass *newClass = &m_classes.Last();
+
+ // now get a list of the base classes:
+ wxXmlNode *baseNode = child->GetChildren();
+ while (baseNode)
+ {
+ // for now we store as "parents" only the parent IDs...
+ // later we will resolve them into full class names
+ if (baseNode->GetName() == "Base")
+ newClass->AddParent(baseNode->GetAttribute("type"));
+
+ baseNode = baseNode->GetNext();
+ }
+
const wxString& ids = child->GetAttribute("members");
if (ids.IsEmpty())
{
else
{
// decode the non-empty list of IDs:
- if (!getMemberIDs(&members, &m_classes.Last(), ids)) {
+ if (!getMemberIDs(&members, newClass, ids)) {
LogError("Invalid member IDs for '%s' class node: %s",
cname, child->GetAttribute("id"));
return false;
m_classes[i].SetHeader(idx->second);
}
+ // resolve parent names
+ for (unsigned int i=0; i<m_classes.GetCount(); i++)
+ {
+ for (unsigned int k=0; k<m_classes[i].GetParentCount(); k++)
+ {
+ unsigned long id;
+
+ if (!getID(&id, m_classes[i].GetParent(k))) {
+ LogError("invalid parent class ID for '%s'", m_classes[i].GetName());
+ return false;
+ }
+
+ wxTypeIdHashMap::const_iterator idx = types.find(id);
+ if (idx == types.end())
+ {
+ // this is an error!
+ LogError("couldn't find parent class ID '%d'", id);
+ }
+ else
+ // replace k-th parent with its true name:
+ m_classes[i].SetParent(k, idx->second);
+ }
+ }
+
// build the list of the wx methods
child = doc.GetRoot()->GetChildren();
while (child)
{
- wxString n = child->GetName();
+ wxString n = child->GetName(), acc = child->GetAttribute("access");
- // only register public methods
- if (child->GetAttribute("access") == "public" &&
+ // only register public&protected methods
+ if ((acc == "public" || acc == "protected") &&
(n == "Method" || n == "Constructor" || n == "Destructor" || n == "OperatorMethod"))
{
unsigned long id = 0;
return false;
}
+ // do some additional check that we can do only here:
+
if (newfunc.IsCtor() && !p->IsValidCtorForThisClass(newfunc)) {
LogError("The method '%s' does not seem to be a ctor for '%s'",
newfunc.GetName(), p->GetName());
return false;
}
- argtypes.Add(wxArgumentType(idx->second, arg->GetAttribute("default")));
+ argtypes.Add(wxArgumentType(idx->second,
+ arg->GetAttribute("default"),
+ arg->GetAttribute("name")));
}
arg = arg->GetNext();
m.SetPureVirtual(p->GetAttribute("pure_virtual") == "1");
m.SetDeprecated(p->GetAttribute("attributes") == "deprecated");
+ // decode access specifier
+ if (p->GetAttribute("access") == "public")
+ m.SetAccessSpecifier(wxMAS_PUBLIC);
+ else if (p->GetAttribute("access") == "protected")
+ m.SetAccessSpecifier(wxMAS_PROTECTED);
+ else if (p->GetAttribute("access") == "private")
+ m.SetAccessSpecifier(wxMAS_PRIVATE);
+
if (!m.IsOk()) {
LogError("The prototype '%s' is not valid!", m.GetAsString());
return false;
bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
{
+ wxClassMemberIdHashMap parents;
wxXmlDocument doc;
wxXmlNode *child;
int nodes = 0;
wxXmlNode *subchild = child->GetChildren();
while (subchild)
{
+ wxString kind = subchild->GetAttribute("kind");
+
+ // parse only public&protected functions:
if (subchild->GetName() == "sectiondef" &&
- subchild->GetAttribute("kind") == "public-func")
+ (kind == "public-func" || kind == "protected-func"))
{
wxXmlNode *membernode = subchild->GetChildren();
return false;
}
+ if (kind == "public-func")
+ m.SetAccessSpecifier(wxMAS_PUBLIC);
+ else if (kind == "protected-func")
+ m.SetAccessSpecifier(wxMAS_PROTECTED);
+ else if (kind == "private-func")
+ m.SetAccessSpecifier(wxMAS_PRIVATE);
+
if (absoluteFile.IsEmpty())
absoluteFile = header;
else if (header != absoluteFile)
// identify <onlyfor> custom XML tags
klass.SetAvailability(GetAvailabilityFor(subchild));
}
+ else if (subchild->GetName() == "basecompoundref")
+ {
+ // add the name of this parent to the list of klass' parents
+ klass.AddParent(subchild->GetNodeContent());
+ }
subchild = subchild->GetNext();
}