using System;
using System.Collections.Generic;
using System.Linq;
using Clang.Ast;
using ICSharpCode.NRefactory.CSharp;
using Sharpie.Bind;
namespace SharpieBinder
{
///
/// Finds a few types that we use later to make decisions, and scans for methods for get/set patterns
///
class ScanBaseTypes : AstVisitor
{
//
// These are the types that we have to lookup earlier, before we run the scan in CxxBinder
//
static public CXXRecordDecl UrhoRefCounted, EventHandlerType, UrhoObjectType;
// Provides a way of mapping names to declarations, we load this as we process
// and use this information later in CxxBinder
public static Dictionary nameToDecl = new Dictionary();
public override void VisitCXXRecordDecl(CXXRecordDecl decl, VisitKind visitKind)
{
if (visitKind != VisitKind.Enter || !decl.IsCompleteDefinition || decl.Name == null)
return;
nameToDecl[decl.QualifiedName] = decl;
switch (decl.QualifiedName) {
case "Urho3D::RefCounted":
UrhoRefCounted = decl;
break;
case "Urho3D::Object":
UrhoObjectType = decl;
break;
case "Urho3D::EventHandler":
EventHandlerType = decl;
break;
}
}
public class GetterSetter
{
public CXXMethodDecl Getter, Setter;
public TypeDeclaration HostType;
public AstType MethodReturn;
public bool SetMethodPublic;
public string Name;
}
// typeName to propertyName to returnType to GetterSetter pairs
public static Dictionary>> allProperties =
new Dictionary>>();
public override void VisitCXXMethodDecl(CXXMethodDecl decl, VisitKind visitKind)
{
if (visitKind != VisitKind.Enter)
return;
var isConstructor = decl is CXXConstructorDecl;
if (decl is CXXDestructorDecl || isConstructor)
return;
if (decl.IsCopyAssignmentOperator || decl.IsMoveAssignmentOperator)
return;
if (decl.Parent == null)
return;
if (!decl.Parent.QualifiedName.StartsWith("Urho3D::"))
return;
// Only get methods prefixed with Get with no parameters
// and Set methods that return void and take a single parameter
var name = decl.Name;
// Handle Get methods that are not really getters
// This is a get method that does not get anything
bool legacySetMethod = false;
QualType type;
if (name.StartsWith("Get") || name.StartsWith("Is")) {
if (decl.Parameters.Count() != 0)
return;
if (decl.ReturnQualType.ToString() == "void")
return;
if (name == "IsElementEventSender" ||
name == "IsOpen" ||
name == "IsPressed")
return;
type = decl.ReturnQualType;
} else if (name.StartsWith("Set")) {
if (decl.Parameters.Count() != 1)
return;
if ((name == "SetTypeName" || name == "SetType") && decl.Parent.Name == "UnknownComponent")
return;
if (decl.Access != AccessSpecifier.Public)
return;
if (!(decl.ReturnQualType.Bind() is Sharpie.Bind.Types.VoidType))
{
legacySetMethod = true;
}
type = decl.Parameters.FirstOrDefault().QualType;
} else
return;
Dictionary> typeProperties;
if (!allProperties.TryGetValue(decl.Parent.Name, out typeProperties)) {
typeProperties = new Dictionary>();
allProperties[decl.Parent.Name] = typeProperties;
}
var propName = name.Substring(name.StartsWith("Is") ? 2 : 3);
Dictionary property;
if (!typeProperties.TryGetValue(propName, out property)) {
property = new Dictionary();
typeProperties[propName] = property;
}
GetterSetter gs;
if (!property.TryGetValue(type, out gs)) {
gs = new GetterSetter() { Name = propName };
}
if (legacySetMethod)
gs.SetMethodPublic = legacySetMethod;
if (name.StartsWith("Get") || name.StartsWith("Is")) {
if (propName != decl.Parent.Name || propName == "Text")
// do not generate Getter if propertyName equals to typename (Text type already has a workaround for this case)
gs.Getter = decl;
} else {
gs.Setter = decl;
}
property[type] = gs;
}
// Contains a list of all methods that will be part of a property
static Dictionary allPropertyMethods = new Dictionary();
public static GetterSetter GetPropertyInfo(CXXMethodDecl decl)
{
GetterSetter gs;
if (allPropertyMethods.TryGetValue(decl, out gs))
return gs;
return null;
}
//
// After we collected the information, remove pairs that only had a setter, but no getter
//
public void PrepareProperties()
{
var typeRemovals = new List();
foreach (var typeKV in allProperties) {
var propertyRemovals = new List();
foreach (var propNameKV in typeKV.Value) {
var qualTypeRemoval = new List();
foreach (var propTypeKV in propNameKV.Value) {
if (propTypeKV.Value.Getter == null)
qualTypeRemoval.Add(propTypeKV.Key);
}
foreach (var qualType in qualTypeRemoval)
propNameKV.Value.Remove(qualType);
if (propNameKV.Value.Count == 0)
propertyRemovals.Add(propNameKV.Key);
}
foreach (var property in propertyRemovals)
typeKV.Value.Remove(property);
if (typeKV.Value.Count == 0)
typeRemovals.Add(typeKV.Key);
}
foreach (var type in typeRemovals)
allProperties.Remove(type);
foreach (var typeKV in allProperties) {
foreach (var propNameKV in typeKV.Value) {
foreach (var gs in propNameKV.Value.Values) {
allPropertyMethods[gs.Getter] = gs;
if (gs.Setter != null)
allPropertyMethods[gs.Setter] = gs;
}
}
}
}
}
}