Urho3D/Source/Tools/BindingGenerator/XmlAnalyzer.cpp

1436 lines
36 KiB
C++

//
// Copyright (c) 2008-2021 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "Utils.h"
#include "XmlAnalyzer.h"
#include "XmlSourceData.h"
#include <cassert>
#include <regex>
extern string _sourceDir;
string RemoveRefs(xml_node node)
{
assert(node.name() == string("type") || node.name() == string("defval") || node.name() == string("para"));
string result;
for (xml_node part : node.children())
{
if (part.name() == string("ref"))
result += part.child_value();
else
result += part.value();
}
return result;
}
TypeAnalyzer::TypeAnalyzer(xml_node type, const TemplateSpecialization& specialization)
{
assert(type.name() == string("type"));
fullType_ = RemoveRefs(type);
fullType_ = RemoveFirst(fullType_, "URHO3D_API ");
fullType_ = RemoveFirst(fullType_, " URHO3D_API");
fullType_ = CutStart(fullType_, "volatile ");
fullType_ = CutStart(fullType_, "constexpr ");
fullType_ = ReplaceAll(fullType_, " *", "*");
fullType_ = ReplaceAll(fullType_, " &", "&");
fullType_ = ReplaceAll(fullType_, "< ", "<");
fullType_ = ReplaceAll(fullType_, " >", ">");
for (pair<const string, string> it : specialization)
{
regex rgx("\\b" + it.first + "\\b");
fullType_ = regex_replace(fullType_, rgx, it.second);
}
isConst_ = StartsWith(fullType_, "const ");
name_ = CutStart(fullType_, "const ");
isRvalueReference_ = EndsWith(name_, "&&");
name_ = CutEnd(name_, "&&");
isRefToPoiner_ = EndsWith(name_, "*&");
name_ = CutEnd(name_, "*&");
isDoublePointer_ = EndsWith(name_, "**");
name_ = CutEnd(name_, "**");
isPointer_ = EndsWith(name_, "*");
name_ = CutEnd(name_, "*");
isReference_ = EndsWith(name_, "&");
name_ = CutEnd(name_, "&");
smatch match;
if (regex_match(name_, match, regex("(.+?)<(.+)>")))
{
templateParams_ = match[2].str();
name_ = match[1].str();
}
}
TypeAnalyzer::TypeAnalyzer(const string& typeName)
{
fullType_ = typeName;
name_ = typeName;
isConst_ = false;
isPointer_ = false;
isReference_ = false;
isRvalueReference_ = false;
isDoublePointer_ = false;
isRefToPoiner_ = false;
}
// ============================================================================
ParamAnalyzer::ParamAnalyzer(xml_node param, const TemplateSpecialization& specialization)
: node_(param)
, specialization_(specialization)
{
assert(node_.name() == string("param"));
}
string ParamAnalyzer::ToString() const
{
string type = GetType().ToString();
assert(!type.empty());
string name = GetDeclname();
assert(!name.empty());
return type + " " + name;
}
TypeAnalyzer ParamAnalyzer::GetType() const
{
xml_node type = node_.child("type");
assert(type);
return TypeAnalyzer(type, specialization_);
}
string ParamAnalyzer::GetDeclname() const
{
string result = node_.child("declname").child_value();
//assert(!result.empty()); TODO
return result;
}
string ParamAnalyzer::GetDefval() const
{
xml_node defval = node_.child("defval");
if (defval)
return RemoveRefs(defval);
return "";
}
// ============================================================================
string ExtractCompoundname(xml_node compounddef)
{
assert(IsCompounddef(compounddef));
string result = compounddef.child("compoundname").child_value();
assert(!result.empty());
return result;
}
xml_node FindSectiondef(xml_node compounddef, const string& kind)
{
assert(IsCompounddef(compounddef));
assert(!kind.empty());
for (xml_node sectiondef : compounddef.children("sectiondef"))
{
if (ExtractKind(sectiondef) == kind)
return sectiondef;
}
return xml_node();
}
bool IsStatic(xml_node memberdef)
{
assert(IsMemberdef(memberdef));
string staticAttr = memberdef.attribute("static").value();
assert(!staticAttr.empty());
return staticAttr == "yes";
}
bool IsExplicit(xml_node memberdef)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "function");
string explicitAttr = memberdef.attribute("explicit").value();
assert(!explicitAttr.empty());
return explicitAttr == "yes";
}
string ExtractDefinition(xml_node memberdef)
{
assert(IsMemberdef(memberdef));
string result = memberdef.child("definition").child_value();
assert(!result.empty());
return result;
}
static string BeautifyDefinition(const string& definition)
{
string result = definition;
result = ReplaceAll(result, " **", "** ");
result = ReplaceAll(result, " &&", "&& ");
result = ReplaceAll(result, " *&", "*& ");
result = ReplaceAll(result, " *", "* ");
result = ReplaceAll(result, " &", "& ");
result = ReplaceAll(result, "=", " = ");
result = ReplaceAll(result, " = = ", "==");
result = ReplaceAll(result, "! = ", "!=");
result = ReplaceAll(result, "+ = ", "+=");
result = ReplaceAll(result, "- = ", "-=");
result = ReplaceAll(result, "* = ", "*=");
result = ReplaceAll(result, "/ = ", "/=");
result = ReplaceAll(result, "operator = ", "operator=");
result = ReplaceAll(result, "operator", "operator ");
while (Contains(result, " "))
result = ReplaceAll(result, " ", " ");
result = ReplaceAll(result, " )", ")");
result = ReplaceAll(result, "< ", "<");
result = ReplaceAll(result, " >", ">");
result = ReplaceAll(result, "template<", "template <");
return result;
}
string ExtractArgsstring(xml_node memberdef)
{
assert(IsMemberdef(memberdef));
xml_node argsstring = memberdef.child("argsstring");
assert(argsstring);
return argsstring.child_value();
}
string ExtractCleanedFunctionArgsstring(xml_node memberdef)
{
assert(ExtractKind(memberdef) == "function");
string argsstring = ExtractArgsstring(memberdef);
assert(StartsWith(argsstring, "("));
size_t endPos = argsstring.find_last_of(')');
assert(endPos != string::npos);
return argsstring.substr(1, endPos - 1);
}
string ExtractProt(xml_node memberdef)
{
assert(IsMemberdef(memberdef));
string result = memberdef.attribute("prot").value();
assert(!result.empty());
return result;
}
TypeAnalyzer ExtractType(xml_node memberdef, const TemplateSpecialization& specialization)
{
assert(IsMemberdef(memberdef));
xml_node type = memberdef.child("type");
assert(type);
// Doxygen bug workaround https://github.com/doxygen/doxygen/issues/7732
// For user-defined conversion operator exract return type from function name
if (RemoveRefs(type).empty() && ExtractKind(memberdef) == "function")
{
string functionName = ExtractName(memberdef);
if (StartsWith(functionName, "operator "))
return TypeAnalyzer(CutStart(functionName, "operator "));
}
return TypeAnalyzer(type, specialization);
}
vector<ParamAnalyzer> ExtractParams(xml_node memberdef, const TemplateSpecialization& specialization)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "function");
vector<ParamAnalyzer> result;
for (xml_node param : memberdef.children("param"))
result.push_back(ParamAnalyzer(param, specialization));
return result;
}
string JoinParamsTypes(xml_node memberdef, const TemplateSpecialization& specialization)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "function");
string result;
vector<ParamAnalyzer> params = ExtractParams(memberdef, specialization);
for (const ParamAnalyzer& param : params)
{
if (!result.empty())
result += ", ";
result += param.GetType().ToString();
}
return result;
}
string JoinParamsNames(xml_node memberdef, bool skipContext)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "function");
string result;
vector<ParamAnalyzer> params = ExtractParams(memberdef);
for (size_t i = 0; i < params.size(); i++)
{
ParamAnalyzer param = params[i];
if (skipContext && i == 0)
{
assert(param.GetType().ToString() == "Context*");
continue;
}
if (!result.empty())
result += ", ";
result += param.GetDeclname();
}
return result;
}
string ExtractID(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node));
string result = node.attribute("id").value();
assert(!result.empty());
return result;
}
string ExtractKind(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node) || IsMember(node) || IsSectiondef(node));
string result = node.attribute("kind").value();
assert(!result.empty());
return result;
}
string ExtractName(xml_node node)
{
assert(IsMemberdef(node) || IsEnumvalue(node));
string result = node.child("name").child_value();
assert(!result.empty());
return result;
}
string ExtractLine(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node));
string result = node.child("location").attribute("line").value();
assert(!result.empty());
return result;
}
string ExtractColumn(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node));
string result = node.child("location").attribute("column").value();
assert(!result.empty());
return result;
}
static string DescriptionToString(xml_node description)
{
string result;
for (xml_node para : description.children("para"))
{
result += RemoveRefs(para);
result += " "; // To avoid gluing words from different paragraphs
}
return result;
}
string ExtractComment(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node));
xml_node brief = node.child("briefdescription");
assert(brief);
xml_node detailed = node.child("detaileddescription");
assert(detailed);
return DescriptionToString(brief) + DescriptionToString(detailed);
}
static string HeaderFullPathToRelative(const string& fullPath)
{
assert(StartsWith(fullPath, _sourceDir + "/Source/Urho3D"));
return ".." + CutStart(fullPath, _sourceDir + "/Source/Urho3D");
}
string ExtractHeaderFile(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node));
xml_node location = node.child("location");
assert(!location.empty());
string declfile = location.attribute("declfile").value();
if (EndsWith(declfile, ".h"))
return HeaderFullPathToRelative(declfile);
string file = location.attribute("file").value();
if (EndsWith(file, ".h"))
return HeaderFullPathToRelative(file);
return string();
}
bool IsTemplate(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node));
return node.child("templateparamlist");
}
vector<string> ExtractTemplateParams(xml_node node)
{
assert(IsMemberdef(node) || IsCompounddef(node));
vector<string> result;
xml_node templateparamlist = node.child("templateparamlist");
for (xml_node param : templateparamlist.children("param"))
{
string type = param.child_value("type");
type = CutStart(type, "class ");
type = CutStart(type, "typename ");
result.push_back(type);
}
return result;
}
// ============================================================================
EnumAnalyzer::EnumAnalyzer(xml_node memberdef)
: memberdef_(memberdef)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "enum");
}
string EnumAnalyzer::GetBaseType() const
{
string result = memberdef_.child("type").child_value();
if (result.empty())
return "int";
return result;
}
string EnumAnalyzer::GetLocation() const
{
string baseType = GetBaseType();
if (baseType == "int")
return "enum " + GetTypeName() + " | File: " + GetHeaderFile();
return "enum " + GetTypeName() + " : " + baseType + " | File: " + GetHeaderFile();
}
vector<string> EnumAnalyzer::GetEnumerators() const
{
vector<string> result;
for (xml_node enumvalue : memberdef_.children("enumvalue"))
result.push_back(ExtractName(enumvalue));
return result;
}
// ============================================================================
GlobalVariableAnalyzer::GlobalVariableAnalyzer(xml_node memberdef)
: memberdef_(memberdef)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "variable");
}
// ============================================================================
ClassAnalyzer::ClassAnalyzer(xml_node compounddef, const TemplateSpecialization& specialization)
: compounddef_(compounddef)
, specialization_(specialization)
{
assert(IsCompounddef(compounddef));
}
string ClassAnalyzer::GetClassName() const
{
string compoundname = ExtractCompoundname(compounddef_);
assert(StartsWith(compoundname, "Urho3D::"));
return CutStart(compoundname, "Urho3D::");
}
string ClassAnalyzer::GetDirName() const
{
string str = CutStart(GetHeaderFile(), "../");
size_t pos = str.find('/');
assert(pos != string::npos);
return str.substr(0, pos);
}
bool ClassAnalyzer::IsInternal() const
{
if (GetHeaderFile().empty()) // Defined in *.cpp
return true;
if (Contains(GetClassName(), "::")) // Defined inside another class
return true;
return false;
}
vector<xml_node> ClassAnalyzer::GetMemberdefs() const
{
vector<xml_node> result;
xml_node listofallmembers = compounddef_.child("listofallmembers");
assert(listofallmembers);
for (xml_node member : listofallmembers.children("member"))
{
xml_attribute ambiguityscope = member.attribute("ambiguityscope");
if (!ambiguityscope.empty())
continue;
string refid = member.attribute("refid").value();
assert(!refid.empty());
auto it = SourceData::members_.find(refid);
if (it == SourceData::members_.end())
continue;
xml_node memberdef = it->second;
result.push_back(memberdef);
}
return result;
}
vector<string> ClassAnalyzer::GetAllPublicMembersRefids() const
{
vector<string> result;
xml_node listofallmembers = compounddef_.child("listofallmembers");
assert(listofallmembers);
for (xml_node member : listofallmembers.children("member"))
{
string prot = member.attribute("prot").value();
assert(!prot.empty());
if (prot != "public")
continue;
xml_attribute ambiguityscope = member.attribute("ambiguityscope");
if (!ambiguityscope.empty())
continue;
string refid = member.attribute("refid").value();
assert(!refid.empty());
result.push_back(refid);
}
return result;
}
static vector<xml_node> GetHiddenMemberdefs(const ClassAnalyzer& classAnalyzer)
{
vector<string> thisRefids = classAnalyzer.GetAllPublicMembersRefids();
vector<ClassAnalyzer> baseClasses = classAnalyzer.GetBaseClasses();
vector<xml_node> result;
for (const ClassAnalyzer& baseClass : baseClasses)
{
vector<string> baseRefids = baseClass.GetAllPublicMembersRefids();
for (const string& baseRefid : baseRefids)
{
if (CONTAINS(thisRefids, baseRefid))
continue;
auto it = SourceData::members_.find(baseRefid);
if (it == SourceData::members_.end())
continue;
xml_node memberdef = it->second;
result.push_back(memberdef);
}
}
return result;
}
vector<string> ClassAnalyzer::GetHiddenMethods() const
{
vector<xml_node> hiddenMemberdefs = GetHiddenMemberdefs(*this);
vector<string> result;
for (xml_node memberdef : hiddenMemberdefs)
{
if (ExtractKind(memberdef) == "function" && !IsStatic(memberdef))
result.push_back(GetFunctionDeclaration(memberdef));
}
sort(result.begin(), result.end());
return result;
}
vector<string> ClassAnalyzer::GetHiddenStaticMethods() const
{
vector<xml_node> hiddenMemberdefs = GetHiddenMemberdefs(*this);
vector<string> result;
for (xml_node memberdef : hiddenMemberdefs)
{
if (ExtractKind(memberdef) == "function" && IsStatic(memberdef))
result.push_back(GetFunctionDeclaration(memberdef));
}
sort(result.begin(), result.end());
return result;
}
vector<string> ClassAnalyzer::GetHiddenFields() const
{
vector<xml_node> hiddenMemberdefs = GetHiddenMemberdefs(*this);
vector<string> result;
for (xml_node memberdef : hiddenMemberdefs)
{
if (ExtractKind(memberdef) == "variable" && !IsStatic(memberdef))
result.push_back(GetVariableDeclaration(memberdef));
}
sort(result.begin(), result.end());
return result;
}
vector<string> ClassAnalyzer::GetHiddenStaticFields() const
{
vector<xml_node> hiddenMemberdefs = GetHiddenMemberdefs(*this);
vector<string> result;
for (xml_node memberdef : hiddenMemberdefs)
{
if (ExtractKind(memberdef) == "variable" && IsStatic(memberdef))
result.push_back(GetVariableDeclaration(memberdef));
}
sort(result.begin(), result.end());
return result;
}
int ClassAnalyzer::GetInherianceDeep(int counter) const
{
vector<ClassAnalyzer> baseClasses = GetBaseClasses();
int result = counter;
for (const ClassAnalyzer& baseClass : baseClasses)
{
int ret = baseClass.GetInherianceDeep(counter + 1);
if (ret > result)
result = ret;
}
return result;
}
vector<MethodAnalyzer> ClassAnalyzer::GetAllMethods() const
{
vector<MethodAnalyzer> result;
vector<xml_node> memberdefs = GetMemberdefs();
for (xml_node memberdef : memberdefs)
{
if (ExtractKind(memberdef) == "function")
result.push_back(MethodAnalyzer(*this, memberdef));
}
return result;
}
vector<MethodAnalyzer> ClassAnalyzer::GetAllPublicMethods() const
{
vector<MethodAnalyzer> result;
vector<xml_node> memberdefs = GetMemberdefs();
for (xml_node memberdef : memberdefs)
{
if (ExtractKind(memberdef) == "function" && ExtractProt(memberdef) == "public")
result.push_back(MethodAnalyzer(*this, memberdef));
}
return result;
}
vector<MethodAnalyzer> ClassAnalyzer::GetThisPublicMethods() const
{
vector<MethodAnalyzer> result;
xml_node sectiondef = FindSectiondef(compounddef_, "public-func");
for (xml_node memberdef : sectiondef.children("memberdef"))
{
MethodAnalyzer methodAnalyzer(*this, memberdef);
result.push_back(methodAnalyzer);
}
return result;
}
vector<MethodAnalyzer> ClassAnalyzer::GetThisPublicStaticMethods() const
{
vector<MethodAnalyzer> result;
xml_node sectiondef = FindSectiondef(compounddef_, "public-static-func");
for (xml_node memberdef : sectiondef.children("memberdef"))
{
MethodAnalyzer methodAnalyzer(*this, memberdef);
result.push_back(methodAnalyzer);
}
return result;
}
vector<FieldAnalyzer> ClassAnalyzer::GetAllFields() const
{
vector<FieldAnalyzer> result;
vector<xml_node> memberdefs = GetMemberdefs();
for (xml_node memberdef : memberdefs)
{
if (ExtractKind(memberdef) == "variable")
result.push_back(FieldAnalyzer(*this, memberdef));
}
return result;
}
vector<FieldAnalyzer> ClassAnalyzer::GetThisPublicFields() const
{
vector<FieldAnalyzer> result;
xml_node sectiondef = FindSectiondef(compounddef_, "public-attrib");
for (xml_node memberdef : sectiondef.children("memberdef"))
{
FieldAnalyzer fieldAnalyzer(*this, memberdef);
result.push_back(fieldAnalyzer);
}
return result;
}
vector<FieldAnalyzer> ClassAnalyzer::GetThisPublicStaticFields() const
{
vector<FieldAnalyzer> result;
xml_node sectiondef = FindSectiondef(compounddef_, "public-static-attrib");
for (xml_node memberdef : sectiondef.children("memberdef"))
{
FieldAnalyzer fieldAnalyzer(*this, memberdef);
result.push_back(fieldAnalyzer);
}
return result;
}
bool ClassAnalyzer::ContainsMethod(const string& name) const
{
vector<MethodAnalyzer> methods = GetAllMethods();
for (const MethodAnalyzer& method : methods)
{
if (method.GetName() == name)
return true;
}
return false;
}
shared_ptr<MethodAnalyzer> ClassAnalyzer::GetMethod(const string& name) const
{
vector<MethodAnalyzer> methods = GetAllMethods();
for (const MethodAnalyzer& method : methods)
{
if (method.GetName() == name)
return make_shared<MethodAnalyzer>(method);
}
return nullptr;
}
int ClassAnalyzer::NumMethods(const string& name) const
{
int result = 0;
vector<MethodAnalyzer> methods = GetAllMethods();
for (const MethodAnalyzer& method : methods)
{
if (method.GetName() == name)
result++;
}
return result;
}
bool ClassAnalyzer::IsAbstract() const
{
vector<MethodAnalyzer> methods = GetAllMethods();
for (const MethodAnalyzer& method : methods)
{
if (method.IsPureVirtual())
{
if (!IsRefCounted())
return true;
// Some pure virtual functions is implemented by URHO3D_OBJECT
string name = method.GetName();
if (name == "GetType" || name == "GetTypeInfo" || name == "GetTypeName")
{
if (ContainsMethod("URHO3D_OBJECT"))
continue;
}
return true;
}
}
return false;
}
bool ClassAnalyzer::AllFloats() const
{
if (Contains(GetComment(), "ALL_FLOATS")) // TODO: remove
return true;
vector<FieldAnalyzer> fields = GetAllFields();
for (const FieldAnalyzer& field : fields)
{
if (field.IsStatic())
continue;
string type = field.GetType().ToString();
if (type != "float" && type != "double")
return false;
}
return true;
}
bool ClassAnalyzer::AllInts() const
{
if (Contains(GetComment(), "ALL_INTS")) // TODO: remove
return true;
vector<FieldAnalyzer> fields = GetAllFields();
for (const FieldAnalyzer& field : fields)
{
if (field.IsStatic())
continue;
string type = field.GetType().ToString();
if (type != "int" && type != "unsigned")
return false;
}
return true;
}
bool ClassAnalyzer::IsPod() const
{
bool result = Contains(GetComment(), "IS_POD");
if (AllFloats() || AllInts())
result = true;
return result;
}
shared_ptr<ClassAnalyzer> ClassAnalyzer::GetBaseClass() const
{
xml_node basecompoundref = compounddef_.child("basecompoundref");
if (!basecompoundref)
return shared_ptr<ClassAnalyzer>();
string refid = basecompoundref.attribute("refid").value();
assert(!refid.empty());
auto it = SourceData::classesByID_.find(refid);
if (it == SourceData::classesByID_.end())
return shared_ptr<ClassAnalyzer>();
xml_node compounddef = it->second;
return make_shared<ClassAnalyzer>(compounddef);
}
vector<ClassAnalyzer> ClassAnalyzer::GetBaseClasses() const
{
// Workaround for bug in doxygen 1.9+
// https://github.com/doxygen/doxygen/issues/8805
// This can be removed in future
vector<string> pushed_refids;
vector<ClassAnalyzer> result;
for (xml_node basecompoundref : compounddef_.children("basecompoundref"))
{
string refid = basecompoundref.attribute("refid").value();
if (refid.empty()) // Type from ThirdParty lib
continue;
auto it = SourceData::classesByID_.find(refid);
if (it == SourceData::classesByID_.end())
continue;
// This can be removed in future
if (CONTAINS(pushed_refids, refid))
continue;
pushed_refids.push_back(refid);
xml_node compounddef = it->second;
result.push_back(ClassAnalyzer(compounddef));
}
return result;
}
inline bool Contains(const vector<ClassAnalyzer>& classes, const ClassAnalyzer& cl)
{
for (const ClassAnalyzer& c : classes)
{
// Comparison without template specialization
if (c.GetCompounddef() == cl.GetCompounddef())
return true;
}
return false;
}
static void RecursivelyGetBaseClasses(const ClassAnalyzer& analyzer, vector<ClassAnalyzer>& result)
{
for (const ClassAnalyzer& baseClass : analyzer.GetBaseClasses())
{
if (!Contains(result, baseClass))
result.push_back(baseClass);
RecursivelyGetBaseClasses(baseClass, result);
}
}
vector<ClassAnalyzer> ClassAnalyzer::GetAllBaseClasses() const
{
vector<ClassAnalyzer> result;
RecursivelyGetBaseClasses(*this, result);
return result;
}
vector<ClassAnalyzer> ClassAnalyzer::GetDerivedClasses() const
{
vector<ClassAnalyzer> result;
for (xml_node basecompoundref : compounddef_.children("derivedcompoundref"))
{
string refid = basecompoundref.attribute("refid").value();
if (refid.empty()) // Type from ThirdParty lib
continue;
auto it = SourceData::classesByID_.find(refid);
if (it == SourceData::classesByID_.end())
continue;
xml_node compounddef = it->second;
result.push_back(ClassAnalyzer(compounddef));
}
return result;
}
static void RecursivelyGetDerivedClasses(const ClassAnalyzer& analyzer, vector<ClassAnalyzer>& result)
{
for (const ClassAnalyzer& derivedClass : analyzer.GetDerivedClasses())
{
if (!Contains(result, derivedClass))
result.push_back(derivedClass);
RecursivelyGetDerivedClasses(derivedClass, result);
}
}
vector<ClassAnalyzer> ClassAnalyzer::GetAllDerivedClasses() const
{
vector<ClassAnalyzer> result;
RecursivelyGetDerivedClasses(*this, result);
return result;
}
bool ClassAnalyzer::HasThisConstructor() const
{
vector<MethodAnalyzer> methods = GetAllMethods();
for (const MethodAnalyzer& method : methods)
{
if (method.IsThisConstructor())
return true;
}
return false;
}
bool ClassAnalyzer::IsRefCounted() const
{
if (GetClassName() == "RefCounted")
return true;
vector<ClassAnalyzer> baseClasses = GetAllBaseClasses();
for (const ClassAnalyzer& classAnalyzer : baseClasses)
{
if (classAnalyzer.GetClassName() == "RefCounted")
return true;
}
return false;
}
shared_ptr<MethodAnalyzer> ClassAnalyzer::GetDefinedThisDefaultConstructor() const
{
vector<MethodAnalyzer> methods = GetAllMethods();
for (const MethodAnalyzer& method : methods)
{
if (method.IsThisDefaultConstructor())
return make_shared<MethodAnalyzer>(method);
}
return nullptr;
}
vector<MethodAnalyzer> ClassAnalyzer::GetThisNonDefaultConstructors() const
{
vector<MethodAnalyzer> result;
vector<MethodAnalyzer> methods = GetAllMethods();
for (const MethodAnalyzer& method : methods)
{
if (method.IsThisNonDefaultConstructor())
result.push_back(method);
}
return result;
}
// ============================================================================
FunctionAnalyzer::FunctionAnalyzer(xml_node memberdef, const TemplateSpecialization& specialization)
: memberdef_(memberdef)
, specialization_(specialization)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "function");
}
// ============================================================================
MethodAnalyzer::MethodAnalyzer(const ClassAnalyzer& classAnalyzer, xml_node memberdef, const TemplateSpecialization& specialization)
: FunctionAnalyzer(memberdef, specialization)
, classAnalyzer_(classAnalyzer)
{
// Append template specialization from class
specialization_.insert(classAnalyzer.GetSpecialization().begin(), classAnalyzer.GetSpecialization().end());
}
string MethodAnalyzer::GetVirt() const
{
string result = memberdef_.attribute("virt").value();
assert(!result.empty());
return result;
}
string MethodAnalyzer::GetContainsClassName() const
{
string argsstring = ExtractArgsstring(memberdef_);
assert(!argsstring.empty());
string prototype = ExtractDefinition(memberdef_) + argsstring;
smatch match;
regex_match(prototype, match, regex(".*Urho3D::(.+?)::.*"));
assert(match.size());
string result = match[1].str();
return result;
}
/*
string FieldAnalyzer::GetDeclaration() const
{
string definition = ExtractDefinition(memberdef_);
assert(!definition.empty());
// Remove Urho3D::
smatch match;
regex_match(definition, match, regex("(.*)Urho3D::(.+)"));
assert(match.size() == 3);
string result = match[1].str() + match[2].str();
result = BeautifyDefinition(result);
return result;
}
*/
string GetVariableDeclaration(xml_node memberdef)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "variable");
string result = ExtractDefinition(memberdef);
result = RemoveFirst(result, "URHO3D_API ");
result = RemoveFirst(result, " URHO3D_API");
assert(Contains(result, " Urho3D::"));
result = ReplaceFirst(result, " Urho3D::", " ");
if (IsStatic(memberdef))
result = "static " + result;
result = BeautifyDefinition(result);
return result;
}
string GetFunctionDeclaration(xml_node memberdef)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "function");
string argsstring = ExtractArgsstring(memberdef);
assert(!argsstring.empty());
string prototype = ExtractDefinition(memberdef) + argsstring;
smatch match;
regex_match(prototype, match, regex("([^(]*)Urho3D::(.+?)"));
assert(match.size());
string result = match[1].str() + match[2].str();
if (IsExplicit(memberdef))
result = "explicit " + result;
if (IsTemplate(memberdef))
{
string t = "";
xml_node templateparamlist = memberdef.child("templateparamlist");
for (xml_node param : templateparamlist.children("param"))
{
if (t.length() > 0)
t += ", ";
xml_node type = param.child("type");
assert(type);
t += type.child_value();
xml_node declname = param.child("declname");
if (!declname.empty())
t += " " + string(declname.child_value());
}
result = "template<" + t + "> " + result;
}
result = RemoveFirst(result, "URHO3D_API ");
result = RemoveFirst(result, " URHO3D_API");
result = BeautifyDefinition(result);
return result;
}
string GetFunctionLocation(xml_node memberdef)
{
return GetFunctionDeclaration(memberdef) + " | File: " + ExtractHeaderFile(memberdef);
}
bool MethodAnalyzer::IsConst() const
{
string constAttr = memberdef_.attribute("const").value();
assert(!constAttr.empty());
return constAttr == "yes";
}
bool MethodAnalyzer::CanBeGetProperty() const
{
string returnType = GetReturnType().ToString();
if (returnType == "void" || returnType.empty())
return false;
if (GetParams().size() != 0 && GetParams().size() != 1)
return false;
return true;
}
bool MethodAnalyzer::CanBeSetProperty() const
{
string returnType = GetReturnType().ToString();
if (returnType != "void")
return false;
if (GetParams().size() != 1 && GetParams().size() != 2)
return false;
return true;
}
bool MethodAnalyzer::IsParentDestructor() const
{
string functionName = GetName();
if (!StartsWith(functionName, "~"))
return false;
return !IsThisDestructor();
}
bool MethodAnalyzer::IsConstructor() const
{
string name = GetName();
return ExtractDefinition(memberdef_) == "Urho3D::" + name + "::" + name;
}
bool MethodAnalyzer::IsDestructor() const
{
string name = GetName();
return StartsWith(name, "~");
}
bool MethodAnalyzer::IsParentConstructor() const
{
if (IsThisConstructor())
return false;
string name = GetName();
return ExtractDefinition(memberdef_) == "Urho3D::" + name + "::" + name;
}
shared_ptr<MethodAnalyzer> MethodAnalyzer::Reimplements() const
{
xml_node reimplements = memberdef_.child("reimplements");
if (!reimplements)
return shared_ptr<MethodAnalyzer>();
string refid = reimplements.attribute("refid").value();
assert(!refid.empty());
auto it = SourceData::members_.find(refid);
if (it == SourceData::members_.end())
return shared_ptr<MethodAnalyzer>();
xml_node memberdef = it->second;
return make_shared<MethodAnalyzer>(classAnalyzer_, memberdef);
}
// ============================================================================
FieldAnalyzer::FieldAnalyzer(ClassAnalyzer classAnalyzer, xml_node memberdef)
: classAnalyzer_(classAnalyzer)
, memberdef_(memberdef)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "variable");
}
string FieldAnalyzer::GetLocation() const
{
string result = GetDeclaration();
result += " | File: " + GetHeaderFile();
if (!classAnalyzer_.usingLocation_.empty())
result = classAnalyzer_.usingLocation_ + " | " + result;
return result;
}
// ============================================================================
NamespaceAnalyzer::NamespaceAnalyzer(xml_node compounddef)
: compounddef_(compounddef)
{
assert(IsCompounddef(compounddef));
assert(ExtractKind(compounddef) == "namespace");
}
vector<EnumAnalyzer> NamespaceAnalyzer::GetEnums()
{
xml_node sectiondef = FindSectiondef(compounddef_, "enum");
assert(sectiondef);
vector<EnumAnalyzer> result;
for (xml_node memberdef : sectiondef.children("memberdef"))
{
EnumAnalyzer analyzer(memberdef);
result.push_back(analyzer);
}
return result;
}
vector<GlobalVariableAnalyzer> NamespaceAnalyzer::GetVariables()
{
xml_node sectiondef = FindSectiondef(compounddef_, "var");
assert(sectiondef);
vector<GlobalVariableAnalyzer> result;
for (xml_node memberdef : sectiondef.children("memberdef"))
result.push_back(GlobalVariableAnalyzer(memberdef));
return result;
}
vector<GlobalFunctionAnalyzer> NamespaceAnalyzer::GetFunctions()
{
xml_node sectiondef = FindSectiondef(compounddef_, "func");
assert(sectiondef);
vector<GlobalFunctionAnalyzer> result;
for (xml_node memberdef : sectiondef.children("memberdef"))
result.push_back(GlobalFunctionAnalyzer(memberdef));
return result;
}
// ============================================================================
UsingAnalyzer::UsingAnalyzer(xml_node memberdef)
: memberdef_(memberdef)
{
assert(IsMemberdef(memberdef));
assert(ExtractKind(memberdef) == "typedef");
}
// ============================================================================
GlobalFunctionAnalyzer::GlobalFunctionAnalyzer(xml_node memberdef, const TemplateSpecialization& specialization)
: FunctionAnalyzer(memberdef, specialization)
{
}
// ============================================================================
ClassStaticFunctionAnalyzer::ClassStaticFunctionAnalyzer(const ClassAnalyzer& classAnalyzer, xml_node memberdef, const TemplateSpecialization& specialization)
: FunctionAnalyzer(memberdef, specialization)
, classAnalyzer_(classAnalyzer)
{
assert(IsStatic(memberdef));
}