using System; using System.Collections; using System.Collections.Generic; namespace Godot { using Array = Godot.Collections.Array; using Dictionary = Godot.Collections.Dictionary; static class MarshalUtils { /// /// Returns if the generic type definition of /// is ; otherwise returns . /// /// /// is not a generic type. That is, IsGenericType returns false. /// static bool TypeIsGenericArray(Type type) { return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); } /// /// Returns if the generic type definition of /// is ; otherwise returns . /// /// /// is not a generic type. That is, IsGenericType returns false. /// static bool TypeIsGenericDictionary(Type type) { return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); } static void ArrayGetElementType(Type arrayType, out Type elementType) { elementType = arrayType.GetGenericArguments()[0]; } static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) { var genericArgs = dictionaryType.GetGenericArguments(); keyType = genericArgs[0]; valueType = genericArgs[1]; } static bool GenericIEnumerableIsAssignableFromType(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) return true; foreach (var interfaceType in type.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) return true; } Type baseType = type.BaseType; if (baseType == null) return false; return GenericIEnumerableIsAssignableFromType(baseType); } static bool GenericIDictionaryIsAssignableFromType(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) return true; foreach (var interfaceType in type.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) return true; } Type baseType = type.BaseType; if (baseType == null) return false; return GenericIDictionaryIsAssignableFromType(baseType); } static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { elementType = type.GetGenericArguments()[0]; return true; } foreach (var interfaceType in type.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { elementType = interfaceType.GetGenericArguments()[0]; return true; } } Type baseType = type.BaseType; if (baseType == null) { elementType = null; return false; } return GenericIEnumerableIsAssignableFromType(baseType, out elementType); } static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) { var genericArgs = type.GetGenericArguments(); keyType = genericArgs[0]; valueType = genericArgs[1]; return true; } foreach (var interfaceType in type.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) { var genericArgs = interfaceType.GetGenericArguments(); keyType = genericArgs[0]; valueType = genericArgs[1]; return true; } } Type baseType = type.BaseType; if (baseType == null) { keyType = null; valueType = null; return false; } return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType); } static Type MakeGenericArrayType(Type elemType) { return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); } static Type MakeGenericDictionaryType(Type keyType, Type valueType) { return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType); } // TODO Add support for IEnumerable and IDictionary // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr) { if (enumerable is ICollection collection) { int count = collection.Count; object[] tempArray = new object[count]; collection.CopyTo(tempArray, 0); for (int i = 0; i < count; i++) { Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]); } } else { foreach (object element in enumerable) { Array.godot_icall_Array_Add(godotArrayPtr, element); } } } internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr) { foreach (DictionaryEntry entry in dictionary) { Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); } } internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr) { #if DEBUG if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType())) throw new InvalidOperationException("The type does not implement IDictionary<,>"); #endif // TODO: Can we optimize this? var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator(); var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator(); while (keys.MoveNext() && values.MoveNext()) { object key = keys.Current; object value = values.Current; Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value); } } } }